sessionrecording: close idle connections after upload

If we don't close the connection between SSH server and recorder
explicitly once it's idle after the upload stream is closed, the
connection stays open and holds on to a port on the server. This
leads to port exhaustion on the server in the medium to long run.

To avoid this, close the idle connections explicitly. As an extra
step of precaution, set an idleConnTimeout of 30 seconds on both
the HTTP1 and HTTP2 recorder clients.

Updates tailscale/corp#43742

Signed-off-by: Gesa Stupperich <gesa@tailscale.com>
This commit is contained in:
Gesa Stupperich
2026-06-19 10:33:50 +01:00
committed by Gesa Stupperich
parent 0861dafddf
commit 53ef7f92cb

View File

@@ -39,6 +39,9 @@
// in tests.
var uploadAckWindow = 30 * time.Second
// idleConnTimeout is the idle timeout for connections to the recorder.
var idleConnTimeout = 30 * time.Second
// ConnectToRecorder connects to the recorder at any of the provided addresses.
// It returns the first successful response, or a multierr if all attempts fail.
//
@@ -234,6 +237,7 @@ func connectV1(ctx context.Context, hc *http.Client, ap netip.AddrPort) (io.Writ
// errChan is used to indicate the result of the request.
errChan := make(chan error, 1)
go func() {
defer hc.CloseIdleConnections()
defer close(errChan)
resp, err := hc.Do(req)
if err != nil {
@@ -292,6 +296,7 @@ func connectV2(ctx context.Context, hc *http.Client, ap netip.AddrPort) (io.Writ
acks := make(chan int64)
// Read acks from the response and send them to the acks channel.
go func() {
defer hc.CloseIdleConnections()
defer close(errChan)
defer close(acks)
defer resp.Body.Close()
@@ -394,6 +399,7 @@ func clientHTTP1(dialCtx context.Context, dial netx.DialFunc) *http.Client {
}()
return dial(perAttemptCtx, network, addr)
}
tr.IdleConnTimeout = idleConnTimeout
return &http.Client{Transport: tr}
}
@@ -418,6 +424,7 @@ func clientHTTP2(dialCtx context.Context, dial netx.DialFunc) *http.Client {
}()
return dial(perAttemptCtx, network, addr)
},
IdleConnTimeout: idleConnTimeout,
},
}
}