Linux9 min read

SSH hardening: securing remote access

linuxsshsecuritynetworking

SSH is how you get into your server. It’s also how attackers try to get in. Default SSH configuration works but leaves room for improvement. A few changes make the difference between a server that gets brute-forced daily and one that shrugs off automated attacks.

Key-based authentication

Passwords get brute-forced. Keys don’t. Generate a key pair:

ssh-keygen -t ed25519 -C "upi@workstation"

Copy the public key to your server:

ssh-copy-id -i ~/.ssh/id_ed25519.pub upi@server

Test that key-based login works before disabling passwords.

Disable password authentication

Edit /etc/ssh/sshd_config:

PasswordAuthentication no
PubkeyAuthentication yes

Restart SSH:

sudo systemctl restart sshd

Keep your current session open while you test the new connection. Locking yourself out is easy when you change SSH settings.

Disable root login

Root should never log in directly via SSH. Edit /etc/ssh/sshd_config:

PermitRootLogin no

Use a regular user and sudo instead. This gives you an audit trail and prevents the most commonly targeted account from being directly accessible.

Change the default port

This is security through obscurity, but it cuts down the noise in your logs dramatically. Pick a high port:

Port 2222

Update your SSH client config (~/.ssh/config) to match:

Host myserver
    HostName server.example.com
    Port 2222
    User upi

Then connect with ssh myserver instead of remembering the port.

Limit users and groups

Only allow specific users to SSH in:

AllowUsers upi admin
# or
AllowGroups sshusers

This prevents service accounts and other system users from being SSH targets.

Set connection timeouts

Idle sessions are a risk. Set timeouts:

ClientAliveInterval 300
ClientAliveCountMax 2

This disconnects idle sessions after 10 minutes (300 seconds × 2).

Rate limiting with fail2ban

Install fail2ban:

sudo apt install fail2ban

Create a local config:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Edit /etc/fail2ban/jail.local:

[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600

This bans an IP for 1 hour after 3 failed attempts within 10 minutes.

Start it:

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Check banned IPs:

sudo fail2ban-client status sshd

SSH agent forwarding

If you need to hop through a bastion host, agent forwarding lets you use your local keys on the remote server:

ssh -A upi@bastion

Use this carefully. Anyone with root on the bastion can use your forwarded agent. For production, consider ProxyJump instead:

ssh -J bastion destination

ProxyJump creates a direct tunnel without exposing your agent.

Two-factor authentication

For extra security, set up TOTP with Google Authenticator:

sudo apt install libpam-google-authenticator
google-authenticator

Follow the prompts to set up the QR code, then configure PAM and sshd to require it. This combines something you have (the TOTP token) with something you know (your key passphrase) or have (your SSH key).

Auditing SSH access

Check who’s currently logged in:

who
w

See recent login attempts:

last
lastb  # failed attempts

Monitor auth log in real time:

sudo tail -f /var/log/auth.log

Common mistakes

Disabling password auth before testing key auth. Always verify key-based login works first. Keep your current session open.

Locking yourself out with PermitRootLogin. If root is your only account with sudo, and you disable root login, you’re stuck. Make sure you have a regular user in the sudo group first.

Not restarting sshd. Config changes don’t take effect until you restart the service.

Remarks

SSH hardening is one of the first things you should do on any new server. Key-based auth, no root login, fail2ban, and a non-standard port handle the majority of threats. The rest is defense in depth. Every server I’ve set up gets these changes before anything else goes on it.

Related Posts