Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

#5

P0: Harden SSH availability and host-key handling

closed · opened by keenan 13d ago

keenan opened this 13d ago

Why

server/ssh/server.go accepts connections with no handshake deadline and no per-connection channel cap. Every session channel starts another goroutine; every git-receive-pack starts another process. There is no connection cap or per-IP backoff. Existing host-key permissions are not checked.

Scope

  • Set a handshake deadline on the raw connection, then clear it after NewServerConn.
  • Add a per-listener connection cap and a per-connection session/channel cap.
  • Add a global Git process semaphore.
  • Reject oversized exec commands and bound the vole-runner JSON decoder.
  • Fail startup if an existing host key is group/world-readable.
  • Consider per-IP connection/auth backoff for unknown keys.

Acceptance

  • Handshake-slowloris and channel-flood tests leave CPU and file descriptors bounded.
  • A host key with loose permissions is refused or corrected.

Comments (1)

keenan 12d ago

Plan, in the order I will land it:

  1. Handshake deadline. Set a deadline on the accepted connection before NewServerConn, clear it after, and keep it cleared for the session: a clone and a push are legitimately slow, a handshake is not. Configurable as ssh_handshake_timeout_seconds, default 20.
  2. Connection cap. ssh_max_connections (default 64) is a token per open connection. Past it the connection is closed before a handshake, a goroutine or a git process can be spent on it, and the accept loop keeps running so the listener can never be wedged. The “listener is full” warning is throttled so a flood cannot fill the disk with the news.
  3. Channel cap. Eight session channels per connection, taken from a per- connection token channel so a flood is rejected at the channel layer rather than spawning a goroutine per request.
  4. Git process cap. ssh_max_git_processes (default 16) bounds git processes across every connection, for git-upload-pack, git-receive-pack and vole-change-merge. A command waits up to 30 s for a slot, then is told to retry instead of parking the session.
  5. Payload bounds. An exec line over 4 KiB is refused before it is parsed; a vole-runner request line over 1 MiB ends the session with an error. The runner bound is implemented as a per-line reader, so the decoder’s internal buffering cannot hide an oversized request and a long-lived runner is not limited in total.
  6. Host key mode. Loading an existing host key tightens it to 0600 and says so, and a mode that cannot be tightened fails startup. vole serve already tightens it first; this makes the SSH package safe on its own.
  7. Docs: configuration table, the status page’s read-only view, and the operations limits section.

Per-address backoff for repeated unknown keys is deliberately not here: it is a quota, not a transport bound, and belongs with the abuse controls. Nothing in this issue should require an operator to tune anything to be safe by default.