DEV Community

loading...

Configure SSH Server With Key-Based And Two Factor Authentication

Himadri Ganguly
A technophile by heart πŸ’— and profession πŸ’» with more than 10+ years of experience. Passionate about programming πŸ§‘β€πŸ’», Python 🐍, Kubernetes ☸️, DevSecOps βš™οΈ, IOT πŸ”Œ.
・Updated on ・10 min read

Alt Text

SSH or secure shell is an encrypted protocol used to connect to a server. I think all of us used it for connecting to servers but have you used two-factor authentication with SSH to make the process more secure? Most of you might have not done this till now. So in this tutorial we will go step by step to make that magic happen on Ubuntu and CentOs server (LOL).

Introduction

We all know what SSH is, but to be on the same page let's discuss a little bit about Secure Shell a.k.a SSH.

SSH or Secure Shell is a cryptographic network protocol for operating network services securely over an unsecured network. Typical applications include remote command-line, login, and remote command execution, but any network service can be secured with SSH. - From Wikipedia

So from the definition, we can understand that the main focus of this protocol is securely connecting to services over the untrusted network using a client-server architecture. When data is transferred it is secured and not in plain-text that transfers through the wire, so even if someone eavesdropping on your network they will not able to get the data. It came into existence to replace the Telnet protocol which transfers data in plain-text over the network. The standard port for SSH server is 22 and the client connects to the server on this port.

There are a lot of ways SSH user authentication can be done but in this article, we will see how to use the combination of SSH Key-Based And Two Factor Authentication. In SSH Key-Based Authentication two cryptographic keys are generated one public and one private key. The public key is transferred to the SSH server and the private key is retained by the client which is later used to prove the identity of the client. The private key should be kept securely so that it doesn't get exposed to any untrusted parties.

Create SSH Keys

The first step of configuring SSH Key-Based Authentication is creating the SSH keys. To do this we will go to our Linux terminal and use the tool ssh-keygen to generate the keys. In most Linux distros this tool is present by default but if not, please Google it to find the installation procedure.

Generating SSH key pair

$ ssh-keygen
Enter fullscreen mode Exit fullscreen mode

This will prompt for the name of the file which will be stored in the default location at ~/.ssh, within the home directory of the user. The default name of the public key is id_rsa.pub and the private key is id_rsa.

Generating public/private rsa key pair.
Enter file in which to save the key (/home/username/.ssh/id_rsa):
Enter fullscreen mode Exit fullscreen mode

Next, it will prompt for the passphrase if you want to encrypt your private key in your local system. This passphrase will protect the private key even the key is exposed to any malicious user, and it will prompt for the password to decrypt it when using this private key to connect to the SSH server. If you don't want to use a password just hit the ENTER key which will not encrypt the private key. For this demo, I am not using any passphrase so pressing ENTER and ENTER again to confirm no password.

Enter passphrase (empty for no passphrase):
Enter fullscreen mode Exit fullscreen mode

After this, it will generate the Keys

