6.3.1. Two-Factor SSH Authentication with LinOTP#
1 Introduction#
Adding OneTimePasswords as additional authentication layer for SSH clients does significantly improve security for SSH based login procedures. This article describes how to implement LinOTP in your SSH environment by integrating the usage of OTPs into the PAM stack of a SSH server.
For that two methods are available:
authentication through the LinOTP web API done by:
PAM C modul [recommended]: pam_linotp (provided by LinOTP)
PAM python modul: pam_linotp.py (provided by LinOTP)
authentication by a RADIUS server connected to LinOTP done by:
libpam-radius-auth (natively available in Debian jessie/stretch)
What this guide does not cover:
setup of
LinOTP
setup of
RADIUS
and connecting it toLinOTP
please refer to: http://linotp.org/howtos/howto-radius.htmlbasic setup of SSH, neither client nor server
2 Prerequisites#
You will need:
SSH server
SSH client
LinOTP (reachable either from the SSH server or the RADIUS server depending of you choice of authentication)
optional: RADIUS server (if this is the authentication method of your choice)
This guide is written for Debian jessie, albeit the how-to should be applicable to most distributions.
Package versions
This how-to was tested with the following versions of packages (retrieved from Debian and LinOTP repositories):
linotp: 2.10.0.4-1
libpam-linotp: 2.9-1
libpam-radius-auth: 1.3.16-4.4
libpam-python: 1.0.4-1
openssh-server: 1:6.7p1-5+deb8u4
openssh-client: 7.4p1-10+deb9u3
pam_linotp.py: 2.7
Don’t worry - you can use these packages in other version (older as well as newer, but of course newer is recommended) and have a successfully running setup. But if you encounter obscure problems, please check the changelogs, whether they contain any related modifications.
3 Direct authentication via LinOTP web interface (no RADIUS server needed)#
As it comes in the world of Open source, there are two implementations of the necessary PAM module:
pam_linotp (written in C, developed and updated by netgo GmbH)
libpam-python together with pam_py_linotp (a script-based version, developed and updated by netgo GmbH)
Both PAM modules connect directly to the https-interface of your LinOTP instance. So no trouble in setting up a RADIUS server :)
Which one you like more is up to you, we will show both setups for your SSH server.
3.1 pam_linotp#
This is the recommended method. Alternatively see next chapter for pam_linotp.py
.
If not yet done, add the linotp-Repository to your SSH server.
echo 'deb http://dist.linotp.org/debian/linotp2 jessie linotp' > /etc/apt/sources.list.d/linotp.list
Add the gpg-key of linotp to your apt-keyring:
apt-key adv --keyserver eu.pool.sks-keyservers.net --recv-keys 913DFF12F86258E5
And install
pam_linotp
(written in C):
apt-get update
apt-get install libpam-linotp
You have to create a symbolic link to the
libpam-linotp
module, otherwise PAM will be unable to locate it (here the example for a 64bit system)
ln -s /usr/lib/x86_64-linux-gnu/security/pam_linotp.so /lib/x86_64-linux-gnu/security/
Add a PAM configuration file for LinOTP, which can be used for SSH or any other loginprocedure of your choice:
/etc/pam.d/common-linotp
:
auth [success=1 default=ignore] pam_linotp.so nosslhostnameverify \
nosslcertverify url=https://192.168.8.203/validate/simplecheck
auth requisite pam_deny.so
#The next line is required if common-auth is commented out in /etc/pam.d/sshd
#in order to have validation via pub-key + OTP without asking for the users password
auth required pam_permit.so
Substitute the IP with the one of your LinOTP server.
WARNING: If you have self-signed https-certificates you must set the ‘nosslhostnameverify’ and ‘nosslcertverify’ plugin options unless you configure the certificates to be trusted [1].
The pam_linotp
plugin knows a number of parameters:
- url
the IP of your LinOTP machine
- realm
sets the realm which should be used to get the authentication (i.e. ‘realm=management’), if not set it defaults to the standardrealm of LinOTP
- debug
if you have trouble, try to set this - you will get a lot more messages in the logfiles (i.e. in ‘/var/log/auth’) - be careful: the PIN+OTP will be shown
- nosslhostnameverify
ignore the mismatch of real hostname and the hostname in the certificate
- nosslcertverify
necessary for self-signed certificates unless configured as trusted [1]
Include the new PAM file in the PAM login configuration for SSH; with
pam_linotp
you can put it before@include common-auth
or after, depending on whether the OTP should be asked first or the Password of the user
/etc/pam.d/sshd
# PAM configuration for the Secure Shell service # Read environment variables from /etc/environment and # /etc/security/pam_env.conf. auth required pam_env.so # [1] # In Debian 4.0 (etch), locale-related environment variables were moved to # /etc/default/locale, so read that as well. auth required pam_env.so envfile=/etc/default/locale # Include LinOTP authentication @include common-linotp # Standard Un*x authentication. # Deactivate if public key + OTP only login should be allowed # Mind to have the pam_permit.so line in common-linotp #@include common-auth # Disallow non-root logins when /etc/nologin exists. account required pam_nologin.so # Uncomment and edit /etc/security/access.conf if you need to set complex # access limits that are hard to express in sshd_config. # account required pam_access.so # Standard Un*x authorization. @include common-account # Standard Un*x session setup and teardown. @include common-session # Print the message of the day upon successful login. # This includes a dynamically generated part from /run/motd.dynamic # and a static (admin-editable) part from /etc/motd. session optional pam_motd.so motd=/run/motd.dynamic noupdate session optional pam_motd.so # [1] # Print the status of the user's mailbox upon successful login. session optional pam_mail.so standard noenv # [1] # Set up user limits from /etc/security/limits.conf. session required pam_limits.so # Set up SELinux capabilities (need modified pam) # session required pam_selinux.so multiple # Standard Un*x password updating. @include common-password
3.2 pam_py_linotp#
Disclaimer: At the current state, pam_py_linotp
does not interact
correctly with the Debian SSH-PAM-stack. You can try your luck, but you
have been warned…
The python-based PAM plugin requires the package
libpam-python
:
apt-get install libpam-python python
Then we need the PAM plugins itself. Here you have two options:
Download by hand from ‘https://pypi.python.org/pypi/pam_py_linotp/’:
download:
wget --no-check-certificate -P /tmp https://pypi.python.org/packages/source/p/pam_py_linotp/pam_py_linotp-2.7.tar.gz
extract:
tar -C /tmp -xzf /tmp/pam_py_linotp-2.7.tar.gz
install:
cp /tmp/pam_py_linotp-2.7/src/pam_linotp.py /lib/security/
or: install via pip
provide requirements:
apt-get install python-pip
install:
pip install pam_py_linotp
Whichever way was used for installing the plugin - now it needs to get activated.
Add a PAM configuration file for LinOTP, which can be used for SSH or any other loginprocedure of your choice:
/etc/pam.d/common-linotp
:
auth [success=1 default=ignore] pam_python.so\
/lib/security/pam_linotp.py nosslhostnameverify nosslcertverify\
url=https://192.168.8.203/validate/simplecheck
auth requisite pam_deny.so
Substitute the IP with the one of your LinOTP server.
WARNING: If you have self-signed https-certificates you must set the ‘nosslhostnameverify’ and ‘nosslcertverify’ plugin options unless you configure the certificates to be trusted [1].
The pam_linotp.py
plugin knows a number of parameters:
- url
the IP of your LinOTP machine
- realm
sets the realm which should be used to get the authentication (i.e. ‘realm=management’), if not set it defaults to the standardrealm of LinOTP
- debug
if you have trouble, try to set this - you will get a lot more messages in the logfiles (i.e. in ‘/var/log/auth’) - be careful: the PIN+OTP will be shown
- nosslhostnameverify
ignore the mismatch of real hostname and the hostname in the certificate
- nosslcertverify
necessary for self-signed certificates unless configured as trusted [1]
Include the new PAM file in the PAM login configuration for SSH:
/etc/pam.d/sshd
# PAM configuration for the Secure Shell service # Read environment variables from /etc/environment and # /etc/security/pam_env.conf. auth required pam_env.so # [1] # In Debian 4.0 (etch), locale-related environment variables were moved to # /etc/default/locale, so read that as well. auth required pam_env.so envfile=/etc/default/locale # Include LinOTP authentication @include common-linotp # Standard Un*x authentication. # Deactivate if public key + OTP only login should be allowed # Mind to have the pam_permit.so line in common-linotp #@include common-auth # Disallow non-root logins when /etc/nologin exists. account required pam_nologin.so # Uncomment and edit /etc/security/access.conf if you need to set complex # access limits that are hard to express in sshd_config. # account required pam_access.so # Standard Un*x authorization. @include common-account # Standard Un*x session setup and teardown. @include common-session # Print the message of the day upon successful login. # This includes a dynamically generated part from /run/motd.dynamic # and a static (admin-editable) part from /etc/motd. session optional pam_motd.so motd=/run/motd.dynamic noupdate session optional pam_motd.so # [1] # Print the status of the user's mailbox upon successful login. session optional pam_mail.so standard noenv # [1] # Set up user limits from /etc/security/limits.conf. session required pam_limits.so # Set up SELinux capabilities (need modified pam) # session required pam_selinux.so multiple # Standard Un*x password updating. @include common-password
4 Authentication via RADIUS#
RADIUS is widely used authentication protocol. If you have a RADIUS server running you can connect it to LinOTP and use its One Time Passwords as (additional) authentication layer for your SSH-clients (see our LinOTP+RADIUS documentation how such a setup is done: http://linotp.org/howtos/howto-radius.html).
Here we describe how to establish an authentication request from the SSH server to a RADIUS server in order to validate SSH clients.
We assume you have the following:
a SSH server
a SSH client
LinOTP
a RADIUS server connected to your LinOTP instance: http://linotp.org/howtos/howto-radius.html
4.1 Connect to RADIUS via PAM (libpam-radius-auth)#
The following must be done at the SSH server.
install the necessary PAM-RADIUS plugin:
apt-get install libpam-radius-auth
Adopt plugin configuration to your needs:
You will find the documentation and examples of the PAM module in
/usr/share/doc/libpam-radius-auth
. Have a look… And adopt the configuration
of the PAM plugin in /etc/pam_radius_auth.conf
according to your needs:
# RADIUSserver[:port] shared_secret_of_RADIUS_server timeout (s)
# you can provide more than one server line
192.168.8.203 SECRET 3
Add a PAM configuration file for RADIUS, which can be used for SSH or any other loginprocedure of your choice:
/etc/pam.d/common-linotp
:
auth [success=1 default=ignore] pam_radius_auth.so
auth requisite pam_deny.so
#The next line is required if common-auth is commented out in /etc/pam.d/sshd
#in order to have validation via pub-key + OTP without asking for the users password
auth required pam_permit.so
You can set the parameter debug for the plugin to make it verbose in case of trouble.
Include the new PAM file in the PAM login configuration for SSH - it is important to put it before
@include common-auth
, because the other way around (ask first password of the user and then the OTP) does unfortunately not work correctly:
/etc/pam.d/sshd
# PAM configuration for the Secure Shell service # Read environment variables from /etc/environment and # /etc/security/pam_env.conf. auth required pam_env.so # [1] # In Debian 4.0 (etch), locale-related environment variables were moved to # /etc/default/locale, so read that as well. auth required pam_env.so envfile=/etc/default/locale # Include LinOTP-RADIUS authentication @include common-linotp # Standard Un*x authentication. # Deactivate if public key + OTP only login should be allowed # Mind to have the pam_permit.so line in common-linotp #@include common-auth # Disallow non-root logins when /etc/nologin exists. account required pam_nologin.so # Uncomment and edit /etc/security/access.conf if you need to set complex # access limits that are hard to express in sshd_config. # account required pam_access.so # Standard Un*x authorization. @include common-account # Standard Un*x session setup and teardown. @include common-session # Print the message of the day upon successful login. # This includes a dynamically generated part from /run/motd.dynamic # and a static (admin-editable) part from /etc/motd. session optional pam_motd.so motd=/run/motd.dynamic noupdate session optional pam_motd.so # [1] # Print the status of the user's mailbox upon successful login. session optional pam_mail.so standard noenv # [1] # Set up user limits from /etc/security/limits.conf. session required pam_limits.so # Set up SELinux capabilities (need modified pam) # session required pam_selinux.so multiple # Standard Un*x password updating. @include common-password
5 Final Configuration of the SSH server#
Whichever PAM-Module you have chosen - you must as last step activate challenge
response functionality in you SSH server configuration. So open
/etc/ssh/sshd_config
and add:
ChallengeResponseAuthentication yes
# Optional if needed
# By default, users with publickey connect directly and users without
# publickey need OTP and password via PAM.
# The following directive enforces public key together with OTP for everyone.
AuthenticationMethods publickey,keyboard-interactive:pam
Reload the SSH server and start testing…
6 Test#
Connect with a SSH client and if PAM is working properly you should see something like this:
root@vpnclient:~# ssh paul@192.168.42.227
Your OTP:
Password:
If you use libpam-radius-auth
there is no distinction between password and
OTP and both questions are the same (so your user must know the correct
sequence…). pam_echo
could be used to inject messages before the
authentication modules in order to identify the factors.
root@vpnclient:~# ssh paul@192.168.42.227
Password:
Password:
If you encounter any problems:
activate
debug
feature of the PAM moduleread auth-logfile of the SSH server:
/var/log/auth.log
have a look at the LinOTP Audit Trail and/or the LinOTP Logfile
/var/log/linotp/linotp.log
Copy the certificate to the SSH server:
scp /etc/ssl/certs/lseappliance.pem IP_SSH_SERVER:/usr/share/ca-certificates/lseappliance.crt
Activate the new certificate on the SSH server:
dpkg-reconfigure ca-certificates