SSHD (Secure SHell Daemon) is the server-side program for secure remote connections cross-platform developed by none other than the OpenBSD team. However, not all SSH sessions are created equal.
The most important reason to choose public key authentication over password authentication is to defeat feasible brute-force attacks. Passwords should be avoided when possible because they are predictable and unavoidably weak. It is up to you to configure your SSH daemon in a secure manner. This blog post will explain how to master the SSH deamon, just as how Hercules tained the wild three-headed Kerberos beast.
SSH can generate DSA, RSA, ECDSA and Ed25519 key pairs. Let's go over these public-key algorithms:
DSA: This algorithm is deprecated due to very poor randomness. OpenSSH version 7.0 and newer even refuse DSA keys smaller than 1024-bits. DSA key pairs should not be used anymore.
RSA: This non-elliptic crypto algorithm which is based on prime numbers generates a relatively insecure key pair when you pick a key size below 2048-bits. The problem with RSA is its source of randomness. RSA is not vulnerable, but the source of entropy is the weakest link in the RSA algorithm. Many manufacturers are likely using the same source of randomness and perhaps even the same seeding. Furthermore, RSA will likely be the first to fall when quantum computations will get more mature.
ECDSA: The elliptic-curve (EC)DSA algorithm is supposed to help us combat these quantum computational attacks, while generating keys with significantly smaller key size without compromising the level of security. The size of the elliptic curve determines the difficulty to break the algorithm. However, secure implementations of the ECDSA curves are theoretically possible but very hard in practice. Furthermore, a weakness in RNG was publicly identified but still incorporated by NIST. We later learned from Snowden that the NSA had worked on the standardization process in order to become the sole editor of this Dual_EC_DRBG standard, and concluded that the Dual_EC_DRBG NIST standard did indeed contain a backdoor for the NSA. Why trust NIST curves when there is a more transparent way of doing crypto?
Ed25519: Long story short: it is not NIST and it is not NSA. The long story is that while NIST curves are advertised as being chosen verifiably at random, there is no explanation for the seeds used to generate these NIST curves. The process used to pick Ed25519 curves is fully documented and can be verified independently. This prevents a malicious party from manipulating the parameters. Furthermore, the Ed25519 algorithm is supposed to be resistant against side-channel attacks. Ed22519 key pairs have been supported since SSH version 6.5 (January 2014).
Generate an Ed25519 key pair
(Updated 2022): We are running Ubuntu 22.04 LTS together with OpenSSH 8.9p1 but the syntax in this post is the same for Debian based distro's:
$ lsb_release -d && ssh -V Description: Ubuntu 22.04.1 LTS OpenSSH_8.9p1 Ubuntu-3, OpenSSL 3.0.2 15 Mar 2022
Lets generate a fresh pair of Ed25519 keys on the client machine, so not on the server-side. Use a passphrase to secure your private key in order to prevent unauthorized actions. Also enable full disk encryption on your systems when possible.
$ ssh-keygen -o -a 256 -t ed25519 -C "$(hostname)-$(date +'%d-%m-%Y')" Generating public/private ed25519 key pair. Enter file in which to save the key (/home/$USER/.ssh/id_ed25519): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/$USER/.ssh/id_ed25519. Your public key has been saved in /home/$USER/.ssh/id_ed25519.pub. The key fingerprint is: SHA256:+zX9yMDeCyKoKSXT3QtfJyfsNHiZFxM020LiCbMERrE ubuntu-box1-01-07-2019 The key's randomart image is: +--[ED25519 256]--+ | .o. | | o. | | .E. | | + . + o | | . o *SB * o | | o o +.=.&.*. | | + .oo*.X* . | | . o oo.+ * o | | .o . . =..| +----[SHA256]-----+
The fingerprint is a short version of the server's public key. It is easier for a human to verify the fingerprint instead of the full key, while it is still hard to spoof another public key with the same fingerprint.
The following files will be generated from the above ssh-keygen command:
$ /home/$USER/.ssh/id_ed25519 #Private key Elliptic Curve Digital Signature Algorithm $ /home/$USER/.ssh/id_ed25519.pub #Public key Elliptic Curve Digital Signature Algorithm $ cat /home/$USER/.ssh/id_ed25519.pub #Later copy paste this public key to the server/traget ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBfJ2Qjt5GPi7DKRPGxJCkvk8xNsG9dA607tnWagOk2D ubuntu-box1-25-06-2019
Make the SSH key pair folder on the client-side only accessible for the local $USER. Note that root and your $USER username have different directories for storing generated SSH key pairs. Best practices dictate to leverage the $USER directory when generating your SSH key pair:
#Make the .ssh directory unreadable for other users and groups $ chmod 700 ~/.ssh $ chmod 700 /home/$USER/.ssh #Make the private SSH key read only $ chmod 400 /home/$USER/.ssh/id_ed25519 $ chmod 400 ~/.ssh/id_ed25519 #Make the local $USER own the SSH key pair files $ chown $USER:$USER ~/.ssh/id_ed25519* $ chown $USER:$USER /home/$USER/.ssh/id_ed25519*
Server-side public key configuration
On the server side we set the correct permissions and copy the public key to the authorized_keys file:
$ rm /etc/ssh/ssh_host_* #Delete old SSH keys $ rm ~/.ssh/id_* #Delete old SSH keys $ sudo dpkg-reconfigure openssh-server #Reset SSH config to defaults and generate new key files $ rm /home/$USER/.ssh/id_* #Delete old SSH keys $ vi /home/$USER/.ssh/authorized_keys #paste public key here $ cd /home/$USER/ && chmod g-w,o-w .ssh/ #The directory containing your .ssh directory must not be writeable by group or others $ chmod 600 /home/$USER/.ssh/authorized_keys #change permissions to r+w only for user $ service sshd restart #restart and reload keys into the SSH deamon
Let's test the authentication from the client to the server:
$ ssh USER@ssh-server-ip -i /home/$USER/.ssh/id_ed25519 -o PasswordAuthentication=no -vv
On the server side we harden the SSHD configuration file. Edit the following variables to your location environment:
ListenAddress: Change this to your IP-adres on which the SSH daemon should be listening.
AllowUsers: Change this to your $USER. Note: this is not root.
$ vi /etc/ssh/sshd_config Protocol 2 #Protocol 1 is fundamentally broken StrictModes yes #Protects from misconfiguration #ListenAddress [ip-here] #Listening address Port 22 #Listening port. Normal 22 AuthenticationMethods publickey #Only public key authentication allowed PubkeyAuthentication yes #Allow public key authentication HostKey /etc/ssh/ssh_host_ed25519_key #Only allow ECDSA pubic key authentication HostKeyAlgorithms email@example.com,ssh-ed25519 #Host keys the client should accepts KexAlgorithms curve25519-sha256 #Specifies the available KEX (Key Exchange) algorithms Ciphers firstname.lastname@example.org,email@example.com #Specifies the ciphers allowed MACs firstname.lastname@example.org #Specifies the available MAC alg. #Only allow incoming ECDSA and ed25519 sessions: HostbasedAcceptedKeyTypes ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519 #PubkeyAcceptedKeyTypes email@example.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,firstname.lastname@example.org,ssh-ed25519 #CASignatureAlgorithms ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519 PermitRootLogin no #Disable root login #AllowUsers [username] #Authorized SSH users are inside the admin group MaxAuthTries 5 #Maximum allowed authentication attempts MaxSessions 2 #Maximum allowed sessions by the user PasswordAuthentication no #No username password authentication PermitEmptyPasswords no #No empty password authentcation allowed IgnoreRhosts yes #Dont read users rhost files HostbasedAuthentication no #Disable host-based authentication ChallengeResponseAuthentication no #Unused authentication scheme X11Forwarding no #Disable X11 forwarding LogLevel VERBOSE #Fingerprint details of failed login attempts SyslogFacility AUTH #Logging authentication and authorization related commands UseDNS no #Client from a location without proper DNS generate a warning in the logs PermitTunnel no #Only SSH connection and nothing else AllowTcpForwarding no #Disablow tunneling out via SSH AllowStreamLocalForwarding no #Disablow tunneling out via SSH GatewayPorts no #Disablow tunneling out via SSH AllowAgentForwarding no #Do not allow agent forwarding Banner /etc/issue.net #Show legal login banner PrintLastLog yes #Show last login ClientAliveInterval 900 #Client timeout (15 minutes) ClientAliveCountMax 0 #This way enforces timeouts on the server side LoginGraceTime 30 #Authenticatin must happen within 30 seconds MaxStartups 2 #Max concurrent SSH sessions TCPKeepAlive yes #Do not use TCP keep-alive AcceptEnv LANG LC_* #Allow client to pass locale environment variables Subsystem sftp /usr/lib/ssh/sftp-server -f AUTHPRIV -l INFO #Enable sFTP subsystem over SSH
Note: remove the email@example.com and firstname.lastname@example.org values from the PubkeyAcceptedKeyTypes setting if you run OpenSSH 8.0 or earlier. sk-based keys are only support in OpenSSH version 8.1 or higher.
Lets test the modified /etc/ssh/sshd_config file and load the changes into the SSH deamon:
$ sudo sshd -T $ service sshd restart
You should be able to authenticate and connect again from your client:
$ ssh USER@ssh-server-ip -i /home/$USER/.ssh/id_ed25519 -o PasswordAuthentication=no -vv
The following SSH banner is created for a warning for legal and compliance purposes:
$ vi /etc/issue.net *************************************************************************** NOTICE TO ANY USERS This computer system is the private property of its corporate owner. It is for authorized use only. Users (authorized or unauthorized) have no explicit or implicit expectation of privacy. Any or all uses of this system and all files on this system may be intercepted, monitored, recorded, copied, audited, inspected, and disclosed to your employer, to authorized site, government, and law enforcement personnel, as well as authorized officials of government agencies, both domestic and foreign. By using this system, the user consents to such interception, monitoring, recording, copying, auditing, inspection, and disclosure at the discretion of such personnel or officials. Unauthorized or improper use of this system may result in civil and criminal penalties and administrative or disciplinary action, as appropriate. By continuing to use this system you indicate your awareness of and consent to these terms and conditions of use. LOG OFF IMMEDIATELY if you do not agree to the conditions stated in this warning and contact us. ****************************************************************************
Rate-limit SSH connections
Configure your host-based firewall on your server to take further take control of your SSH sessions as an alternative or addition to Fail2ban. Why install another package while iptables is already build-in:)
#SERVER_IP_MGMT = Listening IP in your /etc/ssh/sshd_config #NIC_MGMT = (v)NIC of the server for listening on the ssh server #Allow incoming SSH connections iptables -A INPUT -i $NIC_MGMT -p tcp -s 0/0 -d $SERVER_IP_MGMT --sport 32768:65535 --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT iptables -A OUTPUT -o $NIC_MGMT -p tcp -s $SERVER_IP_MGMT -d 0/0 --sport 22 --dport 32768:65535 -m state --state ESTABLISHED -j ACCEPT #Create new state for port 22 to combat brute-force attacks (new rule) iptables -I INPUT -i $NIC_MGMT -p tcp -s 0/0 -d $SERVER_IP_MGMT --sport 32768:65535 --dport 22 -m state --state NEW -m recent --set #Rule apply drop connection if there are more then 50 failed SSH login attempts connections every 3600 seconds (1 hour) iptables -I INPUT -i $NIC_MGMT -p tcp -s 0/0 -d $SERVER_IP_MGMT --sport 32768:65535 --dport 22 -m state --state NEW -m recent --update --seconds 3605 --hitcount 50 -j DROP
By default the SSHD listens on all interfaces which is denoted as 0.0.0.0:22. Make sure the SSHD is listening on a seperate magemenet NIC:
$ netstat -anltpu Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN 289/systemd-resolve tcp 0 0 220.127.116.11:22 0.0.0.0:* LISTEN 3359/sshd
Troubleshoot any authentication errors in the auth.log file:
$ cat /var/log/auth.log | grep sshd*
Lastly, use U2F hardware keys for two factor authentication to increase your level of security. You can read here how to configure YubiKeys with OpenSSH.