How can I enable (and require)
n-factorauthentication for all SSH connections to a CentOS 7 server?
I have read about Google Authenticator, which provides two factor authentication. But that is limited to a pin provided by an app that you have to install in each user’s mobile device.
How can I add variable numbers of authentication factors? Some examples of additional factors might include:
1.) a pin code emailed to the administrator's work email address 2.) a USB key 3.) a pin texted to the administrator using a tool like twilio etc.) ...
A Java program to send the pin emails and texts would be easy enough to build, and could be called by a shell script that replaces the default ssh authentication script, but I have never written a shell script before.
What would the shell code look like to accomplish this, assuming the Java is already written? And where would I place the shell script? The pseudo-code for the shell script might include the following steps:
1.) Trigger the Java program to send email, text, etc. with custom pins when administrator types ssh firstname.lastname@example.org 2.) Replace the login password prompt with a series of prompts to get the pins/credentials from 2.) Possibly interact with a client program to get the usb signature 3.) Send the credentials to the java program, with time stamp to ensure login was done within a minute after java sent the pins 4.) Compare the Boolean returned by the java program with the CentOS password check 5.) Either authenticate or reject the user
My guess as to a first draft of a shell script might be:
#!/bin/bash USER_NAME=#How do I populate this? TEXT_PIN=shuf -i 1-10000 -n 1 EMAIL_PIN=shuf -i 1-10000 -n 1 CLASSPATH=/path/to/classes java -cp $CLASSPATH my.package.SendPinsClass TEXT_PIN EMAIL_PIN if [ $? -eq 0 ] then echo -n "We were not able to send authentication credentials. Please log the current time and report this to the chief administrator. " else echo -n "Pin numbers have been sent to your email and phone number. Please check your email and text messages now before continuing the authentication process. " fi echo -n "Enter Pin number from cell phone: " read TEXT_PIN echo -n "Enter Pin number from email: " read EMAIL_PIN java -cp $CLASSPATH my.package.AuthenticationClass USER_NAME TEXT_PIN EMAIL_PIN if [ $? -eq 0 ] then #DO NOT AUTHENTICATE else #LOG THIS USER IN! fi #HOW DO WE CHECK THE USER'S CentOS 7 PASSWORD? #THIS SCRIPT MUST INCLUDE A PASSWORD CHECK IN AUTHENTICATION PROCESS.
What else goes in the shell script? And how to I put it in place?
The specifications for an answer are given above. However, comments suggesting easier tools instead of rolling one’s own are also welcome as comments.
Based on my ongoing research, I just added more explicit detail to the rough draft of the shell script above to document the steps that the script must take in order to authenticate the user. Key steps can be assumed to successfully be completed by two “black box” java programs that are called by the shell script. The major remaining questions that must be answered before this can be completed include:
1.) How does the script receive and populate the value for the USER_NAME variable? 2.) How does the script check the user's CentOS 7 OS password, and how should the password be integrated into the script's authentication process? Note the security of the password needs to be protected. 3.) How does the script process the approval or rejection of the authentication request with the CentOS 7 operating system? 4.) How is the working shell script placed in the authentication process to replace the default authentication script?
Note: If implementation of the script in CentOS 7 involves bundling the script as a
PAM moduleas @steve suggests in his comment, then the accepted answer needs to give explicit instructions not only for altering the shell script, but also for integrating the shell script into the
PAM moduleand any other aspects of the authentication process. The answer needs to work on CentOS 7.
Per this link which was posted in a comment by @steve below, the contents of
/etc/pam.d/sshdare by default as follows:
#%PAM-1.0 auth required pam_sepermit.so auth substack password-auth auth include postlogin account required pam_nologin.so account include password-auth password include password-auth # pam_selinux.so close should be the first session rule session required pam_selinux.so close session required pam_loginuid.so # pam_selinux.so open should only be followed by sessions to be executed in the user context session required pam_selinux.so open env_params session optional pam_keyinit.so force revoke session include password-auth session include postlogin
Note that, in CentOS 7,
/lib/security/does not exist, and is thus not the location of PAM modules, as in @steve’s link, which is for Debian. Thus, without the PAM modules, we cannot store the shell script in my OP as
/lib/security/2ndfactor.so. The tutorial in @steve’s link suggests the following code to assign the script to
/lib/security/2ndfactor.so. How do we change the following code?
apt-get update apt-get install build-essential libpam0g-dev libcurl4-openssl-dev gcc -fPIC -lcurl -c 2ndfactor.c ld -lcurl -x --shared -o /lib/security/2ndfactor.so 2ndfactor.o
@steve’s link also suggests that we edit
/etc/pam.d/sshdto add a reference to the
# PAM configuration for the Secure Shell service # Read environment variables from /etc/environment and # /etc/security/pam_env.conf. auth required pam_env.so #  # 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 # Standard Un*x authentication. @include common-auth auth required 2ndfactor.so base_url=http://my.server.com/send_code.php code_size=5
But, as you can see above, the
/etc/pam.d/sshdthat comes default in CentOS 7 does not contain
@include common-auth. Also, @steve’s link uses a web url for a php script, but I am trying to call a java program from a shell script. There must be a way to do this without exposing a web url.
Lastly, @steve’s link says to set
ChallengeResponseAuthentication = yesin
/etc/ssd/sshd_config, but in CentOS 7 there is no
Can someone please show how to adapt all this to work with CentOS 7 and Java?
The standard *nix authentication stack uses PAM. You’ll want to look into building a module for PAM:
Typically multi-factor auth vendors will provide one. For example, here’s a how-to guide for implementing google authenticator as a second factor for SSH login:
You can configure PAM to support a variety of different factors as sufficient or mandatory.
I’m not trying to be difficult – I’m trying to redirect your research. From your second edit, you ask for help adapting a set of lines which includes:
gcc -fPIC -lcurl -c 2ndfactor.c
This is the giveaway. You cannot compile a shell script. PAM modules are written in C and compiled (in this example) with gcc. You might be able to call a java application from inside a PAM module, but that’s outside the scope of this site and outside the scope of my knowledge.
- The PAM modules for a modern CentOS version will be in /lib64/security/, done in case you need 32 and 64 bit libraries
Lastly, @steve's link says to set ChallengeResponseAuthentication = yes in /etc/ssd/sshd_config, but in CentOS 7 there is no /etc/ssd/ directory.This is probably just a typo and should be /etc/sshd
As a sys admin, I discourage the use of “weird” configurations. PAM is the well-known and accepted method of authentication, and a lot of applications besides sshd can and will use it.
However, if you are only concerned about implementing 2FA for sshd in tty sessions, you could look at using the ForceCommand option in your sshd configuration:
You should note that it is relatively easy for an authenticated user to later circumvent future attempts to enforce 2FA via ForceCommand, so this is not really a good solution: