When we want to connect securely to a remote machine from within a unix-like system we type in the terminal:

ssh root@<server>

where <server> is the IP address of our server machine or its domain name. After that we are prompted to enter the password for server root. This is the default procedure for a newly created server in order to connect securely via the SSH protocol through port 22. Let’s optimize this procedure and make it more secure.

For all the following steps, we suppose that we are connected to a Debian-like server machine via ssh as root with the above command. The same is true also for other Linux systems. Regarding the client, I use also a linux system although the working procedure is similar to Windows machines. Last thing, I use vim as a command line editor, you can use whatever you like.

Connect to a regular server user account

As we saw in the previous post it is important to connect to the server with a regular user account, not root. Also, this user should have access to sudo command in order to execute root operations. So, as long as there is such a user, we can connect to the server by issuing this command (from client side):

ssh johndoe@192.168.2.100

and enter the password of the user johndoe. Obviously, user and IP address are random values in our example, but they will be used throughout this post.

SSH Server configuration file

Now, connected as johndoe we can make some modifications on our server. So, on the server side, we open the sshd daemon configuration file:

sudo vim /etc/ssh/sshd_config

At the beginning of the file there is the following line:

Port 22

For security reasons we could change this line and assign a port number which is available. The usual pool of ports is in the range of 1024 - 49151. In order not to use a port which is already used for listening by another service we can check the listening ports of our system with:

sudo lsof -i -P | grep LISTEN

Take note of the new port you defined in order to be able to connect! As we mentioned before, we should already be connected to our server with a user other than root. We should definitely change also the following line

PermitRootLogin yes

to

PermitRootLogin no

in order to not allow remote ssh connection via the root user. There is also another interesting line in this file which allows us to perform connection via password which is the default behavior for ssh connections. The line is the following:

PasswordAuthentication yes

In some systems this line could be commented out. For now, we will leave it as it is, until we mention the public/private keys which is a safer way to perform the connection. There are also some other options to consider in this file and usually the comments provide very good explanation for their purpose. But we will not perform other changes for now. Save the file and restart the sshd service for the changes to take effect

sudo systemctl restart sshd

You will notice that you weren’t signed off by the system although you are connected with the old configuration. So from now, when you want to connect to the server via ssh, you should always use a user different from root and also define the port. This can be done with the following command:

ssh -p <new_port> <user>@<ip_address>

SSH client configuration file

Before talking about ssh private/public keys, we will mention a way to automate our ssh connections through a config file, which is very useful in case we have access to multiple server machines.

Let’s say for example that we have a server machine with IP address 192.168.2.100 and we have a user johndoe defined in the system and also have configured the sshd config file to listen to port 5000. In order to store this connection information we can create a new file in our client machine with:

touch ~/.ssh/config

and open the file and write the following:

Host awesome-server
    Hostname	192.168.2.100
    Port	    5000
    User	    johndoe

Let’s save the file. You’ll notice that for that particular connnection we used the alias awesome-server. Now we can connect to this server with:

ssh awesome-server

and connect by typing johndoe’s password. How awesome is that? We have stored all our connections’s details in this file and saved much typing! Let’s add another connection to our file. Now our file has the following content:

Host awesome-server
    Hostname	192.168.2.100
    Port	    5000
    User	    johndoe

Host bsd-server
    Hostname	192.168.2.50
    Port	    3500
    User	    alice

One last important thing about the config file. This file should be readable and writable only by the owner, with no other permissions at all. This file should not be accessible to anyone else. So, in order to be sure we can execute the following command to the client machine:

chmod 600 ~/.ssh/config

where 600 is the same as u=rw which gives only read and write permissions to the owner of the file.

Public-key cryptography

Now, we can add another crucial layer of security to our connection by introducing ssh authenticated keys. The method is called public-key cryptography. You can read more about this method of cryptography in Wikipedia. Basically, we create a pair of keys, one private and one public, which is unique to one another. The point is that we keep the private key somewhere safe in our client computer and we can move the public key in our server. The private key should be only visible to us whereas the public key can be world-visible. But the combination of the two keys is unique. In this way, we can achieve a level of authentication between the two computers (client and server) which is stronger than password authentication.

Let’s start by creating the pair of cryptographic keys. On the client side, we issue the command:

ssh-keygen -t ed25519 -f ~/.ssh/test_id_ed25519 -C "test-client"

Let’s explain the above options and flags. With -t ed25519 we define the type of cryptographic keys to create. There are only a few values acceptable but the most common are rsa and ed25519. These are cryptographic technologies using different algorithms. You can find more information here about ssh keys. The other option -f ~/.ssh/test_id_ed25519 is used to define where to store our private key file. The public key will be in the same location but with an added extension .pub. Finally, -C "test-client" adds a comment to our public key which acts as an identifier. It is completely optional and if you don’t include it, it will take as default value the hostname of the client. You can read more about the command ssh-keygen in its manual page with man ssh-keygen.

After we typed our command and press ENTER the shell will ask us for a passphrase. This is an extra precaution step, it is completely optional but the best practice is to enter one. After we confirm the passphrase, our two keys are present in the location we defined.

Next, we have to move the public key to the user’s home directory of our server. For this we’ll use the utility ssh-copy-id to copy our public key to the server example from before:

ssh-copy-id -i test_id_ed25519.pub -p 5000 johndoe@192.168.2.100

where -i test_id_ed25519.pub is the public key’s path and -p 5000 is the port that we defined in the previous steps. What this command basically did, was to copy the contents of the public key from the client computer to a new line of the file ~/.authorized_keys in the server side. If you check this file in the server side with

cat ~/.ssh/authorized_keys

it will print out something like this:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AILmNs8jsj9xs0N6mmvrxPurRAgsICOOk0DbS2jjA2ptwU3k test-client

That means that our public key was registered in the server machine and we are now ready to perform a connection based on authenticated keys. So, we logout from the current ssh password-based session and, on our client, we type:

ssh -i test_id_ed25519 -p 5000 johndoe@192.168.2.100

where -i test_id_ed25519 is the path of the private key in our client. It will ask us for the passphrase that we defined earlier and the connection is achieved! We could also modify the client’s session config file in order to add our newly created keys. So, on client side, we modify the ~/.ssh/config file accordingly

Host awesome-server
    Hostname        192.168.2.100
    Port            5000
    User            johndoe
    IdentityFile    ~/.ssh/test_id_ed25519

where we added the parameter IdentityFile with a value corresponding to the path of our private key. Now we can achieve the connection with just:

ssh awesome-server

One more thing. If you remember, in our server configuration file /etc/ssh/sshd_config we had an option

PasswordAuthentication yes

which permits ssh connections based on password. As long as we have in our server the public key and achieved a key-based connection then we could set this option to no in order to not allow password-based connections. But we have to be careful here because that means that if we try to connect from another computer we will not be able to achieve connection as long as we don’t have the private key. There are two ways to overcome this. Either we copy the private key to our new client machine or we enter directly to the server via the web interface of our provider (if our server is provided by a cloud company) and change this option to allow password-based connections.

Conclusion

That was a long post, I hope it didn’t confuse you a lot :) The main point is that our servers hold in most cases important files and we should keep them as much secure as possible. We touched some points regarding SSH connections in this post, in a future post we will check some basic firewall rules we can apply to our system.