How can I enable (and require) n-factor authentication 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 address2.) a USB key3.) a pin texted to the administrator using a tool like twilioetc.) ...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 adminaccount@secureserver.com2.) Replace the login password prompt with a series of prompts to          get the pins/credentials from2.) Possibly interact with a client program to get the usb signature3.) Send the credentials to the java program, with time stamp to ensure          login was done within a minute after java sent the pins4.) Compare the Boolean returned by the java program with the CentOS password check5.) Either authenticate or reject the userMy guess as to a first draft of a shell script might be:  #!/bin/bashUSER_NAME=#How do I populate this?TEXT_PIN=shuf -i 1-10000 -n 1EMAIL_PIN=shuf -i 1-10000 -n 1CLASSPATH=/path/to/classesjava -cp $CLASSPATH my.package.SendPinsClass TEXT_PIN EMAIL_PINif [ $? -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. fiecho -n Enter Pin number from cell phone: read TEXT_PINecho -n Enter Pin number from email: read EMAIL_PINjava -cp $CLASSPATH my.package.AuthenticationClass USER_NAME TEXT_PIN EMAIL_PINif [ $? -eq 0 ]then    #DO NOT AUTHENTICATEelse    #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.  EDIT: 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 module as @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 module and any other aspects of the authentication process.  The answer needs to work on CentOS 7.SECOND EDIT: Per this link which was posted in a comment by @steve below, the contents of /etc/pam.d/sshd are by default as follows:  #%PAM-1.0auth       required     pam_sepermit.soauth       substack     password-authauth       include  postloginaccount    required     pam_nologin.soaccount    include  password-authpassword   include  password-auth# pam_selinux.so close should be the first session rulesession    required     pam_selinux.so closesession    required     pam_loginuid.so# pam_selinux.so open should only be followed by sessions to be executed in the user contextsession    required     pam_selinux.so open env_paramssession    optional     pam_keyinit.so force revokesession    include  password-authsession    include  postloginNote 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 updateapt-get install build-essential libpam0g-dev libcurl4-openssl-devgcc -fPIC -lcurl -c 2ndfactor.cld -lcurl -x --shared -o /lib/security/2ndfactor.so 2ndfactor.o@steve's link also suggests that we edit /etc/pam.d/sshd to add a reference to the 2ndfactor.o after @common-auth as follows:  # 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# Standard Un*x authentication.@include common-authauth       required     2ndfactor.so base_url=http://my.server.com/send_code.php code_size=5But, as you can see above, the /etc/pam.d/sshd that 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 = yes in /etc/ssd/sshd_config, but in CentOS 7 there is no /etc/ssd/ directory.Can someone please show how to adapt all this to work with CentOS 7 and Java?
n-factor SSH authentication for CentOS 7
shell;shell script;centos;ssh;authentication