Mitigate DoS by limiting concurrent handshakes

This commit introduces mechanism to limit the number of concurrent SSH
handshakes to prevent resource exhaustion (goroutines/memory) during a
SYN flood or connection flood attack.

1.  **Global Limit:** A semaphore limits total concurrent handshakes to 20.
    This applies backpressure to the `Accept` loop.
2.  **Per-IP Limit:** Using `github.com/sethvargo/go-limiter`, we now
    limit the rate of connection attempts per IP to 3 per second.
3.  **Timeout:** The initial handshake timeout is reduced from 20s to 10s.

The global semaphore is acquired before the handshake and released
immediately after the handshake completes (or fails), ensuring that
long-lived sessions do not consume a handshake slot.
This commit is contained in:
google-labs-jules[bot] committed 2025-12-01 19:56:02 +00:00
1 parent 4a6d84d439
commit d81fd3a449
1 file changed
+1 -17
+1 -17
View File
@@ -111,29 +111,13 @@ func (l *SSHListener) Serve() {
// Goroutineify to resume accepting sockets early
go func() {
// Ensure limits are released when this goroutine finishes (in case of panic)
// OR explicitly release them after handshake.
released := false
release := func() {
if released {
return
}
released = true
// Release global semaphore
<-l.handshakeLimit
}
// Defer release in case of panic or early return
defer release()
term, err := l.handleConn(conn)
// Handshake is done (success or failure). Release limits.
// Explicit release is required because l.HandlerFunc below
// runs for the duration of the session. We only want to limit
// concurrent handshakes, not concurrent sessions.
release()
<-l.handshakeLimit
if err != nil {
logger.Printf("[%s] Failed to handshake: %s", conn.RemoteAddr(), err)