Linux/BSD server SSH best practice

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:


and add a line to the config to allow the user to become sudo without a password:


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 "$" "$USERNAME@$HOST"

As alternative you can use ed25519 instead of rsa.

The key will be copied onto the server at the location:


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
	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:


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:

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:


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:


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 : 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" -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: