How to set up One-Time-Password (OTP) authentication in ThinLinc

This is a tutorial that will go through the steps to enable two-factor authentication, OTP, and user password for logins to your ThinLinc system. ThinLinc uses the underlying Linux authentication mechanism PAM to authenticate a user. This means that this tutorial is general and also works with a Linux system without ThinLinc installed.

ThinLinc requires the OTP to be used twice due to how the ThinLinc client first connects and authenticates to the master server and then reconnects and authenticates to the agent server.

We used Fedora 36 and the Google Authenticator OTP PAM module for this tutorial.


1. Install google-authenticator:

$ sudo dnf install google-authenticator

2. Configure sshd to allow challenge response authentication.

Edit the file /etc/ssh/sshd_config and make sure that ChallengeResponseAuthentication is set to yes:

...
# Change to no to disable s/key passwords
ChallengeResponseAuthentication yes
#ChallengeResponseAuthentication no
...

Note that there might be configuration files inside /etc/ssh/sshd_config.d, for example /etc/ssh/sshd_config.d/50-redhat.conf that might have higher priority. Make sure ChallengeResponseAuthentication is set to yes in any such files as well.


3. Restart the ssh server daemon:

$ sudo systemctl restart sshd

4. Configure PAM to use the google-authenticator module.

Edit the file /etc/pam.d/thinlinc and add pam_google_authenticator.so to the auth step like this:

#%PAM-1.0
auth       required     pam_sepermit.so
auth       substack     password-auth
auth       required     pam_google_authenticator.so secret=/home/${USER}/.ssh/.google_authenticator nullok
auth       include      postlogin
account    required     pam_sepermit.so
account    required     pam_nologin.so
...

Be aware of the order; it is important. The google authenticator step should be placed after the password step. The secret path points to where the key file will be found since we will need to use a non-default location, see step 6. The option nullok allows login for a user which hasn’t enabled OPT yet (nullok is only recommended for testing purposes).


5. Install Google Authenticator on your user’s mobile device.

The app is available for both Android and iOS.


6. Enable OTP for your user.

The user needs to run the program google-authenticator himself on the server. We place the .google_authenticator file in the .ssh folder in order to allow Google Authenticator to work on SELinux-enabled systems.

$ ssh <ADMIN>@<SERVER>
$ su <USERNAME>
$ google-authenticator -s $HOME/.ssh/.google_authenticator

Open the Google Authenticator app on the user’s mobile device, scan the QR code shown in the terminal. This will import the master key for the user. Answer like this to the questions:

 Do you want authentication tokens to be time-based (y/n) y

 ...
 Enter code from app (-1 to skip): <ENTER CODE FROM APP>
 Code confirmed
 Your emergency scratch codes are:
 ...

 Do you want me to update your "/home/<USERNAME>/.ssh/.google_authenticator" file (y/n) y

 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) n

 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) y

 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

7. Set the proper SELinux context on the .google-authenticator file:

$ sudo restorecon -Rv /home/<USERNAME>/.ssh/

8. The server configuration is now complete.

otp

When attempting a login, you should now be prompted for a verification code in addition to a username and password. This code is found in the Google Authenticator app on the user’s mobile device.

4 Likes

Setting up OTP on Ubuntu 22.04 is quite similar to doing it on Fedora 36.

The steps that needed modification were steps 1, 2, and 4, and step 7 could be skipped entirely.

1. Install google-authenticator:
The package for google-authenticator is called libpam-google-authenticator Ubuntu.

$ sudo apt install libpam-google-authenticator

2. Configure sshd to allow challenge response authentication.
Still edit file /etc/ssh/sshd_config, but instead make sure KbdInteractiveAuthentication is set to yes:

...
# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
KbdInteractiveAuthentication yes
...

I did not have any files in /etc/ssh/sshd_config.d, so I did not have to change anything there.

4. Configure PAM to use the google-authenticator module.
Instead, edit file /etc/pam.d/common-auth and add the following lines to the end of the file.

auth required pam_google_authenticator.so secret=/home/${USER}/.ssh/.google_authenticator nullok
auth required pam_permit.so

7. Set the proper SELinux context on the .google-authenticator file:
SELinux is disabled by default, so this step can be skipped unless manually enabled.

3 Likes

Is there a way to have the OTP take effect the first login each day only, or after n minutes of idle time be required to re-enter the OTP?

No, not that I’m aware of. That sounds like a task for PAM to perform.

I have on all my debian VPS servers 2FA for SSH enabled, BUT also disabled Password option by commenting out ### @include common-auth
I also use different ssh ports and passkeys. So in short using passkeys, no password with 2FA and root is not allowed to login via ssh. → login in via ssh works.
But when I try to login to the server it just sits at the login and times out. “Check your Settings”.
I have set under options the ssh port and public key, but not getting the Authentication window.

Thanks

Hi @SuShe,

You could try disabling password authentication using the PasswordAuthentication option in the sshd config, rather than commenting out the entire PAM module. Let me know if this helps.

Hello aaron,
thanks for your reply.
If I disable the password authentication only in the sshd config file under /etc/ssh/sshd_config and leave the include common-auth (/etc/pam.d/sshd) I cannot use MFA for SSH.
So to use 2FA on SSH I have to comment ### include common-auth.

… so started to look around and saw that /etc/pam.d/thinlinc is a softlink to /etc/pam.d/sshd. I will now try to make a copy of the sshd as file thinlinc and modify that to the given settings in this article.
But I am running Debian 12 which has Apparmor how would I need to configure the /etc/pam.d/thinlinc file?

Will have to do more research for Debian

Apparmor shouldn’t make any difference, ThinLinc will configure AppArmour as required during installation. But let us know how you get on.