There are multiple ways to make an SSH connection more secure and one of them is adding “Two Factor Authentication (2FA)” to the system and integrate it with ssh connectivity.
Here the steps that could help with adding this feature to the system and its integration.
This feature could be added to a different distribution of Linux and here I’m using Ubuntu Server 17.10 for demonstration.
Update system
First of all, update the system repository and catalog by running below command.
sudo apt-get update && sudo apt-get upgrade -y && sudo apt autoremove
Install 2FA application
If the ssh server is not yet installed on the system, we can add it as well with below code.
sudo apt-get install openssh-server -y
As for 2FA server-side application, we can use “Google Authenticator” and this application can be easily added to the OS by running below command.
sudo apt-get install libpam-google-authenticator -y
The second part of the installation is for mobile devices; in this step, we need to install “Google Authenticator” client on the mobile devices; below I’ve placed the link for both Android and Apple devices.
Configuration
This part includes four parts, 1) modification of two files, 2) configuration of “Google Authenticator” on the server, 3) configuration of “Google Authenticator” on the mobile device and finally restarting the ssh service to apply all the configuration.
File modification
we need to change “ChallengeResponseAuthentication no” line with “ChallengeResponseAuthentication yes” in “/etc/ssh/sshd_config” file.
Also, we need to add “auth required pam_google_authenticator.so” line to the “/etc/pam.d/sshd” file.
Google Authenticator – Server
Now that we’ve made required changes to accept the 2FA, it’s time to configure the “Google Authenticator” server side by running “google-authenticator” from the user we want to enable this feature for who also must have ssh permission.
In response to “Do you want authentication tokens to be time-based (y/n)” select “y” for yes.
After answering the first question, the application would show “one URL“, “one QR CODE“, “New secret key“, “Verification code” and “Emergency scratch codes” which are very important.
“Do you want me to update your “/home/sohrab/.google_authenticator” file? (y/n)” select “y” for yes.
“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)” select “y” for yes.
“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 the 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)”
In this part, the answer is based on the quality of connectivity, if you’ve experienced a problem with providing the second factor on time, you can select “y” for yes but I personally prefer “n” for no to make providing code limiter and using 2fa more secure.
And for the last question, “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)” we can select “y” for yes to limit the number of possible attempts in the 30s to make service even more secure.
Google Authenticator – Mobile
In this step, we would use information which application has shown after answering the first question, “URL” or “Secret key”.
Now it’s time to open “Google Authenticator” app that we’ve installed before on mobile device and select “add” button to add a new record for this server in the app.
If we select “Scan a barcode” then we can use the shown URL to get same QR code and scan it with the app.
If we select “Enter a provided key” then we can simply type the “Secret key” and follow the rest.
Restarting ssh service
Both bellow commands are same and both of them would do the same task which is restarting the “ssh service” to apply the latest configuration and we need to run only one.
sudo service ssh restart sudo systemctl restart ssh
Important Note
If it’s remote server and you don’t have direct/console access to the server, it’s very important to keep session used to do all configuration open and use a new session to test the functionality of newly added 2FA feature.
In case you’ve connected to the server using ssh, after finishing all above steps including the restarting the ssh service, you’d be still connected and it’s important to keep this session open to prevent any loss of connectivity if configuration didn’t work as expected.
Debugging
In order to get a better understanding of what happening behind the scene or in case of facing any issue during an attempt to login, we can view the “/var/log/auth.log” file by below command.
tail -f -n 15 /var/log/auth.log
Normally, logging is enabled by default for ssh but if we want to get more detail on the process, we can validate if below line has been enabled in “/etc/ssh/sshd_config” file. we might find “LogLevel INFO” but we can change “INFO” with “VERBOSE” to gather more information and enable it by removing “#” from beginning the line (if exist).
# Logging LogLevel VERBOSE
Below you can check the difference between INFO and VERBOSE and decide which one would better serve your needs.
sohrab@dn2:~$ tail -f -n 11 /var/log/auth.log Apr 15 17:56:33 dn2 sudo: pam_unix(sudo:session): session opened for user root by sohrab(uid=0) Apr 15 17:56:33 dn2 sshd[22873]: Received signal 15; terminating. Apr 15 17:56:33 dn2 sshd[22905]: Server listening on 0.0.0.0 port 22. Apr 15 17:56:33 dn2 sshd[22905]: Server listening on :: port 22. Apr 15 17:56:33 dn2 sudo: pam_unix(sudo:session): session closed for user root Apr 15 18:17:01 dn2 CRON[22909]: pam_unix(cron:session): session opened for user root by (uid=0) Apr 15 18:17:01 dn2 CRON[22909]: pam_unix(cron:session): session closed for user root Apr 15 18:37:32 dn2 sshd(pam_google_authenticator)[22936]: Accepted google_authenticator for sohrab Apr 15 18:37:32 dn2 sshd[22930]: Accepted keyboard-interactive/pam for sohrab from 172.16.1.1 port 64923 ssh2 Apr 15 18:37:32 dn2 sshd[22930]: pam_unix(sshd:session): session opened for user sohrab by (uid=0) Apr 15 18:37:32 dn2 systemd-logind[772]: New session 7 of user sohrab.
sohrab@dn2:~$ tail -f -n 11 /var/log/auth.log Apr 15 18:39:22 dn2 sshd[23090]: Connection from 172.16.1.1 port 64937 on 172.16.1.22 port 22 Apr 15 18:39:29 dn2 sshd[23090]: Failed publickey for sohrab from 172.16.1.1 port 64937 ssh2: RSA SHA256:??????????kzY+qUR3TqA7SylteFsO5LGWZ55QHOWNQ Apr 15 18:39:29 dn2 sshd[23090]: Postponed keyboard-interactive for sohrab from 172.16.1.1 port 64937 ssh2 [preauth] Apr 15 18:39:32 dn2 sshd[23090]: Postponed keyboard-interactive/pam for sohrab from 172.16.1.1 port 64937 ssh2 [preauth] Apr 15 18:39:40 dn2 sshd(pam_google_authenticator)[23099]: Accepted google_authenticator for sohrab Apr 15 18:39:40 dn2 sshd[23090]: Postponed keyboard-interactive/pam for sohrab from 172.16.1.1 port 64937 ssh2 [preauth] Apr 15 18:39:40 dn2 sshd[23090]: Accepted keyboard-interactive/pam for sohrab from 172.16.1.1 port 64937 ssh2 Apr 15 18:39:40 dn2 sshd[23090]: pam_unix(sshd:session): session opened for user sohrab by (uid=0) Apr 15 18:39:40 dn2 systemd-logind[772]: New session 8 of user sohrab. Apr 15 18:39:41 dn2 sshd[23090]: User child is on pid 23164 Apr 15 18:39:41 dn2 sshd[23164]: Starting session: shell on pts/1 for sohrab from 172.16.1.1 port 64937 id 0
Mobile device Using
Now each time we want to connect to the server, we need to provide username and password (same as before) and one newly generated code for 2FA.
Hope you find it useful.