Generating public/private rsa key pair.
Enter file in which to save the key (/home/username/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/username/.ssh/id_rsa
Your public key has been saved in /home/username/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:Vv6kq8aYMxH4f+0zP7zltoCDaoE14uHqhtOoVqcvOKE username@hostname
The key's randomart image is:
+---[RSA 3072]----+
|                 |
|                 |
|     .    .      |
|    .o.o o       |
|    o.=.S . .    |
| . . =oo  .+.    |
|. B +  *...+.o  .|
|EB *  =.= ..= +o.|
|+ =.o..=.o...+o=o|
+----[SHA256]-----+
Enter fullscreen mode Exit fullscreen mode

If you now list the directory ~/.ssh you can see that two files are created

$ ls ~/.ssh
id_rsa  id_rsa.pub
Enter fullscreen mode Exit fullscreen mode

Transfer Public Key To SSH Server

Transferring the public key to the SSH server can be done in two ways we will see both the methods. I have created two virtual servers -

  1. Ubuntu Server 20.04 - 192.168.99.105
  2. CentOs 8 - 192.168.99.104

Manually Copy SSH Public Key To Server

We will copy the SSH public key manually to the Ubuntu server.

We will cat the content of the public key

$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDL9LHP61fH98RJ0hAhAdqTTd6dQPJ0H07vHrMzsa8S/bxxRgfd850+aE8mpHcuRnf1I+UMCKpkLZvhUJRBKXW9SmHu6skhXw0mY+TsLZFM3MjziX6vr+el4gbPZ6XROklGbY6BH3axmGz4uaaO/3vKYDGCdOSodm3azQ7ewPgJtOFNGV3BEsTuQh/Q2rmoO/a31FArntvQrljuuYt3Fd9jHTdjd82TjRUoqPR4MugJiB2I8iiVL4kY/VjZrkYiG47rtzOYBNohsQm1knI7Sg0KggR2s27hpvZvQl7guVLAGaF5Rif8vQPQkXIKUdOR46pIjvNAdt2y3HwNd36Uskr3jFmtd/Nf8mAweooKMdP9eNLX9GZX4h49jLJJXL6d3GmnnmUAA3LFauL5BObAKdftILWzGe3KrBroNtTED5nRAWYl8+EVLTDN7sWVE8UhqxH1gEx4gpbQ1dFmqlb9fcgg9349vlzG9bi/lhkIEFgrUUObh7SLJwAjXPEVGqgooHE= username@hostname
Enter fullscreen mode Exit fullscreen mode

Copy the whole content and login to the server i.e in this case Ubuntu server and paste the content in this file ~/.ssh/authorized_keys

username@ubuntu-test-server:~$ mkdir ~/.ssh
username@ubuntu-test-server:~$ vi ~/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

Now go to your local console and check the connection to the server using SSH Key-Based Authentication.

$ ssh username@192.168.99.105
Enter fullscreen mode Exit fullscreen mode

If everything works as expected will be able to log into the remote Ubuntu server console.

Copy SSH Public Key Using SSH Copy Id

Another way we can copy the SSH public key to the server is by using the tool called ssh-copy-id which is by default included with the SSH package. But for this to work you should be able to login to the server using SSH username and password.

We will use this method to copy the key to the CentOs server.

$ ssh-copy-id username@192.168.99.104
Enter fullscreen mode Exit fullscreen mode

If this is the first time you are connecting to the server computer will ask you to confirm the identity of the server.

/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/username/.ssh/id_rsa.pub"
The authenticity of host '192.168.99.104 (192.168.99.104)' can't be established.
ECDSA key fingerprint is SHA256:ZLd3hAk7HENCuKG+T6yEEIKQdSZefSNICP6cmjv/O1E.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
Enter fullscreen mode Exit fullscreen mode

Type yes and hit ENTER to continue. Now the public key will be copied to the server. It will ask for the SSH password for the particular user, to do the operation.

/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
username@192.168.99.104's password:

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'username@192.168.99.104'"
and check to make sure that only the key(s) you wanted were added.
Enter fullscreen mode Exit fullscreen mode

If you go to the CentOs server and cat the file ~/.ssh/authorized_keys you will get the exact content of your local id_rsa.pub file.

[username@centos-test-server ~]$ cat ~/.ssh/authorized_keys
Enter fullscreen mode Exit fullscreen mode

Now from your local console, check the connection to the server using SSH Key-Based Authentication.

$ ssh username@192.168.99.104
Enter fullscreen mode Exit fullscreen mode

If we have done above steps correctly we will be able to get the remote shell of the CentOs server.

Disable SSH Password-Based Authentication

As we are able to SSH both the servers using Key-Based Authentication, so now we can disable the Password-Based Authentication in both the SSH server.

In order to disable password-based authentication in SSH you have to edit the file /etc/ssh/sshd_config in sudo mode and change the line PasswordAuthentication from yes to no. The process is the same for both the server.

[username@centos-test-server ~]$ sudo vi /etc/ssh/sshd_config
Enter fullscreen mode Exit fullscreen mode

Edit line

# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication no # Change this line from yes to no
#PermitEmptyPasswords no
Enter fullscreen mode Exit fullscreen mode

Save the file and exit. Next restart SSH daemon.

[username@centos-test-server ~]$ sudo systemctl restart sshd
Enter fullscreen mode Exit fullscreen mode

Enable Two Factor Authentication In SSH

To use this feature we will be using two tools

  1. Google Authenticator PAM module

  2. Authy

If you want to know what is Pluggable Authentication Module a.k.a PAM you can check it out at

  1. https://www.redhat.com/sysadmin/pluggable-authentication-modules-pam
  2. http://www.opengroup.org/rfc/rfc86.0.html

Let's setup each server one by one.

Ubuntu Server

First, we have to install the Google Authenticator PAM module on the server.

$ sudo apt update && sudo apt-get install libpam-google-authenticator -y
Enter fullscreen mode Exit fullscreen mode

Next, we have to instruct PAM to use the Google Authenticator module with SSH. To do so we have to edit the PAM file sshd and add the line auth required pam_google_authenticator.so nullok at the end of the file, also comment the line @include common-auth in the file.

username@ubuntu-test-server:~$ sudo vi /etc/pam.d/sshd
Enter fullscreen mode Exit fullscreen mode

Add the line

# Standard Un*x password updating.
@include common-password
auth required pam_google_authenticator.so nullok # Add this line

Enter fullscreen mode Exit fullscreen mode

The nullok at the end of the line indicates that the OTP is optional while login in using SSH if OTP is not set up for that user. After you tested everything you can remove the nullok from the line to make 2FA mandatory.

Edit line

# Standard Un*x authentication.
#@include common-auth # Comment out this line
Enter fullscreen mode Exit fullscreen mode

Next, we have to instruct SSH service to use this 2FA, to do so we have to edit the file /etc/ssh/sshd_config and change the line ChallengeResponseAuthentication from no to yes.

username@ubuntu-test-server:~$ sudo vi /etc/ssh/sshd_config
Enter fullscreen mode Exit fullscreen mode

Edit line

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes # Change the line from no to yes
Enter fullscreen mode Exit fullscreen mode

Add another line

AuthenticationMethods publickey,keyboard-interactive # Add this line
Enter fullscreen mode Exit fullscreen mode

Restart SSH service

username@ubuntu-test-server:~$ sudo systemctl restart sshd
Enter fullscreen mode Exit fullscreen mode

Next, we will generate the time-based token using Google Authenticator and add it to our Authy App.

username@ubuntu-test-server:~$ google-authenticator
Enter fullscreen mode Exit fullscreen mode

It will ask to confirm if you want to generate time-based authentication token. Type y and hit ENTER.

Do you want authentication tokens to be time-based (y/n) y
Enter fullscreen mode Exit fullscreen mode

It will generate the QR code which you can scan in Authy App.

  1. Open Authy App
  2. Click on the 3-dots on the top right corner
  3. Click on Add Account
  4. Click on Scan QR Code button and scan the code

Next, it will ask to update the Google Authenticator file in your home directory. You must do this otherwise this 2FA will not work.

Do you want me to update your "/home/username/.google_authenticator" file? (y/n) y
Enter fullscreen mode Exit fullscreen mode

It will ask if the token is used only once in every 30 seconds. This will help to mitigate the man-in-the-middle attack by destroying the key once used.

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y
Enter fullscreen mode Exit fullscreen mode

It will ask if you want to change the time skew. By default, it will generate 3 valid code in a 1:50 minutes rolling window before it time out. Unless there is an issue we should stick to the default.

By default, a new token is generated every 30 seconds by the mobile app.
In order to compensate for possible time-skew between the client and the server,
we allow an extra token before and after the current time. This allows for a
time skew of up to 30 seconds between authentication server and client. If you
experience problems with poor time synchronization, you can increase the window
from its default size of 3 permitted codes (one previous code, the current
code, the next code) to 17 permitted codes (the 8 previous codes, the current
code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
between client and server.
Do you want to do so? (y/n) n
Enter fullscreen mode Exit fullscreen mode

Now there is another beautiful feature of rate-limiting, which will block a remote user after 3 unsuccessful failed attempts.

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting? (y/n) y
Enter fullscreen mode Exit fullscreen mode

We are done with the setup of our Ubuntu server with both Key-Based and 2FA Authentication.

Let's test if it is working or not. So exit from the Ubuntu server and run the ssh command from local terminal.

$ ssh username@192.168.99.105
Enter fullscreen mode Exit fullscreen mode

Now it will prompt for verification code.

Verification code:
Enter fullscreen mode Exit fullscreen mode

After providing the 2FA authentication code from the Authy App we logged into the Ubuntu server shell.

CentOs Server

To install Google Authenticator PAM in CentOs server we have to install the epel repository first.

[username@centos-test-server ~]$ sudo yum install epel-release -y
Enter fullscreen mode Exit fullscreen mode

Now install Google Authenticator PAM and qrencode (which is used to display the QR code).

[username@centos-test-server ~]$ sudo yum install google-authenticator -y && sudo yum install qrencode -y
Enter fullscreen mode Exit fullscreen mode

Next, we have to instruct PAM to use Google Authenticator module before login into SSH.

[username@centos-test-server ~]$ sudo vi /etc/pam.d/sshd
Enter fullscreen mode Exit fullscreen mode

Add the line to the end of the file

session    include      postlogin
auth required pam_google_authenticator.so nullok secret=/home/${USER}/.ssh/.google_authenticator
Enter fullscreen mode Exit fullscreen mode

The line is the same as we have done previously only difference is the part secret=/home/${USER}/.ssh/.google_authenticator which specified the location of the .google_authenticator config file which have to be moved from ~/.google_authenticator to ~/.ssh/.google_authenticator location due to SELinux security context, otherwise, it will not work.

Comment out the line

#auth       substack     password-auth # Comment this line
auth       include      postlogin
Enter fullscreen mode Exit fullscreen mode

Before moving forward we have to move the file .google_authenticator to the ~/.ssh/ folder

[username@centos-test-server ~]$ mv ~/.google_authenticator ~/.ssh/
Enter fullscreen mode Exit fullscreen mode

Next, we have to instruct SSH service to use this 2FA, to do so we have to edit the file /etc/ssh/sshd_config and change the line ChallengeResponseAuthentication from no to yes.

[username@centos-test-server ~]$ sudo vi /etc/ssh/sshd_config
Enter fullscreen mode Exit fullscreen mode

Edit line

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes # Change this line from no to yes
Enter fullscreen mode Exit fullscreen mode

Add another line

AuthenticationMethods publickey,keyboard-interactive
Enter fullscreen mode Exit fullscreen mode

Restart SSH service

[username@centos-test-server ~]$ sudo systemctl restart sshd
Enter fullscreen mode Exit fullscreen mode

Generating a time-based token using the Google Authenticator and adding it to Authy App is the same as above.

After completing all the steps if everything works fine we can log in to the CentOs server using Key-Based and Two Factor Authentication.

Conclusion

Hope you like this article. In this article, I tried to explain as much as possible and in a simple step by step method, how we can create SSH key based authentication and implement 2FA authentication using Google Authenticator.

Thank you.

Discussion (0)