SSH Hardening & Key Management¶
Reference: OpenSSH Docs · Debian Wiki — SSH · sshd_config(5)
Key-Based Authentication¶
Password authentication is convenient but weak. Key-based auth should be used for all server access.
Generate a Key Pair¶
# Generate ED25519 key (preferred — smaller, faster, more secure than RSA)
ssh-keygen -t ed25519 -C "your-label"
# RSA if ED25519 isn't supported by a target system
ssh-keygen -t rsa -b 4096 -C "your-label"
Give the key a passphrase and let the SSH agent remember it. DSA keys don't work at all any more: OpenSSH in Debian 13 removed DSA support completely.
Keys are saved to ~/.ssh/ by default:
~/.ssh/id_ed25519— private key (never share or copy this)~/.ssh/id_ed25519.pub— public key (this gets added to servers)
Copy Public Key to a Server¶
# Preferred method
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
# Manual method if ssh-copy-id isn't available
cat ~/.ssh/id_ed25519.pub | ssh user@server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
Verify correct permissions on the server after:
SSH Config File (~/.ssh/config)¶
Saves typing for hosts you connect to regularly:
# ~/.ssh/config
Host appserver
HostName 192.168.1.10
User youruser
IdentityFile ~/.ssh/id_ed25519
Port 22
Host nas
HostName 192.168.1.20
User youruser
IdentityFile ~/.ssh/id_ed25519
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
Now connect with just ssh appserver instead of the full command.
Hardening sshd_config¶
The server-side config is /etc/ssh/sshd_config. On Debian, its first line is
Include /etc/ssh/sshd_config.d/*.conf, so put your settings in a drop-in instead of editing the main file. Package
upgrades then never prompt about a modified config. The first value sshd reads for a setting wins, and drop-ins
are read before the rest of the main file, so a drop-in overrides it.
Recommended drop-in¶
# Keys only
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
# No direct root logins; use your own account + sudo
PermitRootLogin no
# Only these accounts may log in at all (space-separated; user@host patterns work too)
AllowUsers <YOUR_USER>
# ...or allow a group instead: AllowGroups sshusers
# Unused auth methods off
KerberosAuthentication no
GSSAPIAuthentication no
# Less room for brute force and stale sessions
LoginGraceTime 30
MaxAuthTries 3
MaxStartups 10:30:60
ClientAliveInterval 300
ClientAliveCountMax 3
# Turn these off unless you use them
X11Forwarding no
AllowAgentForwarding no # leave on for a jump host you forward through
Why these:
- Keys only. Password guessing stops being possible. Debian's default
PermitRootLogin prohibit-passwordalready refuses root passwords;noalso refuses root keys, so every login is attributable to a person. AllowUsers. Service accounts, backup users and anything a package creates can't log in interactively unless you list them. If a backup or deploy tool needs SSH, give it its own user with a restricted key (command="...",restrictinauthorized_keys) and add it here.- Keep a sudo password. Keys-only SSH plus a sudo password means a stolen key alone isn't root. See Users & Permissions → sudo.
Changing Port only reduces log noise from scanners. It isn't a security control, and on a LAN-only host it
isn't worth the extra -p everywhere.
Disable passwords only after confirming key auth works
Test key-based login in a separate terminal session before setting PasswordAuthentication no. Locking yourself out requires console access to fix.
Apply Changes¶
# Test config for syntax errors before reloading
sudo sshd -t
# Show the settings sshd will actually use (catches a drop-in being overridden)
sudo sshd -T | grep -Ei 'passwordauth|permitroot|allowusers|kbdinteractive'
# Reload (open sessions stay connected)
sudo systemctl reload ssh
On Debian the unit is ssh.service (sshd.service is an alias). Logins and failures go to the journal:
journalctl -u ssh -f. There's no /var/log/auth.log unless you install rsyslog.
Ubuntu: socket activation
Ubuntu 22.10+ starts sshd from ssh.socket, so Port/ListenAddress changes there need
systemctl daemon-reload && systemctl restart ssh.socket. Debian runs ssh.service directly; a reload is enough.
SSH Agent¶
The SSH agent holds your decrypted private key in memory so you don't re-enter your passphrase every connection.
# Start agent (usually auto-started by desktop environments)
eval "$(ssh-agent -s)"
# Add key to agent
ssh-add ~/.ssh/id_ed25519
# List loaded keys
ssh-add -l
# Remove all keys from agent
ssh-add -D
Agent Forwarding¶
Forward your local SSH agent to a remote host — useful for hopping between servers without copying private keys.
# Connect with forwarding
ssh -A user@jumphost
# Or set in ~/.ssh/config
Host jumphost
ForwardAgent yes
Warning
Only forward your agent to hosts you fully trust. Anyone with root on the remote host can use your forwarded agent.
Useful SSH Commands¶
# Copy files securely
scp file.txt user@server:/destination/
scp -r directory/ user@server:/destination/
# Mount remote filesystem locally (requires sshfs)
sshfs user@server:/remote/path /local/mountpoint
# Run a remote command without interactive shell
ssh user@server "sudo systemctl restart myservice"
# Create a SOCKS proxy through a remote host
ssh -D 1080 user@server
# Port forward — access remote port 8080 locally on 8080
ssh -L 8080:localhost:8080 user@server
# Check which keys are authorized on a server
cat ~/.ssh/authorized_keys
Permissions Reference¶
SSH is strict about permissions — wrong permissions cause silent auth failures.
| Path | Required Permission |
|---|---|
~/.ssh/ |
700 |
~/.ssh/authorized_keys |
600 |
~/.ssh/id_ed25519 (private) |
600 |
~/.ssh/id_ed25519.pub (public) |
644 |
~/.ssh/config |
600 |
# Fix all at once
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_ed25519 ~/.ssh/config
chmod 644 ~/.ssh/id_ed25519.pub