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.