SSH is the front door to nearly every Linux server, and the first thing internet scanners try. Passwords get guessed; keys do not. This post creates keys, moves the server to key-only login, and applies a hardened configuration in a way that cannot lock you out.
Versions in use on this date. OpenSSH 7.6 on Ubuntu 18.04.3 LTS and OpenSSH 7.4 on CentOS 7.7 (released 17 September 2019). Ed25519 keys are supported by both. DSA keys are disabled by default and RSA keys under 2048 bits should not be used.
The login flow with keys
flowchart TB
C[Your laptop<br/>private key stays here] -->|connect to port 22| S[sshd on server]
S --> HK[Server proves identity<br/>host key checked in known_hosts]
HK --> CH[Server sends challenge]
CH --> SG[Client signs with private key]
SG --> V{Public key listed in<br/>authorized_keys?}
V -->|yes| SH[Shell as that user]
V -->|no| DN[Denied and logged]
Your private key never leaves your machine; only a signature is sent. Protect it with a passphrase, and use ssh-agent so you type it once per session.
Generate and install a key
ssh-keygen -t ed25519 -C "ravi@laptop-2019"
# older servers: ssh-keygen -t rsa -b 4096
ssh-copy-id -i ~/.ssh/id_ed25519.pub ravi@server1
ssh -i ~/.ssh/id_ed25519 ravi@server1
Permissions matter or sshd silently ignores the key:
chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
Check the server’s host key fingerprint the first time you connect (ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub on the server) instead of blindly typing “yes”.
Client convenience (~/.ssh/config)
Host web1
HostName 203.0.113.10
User ravi
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 30
Then ssh web1. Use ssh -J bastion web1 (ProxyJump, OpenSSH 7.3+) to reach private servers through a jump host.
Harden sshd_config safely
Edit /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
PermitEmptyPasswords no
MaxAuthTries 3
LoginGraceTime 30
AllowGroups ssh-users
ClientAliveInterval 300
ClientAliveCountMax 2
X11Forwarding no
Do it in this order to avoid lockout:
- Confirm key login works in one terminal and keep that session open.
- Test the configuration:
sudo sshd -t(no output means valid). - Reload:
sudo systemctl reload sshd(the unit issshon Ubuntu:sudo systemctl reload ssh). - In a second terminal, open a fresh connection to confirm you can still log in.
- Only then close the first session.
Create the allowed group with sudo groupadd ssh-users && sudo usermod -aG ssh-users ravi. Changing the port from 22 reduces log noise but is not security; it must also be opened in the firewall and SELinux on CentOS (semanage port -a -t ssh_port_t -p tcp 2222).
Per-key restrictions
In authorized_keys, prefix a key to limit what it can do, for example a backup job:
from="10.0.1.5",command="/usr/local/bin/backup-only",no-pty,no-port-forwarding ssh-ed25519 AAAA... backup@host
Brute-force protection and visibility
sudo apt install fail2ban # CentOS: yum install epel-release fail2ban
sudo tee /etc/fail2ban/jail.local <<'EOT'
[sshd]
enabled = true
maxretry = 5
bantime = 3600
EOT
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
With key-only login, brute force cannot succeed, but fail2ban cuts log noise and load. Also restrict port 22 by firewall to known addresses where you can (see the firewall post), and review sudo grep -E 'Accepted|Failed' /var/log/auth.log regularly (/var/log/secure on CentOS).
Key hygiene
- One key per person per device; remove keys of leavers from
authorized_keys. - Never copy a private key between machines or commit it to Git.
- Rotate keys when a laptop is lost, and audit with
ssh-keygen -lffingerprints. - Verify the effective settings with
sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication'.