I figured it'd been too long since I last wrote, so here's a little gem I put together for your enjoyment.
Firstly, I have long been a fan of Multi-factor authentication (MFA) since it is RELATIVELY easy for users to
use while making password bypasses significantly harder. However, the benefits of MFA get really shallow when I'm
needing to execute a non-interactive command across several dozen systems using something like SSH. Sure, it could
be done...if I sat there mashing my phone or fob every few seconds. This got me thinking there MUST be a way to
white-list IDs in an MFA environment to allow them to run autonomous authentication like in this scenario, however
such documentation alluded me long enough for me to figure a workaround. Good thing too because it forced me to actually
learn something! Shout out to all the hacker heads bobbing out there. You know what I'm talking about.
Anyway, to avoid possible legal issues I'm going to not mention the MFA vendor I'm using. Ultimately it's pretty trivial information anyway as they were of no help other than providing documentation on how you SHOULD configure your systems. That's where my trouble started, so we'll begin there.
This MFA installer works by (among other things) loading it's own Pluggable Authentication Module (PAM) into the Linux OS, thereby allowing it to be triggered after the normal password authentication has succeeded. When a user logs in, certain PAMs get executed to determine whether or not the user name is authorized to login. Password validation is the primary piece of this, but MFA PAM will check to make sure you are who you say you are before allowing you in by either prompting for a temporary PIN or pushing a request to your mobile. Assuming you acknowledge the PIN/PUSH correctly, your login will proceed.
This waterfall effect of PAMs was very interesting to me, so I took some time just learning how that worked once I determined that reverse engineering the modules themselves was going to be...complicated. I began to see how the order of operations was very important. For example, in my target PAM, SSHD.so, I learned if I moved the call to the MFA PAM higher in the stack I could effectively bypass password authentication all together as long as I responded to my mobile device push within the appropriate time frame. Of course this would be bad, so don't do that! It is also important what keyword is associated with the event. There are a couple of possibilities, but I'll just focues on the ones I ran into below:
#%PAM-1.0 auth required pam_sepermit.so auth substack password-auth auth required pam_env.so auth required pam_deny.so auth include postlogin ...
The key word 'required' means if the line fails, the entire module will exit failed without invoking anything further lines down the stack. Normal logins work like this because they are designed to either fail or succeed. As stated above however, MFA adds a step. Take a look at the next example which includes a call to the MFA PAM:
#%PAM-1.0 auth required pam_sepermit.so auth substack password-auth auth required pam_env.so auth sufficient pam_mfa.so auth required pam_deny.so auth include postlogin ...
Here we see how a custom PAM (pam_mfa.so) can be inserted, however it's labeled as 'sufficient'. This works almost opposite of
'required' in that, if it succeeds, the entire process will exit successfully without invoking anything below. Failures on the
other hand merely fall through to the next line in the stack, which is the pam_deny.so. Examples could be a brute force attempt
to the SSH login or getting a push to your mobile that you don't respond to fast enough.
Now, let's take a look at my final example.
#%PAM-1.0 auth required pam_sepermit.so #auth substack password-auth auth required pam_env.so auth sufficient pam_succeed_if.so uid = 1666 auth sufficient pam_mfa.so auth required pam_deny.so auth include postlogin ...
Here we get to the REALLY fun part. UUIDs.
It turns out there a special PAMs that can be called which further modify the behavior of a login, like 'pam_succeed_if.so'. Essentially this is a logic statement that looks for particular UUIDs attempting to login. Assuming that a valide user UID hits, the authentication will drop out successfully BEFORE invoking the MFA PAM.
On the other hand, if a valid UUID is attempting to login that isn't white-listed here, the MFA PAM will be invoked like normal. A great reason for this kind of configuration is to allow trusted user accounts (read Service Accounts) that have limited access to run very particular tasks remotely and without interaction while STILL maintaining MFA for regular admin and user accounts.
Oh yeah, for those paying close attention, the commented out 'auth substrack password-auth' is because I'm using keys to authenticate rather than passwords for the '1666' UUID SSH connections...because hard coding passwords for remove Service Accounts to use for scripting makes my skin crawl.
So that's basically it; three examples to demonstrate the evolution of my SSH daemon using MFA AND white-listed UUIDs. Obviously this is probably the hard way of doing this, but, when you don't have support of clear documentation to an easier method from command and control of your MFA of choice, this could work for you. Just make sure you are clear with your Security team and all before implementing anything like this in production. :)