Use public/private key pairs for authentication instead of passwords.
Generate a passphrase-protected SSH key for every computer that needs to access the server:
ssh-keygen
Permit public-key SSH access from the allowed computers:
Copy the contents of ~/.ssh/id_rsa.pub from each computer into individual lines of ~/.ssh/authorized_keys on the server, or run ssh-copy-id [server IP address] on every computer to which you are granting access (you'll have to enter the server password at the prompt).
Disable password SSH access:
Open /etc/ssh/sshd_config, find the line that says #PasswordAuthentication yes, and change it to PasswordAuthentication no. Restart the SSH server daemon to apply the change (sudo service ssh restart).
Now, the only possible way to SSH into the server is to use a key that matches a line in ~/.ssh/authorized_keys. Using this method, I don't care about brute force attacks because even if they guess my password, it will be rejected. Brute-forcing a public/private key pair is impossible with today's technology.
Other answers provide security, but there is one thing you can do which will make your logs quieter, and make it less likely that you'll be locked out of your account:
Move the server from port 22 to another one. Either at your gateway, or on the server.
It doesn't increase the security, but does mean all the random internet scanners won't clutter up you log files.
Enable two factor authentication with HOTP or TOTP. This is available from 13.10 onwards.
This includes using public key authentication over password authentication as in another answer here, but also requires the user prove he holds his second-factor-device in addition to his private key.
Summary:
sudo apt-get install libpam-google-authenticator
Have each user run the google-authenticator command, which generates ~/.google-authenticator and helps them configure their two factor devices (eg. the Google Authenticator Android app).
Edit /etc/ssh/sshd_config and set:
ChallengeResponseAuthentication yes
PasswordAuthentication no
AuthenticationMethods publickey,keyboard-interactive
Run sudo service ssh reload to pick up your changes to /etc/ssh/sshd_config.
Make the sshd block client IP's that have failed to supply correct login information "DenyHØsts" can do this job quite effectively. I have this installed on all my Linux boxes that are in some way reachable from the great outside.
This will make sure that force-attacks on the SSHD won't be effective, but remember (!) this way you can end up locking yourself out if you forget you password. This can be a problem on a remote server that you don't have access to.
If I want to have some additional security or need to access SSH servers deep inside some corporate network I setup a hidden service by using the anonymisation software Tor.
Install Tor and setup the SSH server itself.
Make sure sshd only listens at localhost.
Open /etc/tor/torrc. Set HiddenServiceDir /var/lib/tor/ssh and HiddenServicePort 22 127.0.0.1:22.
Look at var/lib/tor/ssh/hostname. There is a name like d6frsudqtx123vxf.onion. This is the address of the hidden service.
Furthermore I need Tor on my local host. If it is installed I can enter ssh myhost and SSH opens a connection via Tor. The SSH server on the other side opens its port only on localhost. So nobody can connect it via "normal internet".
There is a Debian Administration article on this topic. It covers basic SSH server configuration and also firewall rules. This could be of interest also to hardened an SSH server.
My approach to SSH hardening is... complex. The following items are in terms of how I do it, from the edge-most border of my network(s) to the servers themselves.
Border-level filtering of traffic through IDS/IPS with known service scanners and signatures in the blocklist. I achieve this with Snort via my border firewall (this is my approach, a pfSense appliance). Sometimes, I can't do this though, such as with my VPSes.
Firewall/Network filtering of the SSH port(s). I explicitly only allow certain systems to reach into my SSH servers. This is either done via a pfSense firewall at the border of my network, or the firewalls on each server explicitly being configured. There are cases where I can't do this, though (which is almost never the case, except in private pen-testing or security testing lab environments where firewalls won't help test things).
In conjunction with my pfSense, or a border firewall NAT-ing the internal network and separating from the Internet and the systems, VPN-Only Access to Servers. Gotta VPN into my networks to get to the servers, because there's no Internet-facing ports as such. This definitely doesn't work for all my VPSes, but in conjunction with #2, I can have one VPS be the 'gateway' by VPNing into that server, and then permit it's IPs to the other boxes. That way, I know exactly what can or cannot SSH in - my one box that is the VPN. (Or, in my home network behind pfSense, my VPN connection, and I'm the only one with VPN access).
Where #3 is not doable, fail2ban, configured to block after 4 failed attempts and block the IPs for an hour or more is a decent protection against people constantly attacking with bruteforcing - just block em at the firewall automatically with fail2ban, and meh. Configuring fail2ban is a pain though...
Port obfuscation by changing the SSH port. However, this is NOT a good idea to do without any additional security measures as well - the mantra of "Security through Obscurity" has already been refuted and disputed in many many cases. I have done this in conjunction with IDS/IPS and network filtering, but it's still a VERY poor thing to do on its own.
MANDATORY Two-Factor Authentication, via Duo Security's Two-Factor Authentication solutions. Every single one of my SSH servers has Duo configured on it, such that in order to even get in, 2FA prompts happen, and I have to confirm each access. (This is the ultimate helpful feature - because even if someone has my passphrase or breaks in, they can't get past the Duo PAM plugins). This is one of the biggest protections on my SSH servers from unauthorized access - each user login MUST tie back to a configured user in Duo, and since I have a restrictive set, no new users can be registered in the system.
My two-cents to securing SSH. Or, at least, my thoughts on approach.
You might want to checkout the FreeOTP app from RedHat instead of using Google Authenticator. Sometimes when updating the app, they lock you out! ;-)
If you want to use other hardware tokens like a Yubikey or an eToken PASS or NG or if you have many users or many servers, you might want to use an opensource two factor authentication backend.
Use public/private key pairs for authentication instead of passwords.
Generate a passphrase-protected SSH key for every computer that needs to access the server:
ssh-keygen
Permit public-key SSH access from the allowed computers:
Copy the contents of
~/.ssh/id_rsa.pub
from each computer into individual lines of~/.ssh/authorized_keys
on the server, or runssh-copy-id [server IP address]
on every computer to which you are granting access (you'll have to enter the server password at the prompt).Disable password SSH access:
Open
/etc/ssh/sshd_config
, find the line that says#PasswordAuthentication yes
, and change it toPasswordAuthentication no
. Restart the SSH server daemon to apply the change (sudo service ssh restart
).Now, the only possible way to SSH into the server is to use a key that matches a line in
~/.ssh/authorized_keys
. Using this method, I don't care about brute force attacks because even if they guess my password, it will be rejected. Brute-forcing a public/private key pair is impossible with today's technology.I would suggest:
Using fail2ban to prevent brute force login attempts.
Disabling logging in as root via SSH. This means an attacker had to figure out both the username and the password making an attack more difficult.
Add
PermitRootLogin no
to your/etc/ssh/sshd_config
.Limiting the users that can SSH to the server. Either by group or just specific users.
Add
AllowGroups group1 group2
orAllowUsers user1 user2
to limit who can SSH to the server.Other answers provide security, but there is one thing you can do which will make your logs quieter, and make it less likely that you'll be locked out of your account:
Move the server from port 22 to another one. Either at your gateway, or on the server.
It doesn't increase the security, but does mean all the random internet scanners won't clutter up you log files.
Enable two factor authentication with HOTP or TOTP. This is available from 13.10 onwards.
This includes using public key authentication over password authentication as in another answer here, but also requires the user prove he holds his second-factor-device in addition to his private key.
Summary:
sudo apt-get install libpam-google-authenticator
Have each user run the
google-authenticator
command, which generates~/.google-authenticator
and helps them configure their two factor devices (eg. the Google Authenticator Android app).Edit
/etc/ssh/sshd_config
and set:Run
sudo service ssh reload
to pick up your changes to/etc/ssh/sshd_config
.Edit
/etc/pam.d/sshd
and replace the line:with:
More details on different configuration options are my blog post from last year: Better two factor ssh authentication on Ubuntu.
Make the sshd block client IP's that have failed to supply correct login information "DenyHØsts" can do this job quite effectively. I have this installed on all my Linux boxes that are in some way reachable from the great outside.
This will make sure that force-attacks on the SSHD won't be effective, but remember (!) this way you can end up locking yourself out if you forget you password. This can be a problem on a remote server that you don't have access to.
Here's one easy thing to do: install ufw (the "uncomplicated firewall") and use it to rate limit incoming connections.
From a command prompt, type:
If ufw is not installed, do this and try again:
Many attackers will try to use your SSH server to brute-force passwords. This will only allow 6 connections every 30 seconds from the same IP address.
If I want to have some additional security or need to access SSH servers deep inside some corporate network I setup a hidden service by using the anonymisation software Tor.
localhost
./etc/tor/torrc
. SetHiddenServiceDir /var/lib/tor/ssh
andHiddenServicePort 22 127.0.0.1:22
.var/lib/tor/ssh/hostname
. There is a name liked6frsudqtx123vxf.onion
. This is the address of the hidden service.Open
$HOME/.ssh/config
and add some lines:Furthermore I need Tor on my local host. If it is installed I can enter
ssh myhost
and SSH opens a connection via Tor. The SSH server on the other side opens its port only on localhost. So nobody can connect it via "normal internet".There is a Debian Administration article on this topic. It covers basic SSH server configuration and also firewall rules. This could be of interest also to hardened an SSH server.
See there article: Keeping SSH access secure.
My approach to SSH hardening is... complex. The following items are in terms of how I do it, from the edge-most border of my network(s) to the servers themselves.
Border-level filtering of traffic through IDS/IPS with known service scanners and signatures in the blocklist. I achieve this with Snort via my border firewall (this is my approach, a pfSense appliance). Sometimes, I can't do this though, such as with my VPSes.
Firewall/Network filtering of the SSH port(s). I explicitly only allow certain systems to reach into my SSH servers. This is either done via a pfSense firewall at the border of my network, or the firewalls on each server explicitly being configured. There are cases where I can't do this, though (which is almost never the case, except in private pen-testing or security testing lab environments where firewalls won't help test things).
In conjunction with my pfSense, or a border firewall NAT-ing the internal network and separating from the Internet and the systems, VPN-Only Access to Servers. Gotta VPN into my networks to get to the servers, because there's no Internet-facing ports as such. This definitely doesn't work for all my VPSes, but in conjunction with #2, I can have one VPS be the 'gateway' by VPNing into that server, and then permit it's IPs to the other boxes. That way, I know exactly what can or cannot SSH in - my one box that is the VPN. (Or, in my home network behind pfSense, my VPN connection, and I'm the only one with VPN access).
Where #3 is not doable, fail2ban, configured to block after 4 failed attempts and block the IPs for an hour or more is a decent protection against people constantly attacking with bruteforcing - just block em at the firewall automatically with fail2ban, and meh. Configuring fail2ban is a pain though...
Port obfuscation by changing the SSH port. However, this is NOT a good idea to do without any additional security measures as well - the mantra of "Security through Obscurity" has already been refuted and disputed in many many cases. I have done this in conjunction with IDS/IPS and network filtering, but it's still a VERY poor thing to do on its own.
MANDATORY Two-Factor Authentication, via Duo Security's Two-Factor Authentication solutions. Every single one of my SSH servers has Duo configured on it, such that in order to even get in, 2FA prompts happen, and I have to confirm each access. (This is the ultimate helpful feature - because even if someone has my passphrase or breaks in, they can't get past the Duo PAM plugins). This is one of the biggest protections on my SSH servers from unauthorized access - each user login MUST tie back to a configured user in Duo, and since I have a restrictive set, no new users can be registered in the system.
My two-cents to securing SSH. Or, at least, my thoughts on approach.
You might want to checkout the FreeOTP app from RedHat instead of using Google Authenticator. Sometimes when updating the app, they lock you out! ;-)
If you want to use other hardware tokens like a Yubikey or an eToken PASS or NG or if you have many users or many servers, you might want to use an opensource two factor authentication backend.
Lately I wrote a howto about this.