Authors: João Paulo Barraca, Vitor Cunha, Paulo C. Bartolomeu, Alfredo Matos, Catarina Silva
Slides
Download here
Introduction
This guide will explore authentication mechanisms by configuring SSH
(Secure Shell) secure sessions. SSH
is an IETF standard comprised of Authentication, Connection, and Transport standards, composed of several RFCs (RFC 4250,
RFC 4251,
RFC 4252,
RFC 4253, and
RFC 4254), and is a common protocol used for several purposes, such as remote shells, git repository data transport, and many other interesting scenarios.
It supports several authentication mechanisms, from which we will explore simple username and password authentication, asymmetric key authentication, and finally, One Time Passwords (OTP).
Setup
To execute this guide, you will create a base Docker image with the latest Ubuntu LTS server to simulate a different virtual machine within a container. This image and general approach are not representative of a real-world docker deployment. The Ubuntu container will take the server role, while your course VM will take the client role.
Server Configuration
Ubuntu Container
Use the following Dockerfile to create the base image with the latest Ubuntu LTS server.
FROM ubuntu:noble
RUN apt update && apt install -y iproute2 net-tools adduser nano
CMD ["/bin/bash", "-c", "trap : TERM INT; sleep infinity & wait"]
Create the image with the following command:
vm:~$ docker build . -t ubuntu-lts
Upon success of the previous command, launch the Ubuntu container
vm:~$ docker run --name server --hostname server -d ubuntu-lts
Now check if the Ubuntu cointainer is alive:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d4a9c0da905e ubuntu-lts "/bin/bash -c 'trap …" 4 seconds ago Up 4 seconds eager_leakey
(...)
Services
Enter the console of the server and check its IP address:
vm:~$ docker exec -it server /bin/bash
root@server:/# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
50: eth0@if51: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:0a:8b:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.139.0.3/24 brd 10.139.0.255 scope global eth0
valid_lft forever preferred_lft forever
In this case, the IP address is 10.139.0.3.
In the container, create a user that you can use to log remotely into the server (we suggest user sio
, password sio
), and install the required services.
root@server:/# adduser sio
You can check if the Telnet
and SSH
services are already running in the server (Ubuntu container). For that, use the following command to check if the TCP ports used by those services are available to accept connections.
(It is not expected that any services should be running now.)
root@server:/# netstat -atnup
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
Using the following commands, you may update the apt
database, install and configure the SSH
and Telnet
services. Use those that are adequate to install the services missing in Ubuntu container.
root@server:/# apt update
... OUTPUT OMITTED...
root@server:/# apt install inetutils-telnetd
Edit /etc/inetd.conf
with nano
, and enable (uncomment) the Telnet
line (23):
# /etc/inetd.conf
telnet stream tcp nowait root /usr/sbin/tcpd /usr/sbin/telnetd
Then restart the Telnet
service, with the command:
root@server:/# service inetutils-inetd restart
Install the SSH
service in the container with the following command:
Hint: Should you be asked during installation, the Geographic area is 8, and the Time zone is 25
root@server:/# apt install openssh-server
If you need to restart the SSH
service, you may use the command:
root@server:/# service ssh restart
Check that both services are up and running.
root@server:/# netstat -atnup
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 4121/sshd: /usr/sbi
tcp 0 0 0.0.0.0:23 0.0.0.0:* LISTEN 432/inetutils-inetd
tcp6 0 0 :::22 :::* LISTEN 4121/sshd: /usr/sbi
Client Configuration
SSH
and Telnet
client applications are installed in most Linux distributions by default. In case you need to install any of them (you shouldn’t need to with the course Virtual Machine), you may use the following commands (choose the ones that are missing, if any):
vm:~$ sudo apt install telnet ssh
Inspecting network traffic
One of the toolkits under an engineer’s belt is using traffic-capturing utilities to find out what is going on. This tool can be very useful for security purposes.
You must also install Wireshark
in your _Virtual Machine (not the container) to capture and analyze packets exchanged between the Ubuntu container (the server) and the Virtual Machine (the client).
For this, issue:
vm:~$ apt install wireshark
In the client (the Virtual Machine), use Wireshark
capture the traffic exchanged between the server and the client. Select the docker0
network bridge; you need to start Wireshark
as root
, or the proper capability to see this network interface. Apply a filter to only observe Telnet
and SSH
packets by applying the following rule in the Wireshark
filter window:
telnet || ssh
With this filter applied, Wireshark
will show all the SSH
and Telnet
packets between server and client.
Inspecting Telnet
traffic
In the client (the Virtual Machine), with an active traffic capture, start a Telnet
session to the server (Ubuntu container). Use the previously acquired Ubuntu container IP address and, when asked, use the credentials you created before:
vm:~$ telnet IP_ADDRESS
After login, list the contents of the root folder using the command:
sio@server:~$ ls /
Close the Telnet
session, stop the Wireshark
capture, and analyze the captured packets. Can you observe the contents of the root folder you listed above in the captured packets?
Still analyzing the captured Telnet
traffic, can you find the password you used to log in when starting the Telnet
session on the server (Ubuntu container)?
What do you conclude regarding the security of the Telnet
protocol?
Inspecting SSH
traffic
In the client (the Virtual Machine), start a new capture and apply a filter to show only the captured SSH
packets.
Start an SSH
session with the server (Ubuntu container). If it is the first connection to that server, the server will authenticate, presenting its public key, and you will be asked if you trust the key and want to save it. Say yes to proceed. Login into the server and list the contents of the root folder using the command:
vm:~$ ssh username@host # ssh sio@DOCKER_SERVER_IP
... OUTPUT OMITTED ...
sio@server:~$ ls /
Close the Telnet
session, stop the Wireshark
capture, and analyze the captured packets. Can you observe any data in the packets? What do you conclude about SSH
general security compared to Telnet
?
Authentication in SSH
In this section, you will analyze how the server and clients authenticate in the SSH
protocol. It is assumed that SSH
configuration files are in their original state, i.e., no change has been made to the SSH
configuration.
Server authentication
In the first interaction of an SSH
client with an SSH
server, the client
must validate the server’s identity. For that purpose, the SSH
server sends
its public key to the client, and the respective fingerprint is presented
to the user so they can verify it and indicate if the key is correct or
not, i.e., if the user trusts the server to which a session is being
started, or not. For this, the user must have the
public key fingerprint of the SSH
server to which they want to connect
(the fingerprint should have been given the public key fingerprint when her account was
created). To get the fingerprint of the SSH
server public key, issue the
following command in the server computer and write the fingerprint
value:
root@server:/# ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub
In the client (the Virtual Machine), eliminate the public keys you possibly
stored from previous SSH
connections. For that purpose, use the
following command:
vm:~$ rm ~/.ssh/known_hosts
From the , start an SSH
session with the server using
the following command, where username (e.g., sio
) is your account name in the
server, and host is the name or IP address of the computer with the SSH
server (Ubuntu container):
vm:~$ ssh username@host # ssh sio@DOCKER_SERVER_IP
The user on the client will be presented with a message similar to the one shown below, asking if the user trusts the identity of the SSH
server they are trying to connect to.
Compare the presented public key fingerprint with the fingerprint you get in the SSH
server. If they are equal, you are trying to connect to the SSH
server you intended to connect to and must answer yes
. Otherwise, your connection may be intercepted, or you are connecting to a wrong server.
The authenticity of host '10.139.0.3 (10.139.0.3)' can't be established.
RSA key fingerprint is 6a:de:e0:af:56:f8:0c:04:11:5b:ef:4d:49:ad:09:23.
Are you sure you want to continue connecting (yes/no)? yes
After the confirmation, the client stores the public key in the
~/.ssh/known_hosts
file, and the user is never again requested to
confirm that SSH
server public key, as long as the server keeps
presenting the same public key.
Start the SSH
session by introducing your password to
authenticate yourself to the SSH
server. Login and password is the
default mechanism for user authentication with SSH
servers.
User authentication with an asymmetric key pair
In the client (the Virtual Machine), you must generate an asymmetric key pair to use for client authentication when starting a session on an SSH
server. For that, use the following command:
vm:~$ ssh-keygen -t rsa
Press Enter
to accept the default name for the file where the key pair
will be stored and define a password for key protection.
Now, you need to install your public key in the SSH
server, in order the
server can use it to authenticate you when starting SSH
sessions. For
that purpose, use the following command, where ~/.ssh/id_rsa.pub
is the
default filename used to store client keys (replace it with the name of
the file where you stored your key pair, in case you haven’t used the
default file name), and username
and server
are the name of the
account and the name of the computer where you want to install your
public key:
vm:~$ ssh-copy-id -i ~/.ssh/id_rsa.pub <username>@<server>
This command stores your public key in the ~/.ssh/authorizedkeys
file
in the user account’s home folder on the server computer. This
public key will be used to verify the user’s possession of the
corresponding private key whenever the user starts an SSH
session with
the server.
Start a new SSH
session with the SSH
server (in the Ubuntu container) using the client.
Notice that your public key is used for your authentication, and you are
not requested to introduce your password.
Compare public key authentication with password authentication when
starting an SSH
session and analyze the advantages and disadvantages
from one in relation to the other.
Public key authentication on SSH
is substantially different from the
authentication in SSL protocol. Explain the differences.
Do you think it is adequate to use the SSH
public key authentication to
authenticate Web servers in the Internet, for example to authenticate
Google or Facebook servers? Explain.
User authentication with one-time passwords
Situations may exist where asymmetric key pairs might not be the most
adequate mechanism to authenticate users when establishing SSH
sessions.
For example, one such situation may occur when the user uses a shared
computer and fears that its key pair can be compromised. In such
situations, one-time passwords may be the most adequate mechanism for
user authentication when establishing an SSH
session with a remote
server, since one-time passwords can only be used one single time (they
are not reusable), and therefore no security mechanisms are needed to
secure its use.
Server configuration
To use one-time passwords in SSH, you need to install OTPW
in the
server. Use the following command:
root@server:/# apt install libpam-otpw otpw-bin
Then you need to configure PAM (Plugable authentication Module) module
of SSH. For that, you must edit the /etc/pam.d/sshd
configuration file
(using nano
, for example). You must disable password
authentication, by commenting the following line (by placing a #
character in the beginning of the line):
#@include common-auth
Then, you must add the following two lines to enable user authentication using one-time passwords:
auth required pam_otpw.so
session optional pam_otpw.so
Then you must configure the SSH
server to accept one-time passwords. For
that, you must edit the /etc/ssh/sshd_config
file to enable the
following three parameters (check that none of these parameters is
duplicated, to avoid server failures when starting):
ChallengeResponseAuthentication yes
You also need to disable password authentication. However, we will allow
public key authentication to prevent the possibility of all one-time
passwords were used. For that, edit again the /etc/ssh/sshd_config
file and check the following two parameters have the indicated values:
PubkeyAuthentication yes
PasswordAuthentication no
SSH server configuration is completed, and must be restarted.
One-time passwords generation
One-time passwords for user authentication must be generated and given
to the user, in order they can use to authenticate when establishing an
SSH session. To generate the user one-time passwords, the user must be
logged in the SSH
server (not has root) and must execute the following
command, where fname.txt
is a name for the file to store the generated
one-time passwords. When executing the command, the user will be
requested to introduce a prefix for the one-time passwords, that the
user must memorize because it will be requested, together with the
one-time password, when authenticating.
sio@server:~$ otpw-gen > fname.txt
Look at the contents of the file with the generated one-time passwords. Note that the one-time passwords are numbered and that each occupies two columns to make them easy to read. When the user is requested to introduce a one-time password for authentication, they must concatenate the text in the two columns, i.e., the blank space between the two columns must not be introduced.
The user must print the file to bring the one-time passwords with her,
in order they can use them whenever they establish a new SSH
session
with the server.
Using one-time passwords
The establishment of a new SSH
session is done with the same command as
indicated elsewhere above in this guide, but now, the server will
request one of the one-time passwords the user generated. The requested
one-time password is identified by its number in a format similar to:
Password 123:
The full one-time password to be introduced is composed by two parts:
the prefix (defined by the user when they generated the one-time
passwords) and the identified one-time password that the user must read
from the file containing the one-passwords they generated. The user must
concatenate the prefix with the requested one-time password. If the
correct password is introduced, SSH
session is established.
2FA with Google Authenticator (TOTP/HOTP)
We can also complement the login with 2-factor authentication (2FA). This is simple to achieve with SSH and a complementary time based one time passwords, such as TOTP or HOTP. This can be done with Google Authenticator for example.
The setup is similar to what was done before, by setting up SSH
and PAM.
So, please ensure the following:
- Comment/remove the information from
OTPW
(previous exercise) - Install Google Authenticator from the Play/App Store
Now, let’s jump into setting up the server and the account.
Server Authentication
root@server:/# apt install libpam-google-authenticator
With the user you want to setup
root@server:/# su sio
sio@server:~$ google-authenticator # go through the setup process
Scan the information with Google Authenticator, and lets enable it on SSHD.
Then you must configure the SSH
server to accept one-time passwords. For
that, you must edit the /etc/ssh/sshd_config
file to enable the
following three parameters (check that none of these parameters is
duplicated, to avoid server failures when starting):
UsePAM yes
ChallengeResponseAuthentication yes
Then you need to configure PAM (Plugable authentication Module) module
of SSH
. For that, you must edit the /etc/pam.d/sshd
configuration file
#@include common-auth
auth required pam_google_authenticator.so
Then just restart SSH
, and it should be working.