Disable SSH TCPKeepAlive (use ClientAlive instead)
Turn off the TCP-level keepalive (TCPKeepAlive no) so that session liveness is decided only by the ClientAliveInterval / ClientAliveCountMax probes. TCP keepalives travel outside the encrypted channel and are spoofable: an attacker on the path can forge them to keep a dead or hijacked session registered as alive.
Checked against the resolved running state (e.g. sshd -T, sysctl, systemctl show), catches drop-ins and Includes a file read would miss. Caveat: runtime ≠ persistence; a value correct now may not survive a reboot.
What Pavois checks
Pavois runs sshd -T and requires the resolved line tcpkeepalive no. sshd -T prints the daemon's effective configuration, after all Include directives and every drop-in in /etc/ssh/sshd_config.d/, so a later drop-in that restores the default yes is detected. A file-based check on /etc/ssh/sshd_config would miss it.
describe command('sshd -T') do
its('stdout') { should match(/^tcpkeepalive\s+no$/i) }
endHow to verify it is applied
Run sshd -T | grep -iE '^(tcpkeepalive|clientalive)' as root. Expected output, tcpkeepalive off and the encrypted probes taking over:
tcpkeepalive no
clientaliveinterval 300
clientalivecountmax 3
The clientalive* values come from their own Pavois rules; what this rule requires is the first line.
Inspect & investigate
The directive itself produces no log entry. What becomes visible is the ClientAlive-driven teardown: when a client stops answering the encrypted probes, sshd logs in /var/log/auth.log (or journalctl -u ssh):
sshd[1234]: Timeout, client not responding from user alice 203.0.113.10 port 51234
With TCPKeepAlive yes and no ClientAlive probes, that same dead session would simply linger, silently.
Remediation
The Pavois harden plan uses the sshd_setting resource to set the directive tcpkeepalive to no in the Pavois drop-in under /etc/ssh/sshd_config.d/. The file is validated with sshd -t -f <file> before being kept, and a change notifies a reload of ssh.service.
Pavois applies this with its own harden engine, the plan below, not a shell script:
| directive | tcpkeepalive |
|---|---|
| notify | action: reload, service: ssh.service |
| resource | sshd_setting |
| value | no |
| verify | sshd -t -f %{path} |
pavois harden plan localwhere the target is local, a user@host SSH alias, or a container , Docs
Impact & precautions
Disabling TCP keepalives removes the packets that keep NAT and stateful-firewall entries warm. If ClientAliveInterval is unset (or larger than the firewall's idle timeout), long idle sessions can be silently dropped by a middlebox: set ClientAliveInterval below that timeout, or have clients use ServerAliveInterval. Conversely, do not disable TCPKeepAlive and leave ClientAlive off: the server would then never detect a dead peer and would accumulate orphaned sessions. The change is applied by a reload, so current sessions are unaffected.