As soon as you get a new Linux server up and running the first things i usually do is to remove all listening services in order to reduce the attack surface as much as possibile.
The SSH service is usually the first one i start with. Reducing risks about it involves the following operations:
- removing “root” access in favour of an unprivileged user;
- removing use of keyboard password in favour of key authentication;
- whitelisting a series of IPs that can access the server;
Prerequisites (optional)
This step is pure optional but it saved me many times. Working on multiple terminals with different servers can be tricky and it can happen that you issue commands to the wrong server (i.e. remember the famous gitlab database failure). To avoid this just decorate your prompt with a coloured hostname.
In bash, for example, adjust the .bashrc
file with the following code:
export PS1="\u@\[\e[1;33m\]\h\[\e[m\]:\w\$ "
Access the server using an unprivileged user with public key authentication
Create a new user onto the server
I start by creating a new user called $USERNAME and belonging to his own group called $USERGROUP onto the server $HOST. Replace any $VARIABLES you see in this article with your own specific values.
Those command should be easy to understand otherwise you need a to learn the ropes first.
Create the group:
groupadd $USERGROUP
Create the user:
useradd -m -s /bin/bash -g $USERGROUP $USERNAME
Set the password:
passwd $USERNAME
If you lack fantasy use this to generate a decent random password:
openssl rand -base64 32
Optionally enable sudo
If you’re a sudo fans you can run:
visudo
and add a line to the config to allow the user to become sudo without a password:
$USERNAME ALL=(ALL:ALL) NOPASSWD:ALL
test if you can login as $USERNAME and become root before continuing with the next steps.
Create a key on your client
On your local client machine, you can generate the key (both public and private) be used to access the server:
ssh-keygen -b 4096 -a 6000 -t rsa -C 'generated by badpenguin' -f ~/.ssh/$HOST.id_rsa
Once it has been create then copy the public key onto the remote server using:
ssh-copy-id -i "$HOST.id_rsa.pub" "$USERNAME@$HOST"
As alternative you can use ed25519
instead of rsa
.
The key will be copied onto the server at the location:
/home/$USERNAME/.ssh/authorized_keys
Just for safety make sure that permission mode on those remote files is as the following:
- 0600 for the file
.ssh/authorized_keys
- 0700 for the directory
.ssh
Last steps require to modify (or create) the file ~/.ssh/config
file on your client machine appending the following config block:
Host $HOST HostName $HOST Port $SSHPORT User $USERNAME IdentityFile ~/.ssh/$HOST.id_rsa IdentitiesOnly yes
If you want to know more about the config’s file syntax just run: man ssh_config
Now, if you did it everything correctly now you’re able to login onto the server just running:
ssh $HOSTNAME
Changing the key’s passphrase
Is it possible to change the password of your key without affecting the operation we already did. If you changed your mind about the password you can run:
ssh-keygen -f~/.ssh/$HOST.id_rsa -p
Storing the key in a password manager
If your filesystem is not encrypted it could be usefull to move your keys in a password manager. Keepass is a tool available for Linux that has a plugin called KeeAgent that acts as an ssh-agent.
That means that you can use it to store securely your keys in KeeAgent and use them to authenticate.
For an how to just follow: https://superuser.com/questions/905449/enable-ssh-keys-on-startup-in-keepass-with-keeagent-plugin
If you did it do not forget edit ~/.ssh/config
and remove the lines starting with IdentityFile and IdentitiesOnly from the config’s sections referring the hostname’s keys moved to KeeAgent.
Once tested that everything works you can delete the keys:
rm -vi ~/.ssh/$HOST.id_rsa*
Locking out “root” but not yourself
We’re now going to lock root out of the door. Be sure to not lock yourself out from the server, keep a root terminal open if any bad thing happen.
The following settings must be applied to the file /etc/ssh/sshd_config
onto the server
PasswordAuthentication no ChallengeResponseAuthentication no PermitRootLogin no AllowUsers $USERNAME
Once you’re done apply them reloading the SSH service:
/etc/init.d/ssh reload
NOTE: some Linux system that runs systemd uses a different syntax.
Totally disable “root”
I don’t suggest this because it could be usefull to login via tty or remote serial to server but if you wish you can totally disable root login but still allowing sudoers by running:
passwd -l root
Stop annoying bots: change the listening port
As soon a server is connected to internet it get bombed by any kind of scanner. Bots trying to login via SSH are quite annoying when checking the logs daily.
To minimize that i usually change SSH port. This is NOT a security features. The so called “Security through obscurity” must be avoided. In my case i just do it to reduce noise in the logs.
Anyway to change it edit /etc/sshd/sshd_config
file onto the server and change the Port
settings and restart the service.
Do not forget to modify ~/.ssh/config
on your client to have the matching hostname’s section to have thePort
settings to the same value.
Restricting access to only whitelisted IPs
The settings between Linux and FreeBSD change a bit.
In linux change /etc/hosts.allow
and make sure a line exists of that kind:
SSHD: $CLIENTIP
You can listen more IP by separating them by space. For more info run: man hosts.allow
Then change /etc/hosts.deny
and make sure a line exists with:
SSHD: ALL
For more info on this run: man hosts.deny
In FreeBSD the situation is different. Everything can be done with just /etc/hosts.allow
but the syntax is quite different:
sshd : $CLIENTIP $CLIENTIP2 $CLIENTIP3 : ALLOW sshd : ALL : DENY
It also allow to do funny stuffs like blocking ssh bots just using the router’s blackhole instead of a firewall:
sshd : ALL : spawn (route add "%a" 127.0.0.1 -blackhole ) & : DENY
Create a webmaster user
If you don’t use continuos integration or some other deployment tool or you just want a user to access and edit the webserver files then i usually create a dedicated user for web development.
This user will have the same group as the webserver but it will be chrooted to the directory of the domain it is allowed to manage, avoid him to access other domains in other directories.
You can follow the same procedure describe above to login without a password using a key but with just a few differences.
The $USERGROUP cannot be choose freely but must be the group of your webserver. For instance www-data
in Debian.
You can use ssh to chroot it directly adding the following config to /etc/sshd/sshd_config:
Match User $USERNAME ChrootDirectory /var/www/$DOCUMENTROOT/ ForceCommand internal-sftp AllowTcpForwarding no #PasswordAuthentication yes PermitTunnel no AllowAgentForwarding no X11Forwarding no
PasswordAuthentication can be turned on for the time strictly necessary to copy the key from your client to the server. Once you’re done remove or comment it.
Firewall and Fail2Ban
As you noticed i didn’t mentioned both of them simply because i just don’t use them.
Beyond this tutorial
This is a todo list for myself:
- Use OTP authentication. Evaluating this method when i’m not in office or i don’t have fixed IPs: http://docs.hardentheworld.org/Applications/OpenSSH/#otp-setup
- Switch from RSA key to ed25519: http://blog.siphos.be/2015/08/switching-openssh-to-ed25519-keys/ and https://blog.g3rt.nl/upgrade-your-ssh-keys.html
- check wich tool among: ansible, puppet, chef, etc. provides automation for the above process;