Comprehensive SSH Linux sysadmin guide
~296sOverview: Generating and using SSH (Secure Shell) keypairs securely on Linux for host authentication purposes.
Motivation
SSH is the main tool used by sysadmins to remotely control servers in a secure way. Learning how to use and harden SSH is imperative for privsec enthusiasts.
Assumptions
- you have root access to a VPS (Debian/-based) from a GNU/Linux machine
- basic terminal and nano editor knowledge
- ~30m time
0. Install OpenSSH on client
The openssh-client package should normally be installed by default on Debian 13 (your local machine, not the server).
Check with ssh. It should return a list of usage flags. If for some reason you don't have it just grab it from apt:
sudo apt update && sudo apt install openssh-client
1. Generate SSH keypair on client
To generate your SSH keypair run:
ssh-keygen -o -a 100 -t ed25519
Reply yes when asked if the keys should be stored as /home/$user/.ssh/id_ed25519.
Note: run the command with fewer key derivation function rounds (ie. -a 75) only if your machine can't handle the load (a higher value == higher privkey passphrase strength vs brute force attacks).
Note: 'ed25519' is the recommended key type by OpenSSH; do not use 'rsa' or 'ecdsa'.
2. Harden SSH configuration on client
The main SSH file is /etc/ssh/ssh_config and it includes all files in the /etc/ssh/ssh_config.d/ directory.
Check what file(s) are imported from that folder (if any):
ls /etc/ssh/ssh_config.d
You might see something like this on Debian 13:
20-systemd-ssh-proxy.conf
To overwrite the system-wide defaults we need to create a new file with a higher number prefix like 30-security.conf since SSH config files are read in filename order (settings in higher/later files are applied last when setting the same option):
sudoedit /etc/ssh/ssh_config.d/30-security.conf
Let's safely apply some of the best and most recommended cryptographic algorithms available today:
Host *
AddressFamily inet
StrictHostKeyChecking accept-new
VisualHostKey yes
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr
MACs umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
KexAlgorithms sntrup761x25519-sha512,sntrup761x25519-sha512@openssh.com,mlkem768x25519-sha256,curve25519-sha256,curve25519-sha256@libssh.org
HostKeyAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519
PubkeyAcceptedAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519
Press CTRL+X > Y > Enter to save and exit the editor (assuming the default is nano on Debian).
Note: only include 'AddressFamily inet' if you don't want to use IPv6.
Note: use 'StrictHostKeyChecking yes' if the host key is already in 'known_hosts'.
2.1 (optional) Check config on client
Use ssh -G to display the effective values after all SSH config files are processed:
ssh -G localhost | grep -Ei 'visualhostkey|ciphers|macs|kexalgorithms|hostkeyalgorithms|pubkeyacceptedalgorithms'
The output should be the options that we set in the 30-security.conf file. If some values differ, make sure you don't have another file in /etc/ssh/ssh_config.d/ with a higher prefix (31+).
3 Install OpenSSH on server
sudo apt update && sudo apt install openssh-server
3.1 Harden SSH configuration on server
Open two terminal tabs/windows and establish two separate SSH connections to the server, for backup purposes:
ssh root@[SERVER-IP]
Restart the sshd daemon:
systemctl restart sshd
If both connections are still alive, try opening a third terminal window and test new connections:
ssh root@[SERVER-IP]
Proceed if everything is still working as expected (no interruptions).
Similar to what we did on the client, let's create a file in /etc/sshd_config.d/:
sudoedit /etc/ssh/sshd_config.d/30-security.conf
And paste the following values:
Protocol 2
AddressFamily inet
MaxAuthTries 3
HostKey /etc/ssh/ssh_host_ed25519_key
HostKeyAlgorithms ssh-ed25519
PubkeyAcceptedAlgorithms ssh-ed25519,sk-ssh-ed25519@openssh.com
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs umac-128-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
KexAlgorithms sntrup761x25519-sha512,sntrup761x25519-sha512@openssh.com,mlkem768x25519-sha256,curve25519-sha256,curve25519-sha256@libssh.org
KbdInteractiveAuthentication no
UsePAM yes
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
PrintMotd no
DebianBanner no
Note: only include 'AddressFamily inet' if you don't want to use IPv6.
4. Install SSH pubkey to server
You can either upload your user SSH key to the server or share root's key (if any) by copying it over to the newuser home. Choose one of the options below.
4.1 Option A: Upload user key
To copy the public SSH key to the server, run ssh-copy-id on the client:
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@[SERVER-IP]
The output should be similar to this:
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/user/.ssh/id_ed25519.pub"
The authenticity of host [SERVER-IP] can't be established.
ECDSA key fingerprint is SHA256:[SERVER-FINGERPRINT].
Are you sure you want to continue connecting (yes/no/[fingerprint])?
Note: replace '[SERVER-IP]' with the actual IP address of the server and 'root' with your provider default first time login user name.
4.2 Option B: Reuse root key (if any)
Switch to root if necessary:
su - root
Copy the key to $user home:
cp /root/.ssh/authorized_keys /home/$user/.ssh/authorized_keys
Now grant $user ownership over the folder:
chown $user /home/$user/.ssh/authorized_keys
Adjust permissions:
su $user && chmod 600 /home/$user/.ssh/authorized_keys
Note: replace '$user' with a privileged username on your server.
4.1 Server login
You should be able to connect with:
ssh root@[SERVER-IP]
You SSH pubkey should be located in authorized_keys.
Note: replace 'root' and '[SERVER-IP]' accordingly and check if the SSH server fingerprint matches the one from your provider if possible.
Observations
- always use strong passphrases for SSH keys
- never share your private key with anyone and store it safely
-
check permissions if you have any issues:
-
[Client]
~/.ssh/id_*.pubeither 600 OR 644 -
[Client]
~/.ssh/configeither 600 OR 644 -
[Client]
~/.ssh/known_hostsshould be 644 -
[Server]
/etc/ssh/ssh_configand/etc/ssh/ssh_config.d/*.confowned by root:root and 644 -
[Server]
/etc/ssh/ssh_host_*_keyowned by root:root and 600 -
[Server]
/etc/ssh/ssh_host_*_key.pubowned by root:root and 644 -
[Client/Server]
~/.ssh/folder should be owned by $user:$user and 700 -
[Client/Server]
~/.ssh/authorized_keysshould be 600
-
[Client]
Consult the Linux server security essentials1 guide for tips on how to lock down SSH even more by restricting root access entirely, change the default port and more.