2FA for ssh
2 Factor Authentication is a great way to improve security on your systems. Adding 2FA for secure ssh on CentOS8 is simple enough, and there are plenty of ‘how-to’ guides out there. For example: https://www.digitalocean.com/community/tutorials/how-to-set-up-multi-factor-authentication-for-ssh-on-centos-7 was written for CentOS7, but is still broadly applicable on CentOS8.
This particular install needed a tweak though, and I had to dig through information all over the web to work out what was needed to get it configured as required. What I needed to achieve was:
- external ssh logins by one specified user only, secured with ssh keys AND 2FA
- internal SSH logins by that user and another, with just password authentication
To do this I assume you already have setup:
- sshd running on a port externally visible
- 2 x pre-existing users, user1 and user2, who can log on to ssh already
- user1 who already access the ssh server externally using secure keys and no password
- external access, port forwarding, secure keys etc., already working – these are not in scope for this post
The end goal is to get:
- user1 – access ssh from the internal network, and is the ONLY user granted external access to ssh
- user2 – access ssh from the internal network only
Google-authenticator from EPEL
The 2FA part uses the google-authenticator package, so install this from the EPEL repo.
Once it is installed ‘su’ to user1 and run ‘google-authenticator’ – make sure it completes through and writes the .google_authenticator file in the user’s home directory, picking appropriate options and entering the generated 2FA code into the app of your choosing. Make sure you record the resultant recovery codes somewhere safe! We don’t need to run it as user2, as user2 will not be using 2FA (internal access only).
Configure PAM
PAM is the module sshd uses to manage authentication. We need to tweak its config on how it will manage auth for sshd:
nano /etc/pam.d/sshd
…and add the following at the end of the file (2 lines):
auth [success=done default=ignore] pam_access.so accessfile=/etc/security/access-local.conf
auth optional pam_google_authenticator.so
Note the line has ‘auth optional’ – we will enforce it with sshd_config.
Create and edit the noted ‘/etc/security/access-local.conf’ file and add the following (editing as needed for your local IP setup):
+ : ALL : 10.0.0.0/24 - : ALL : ALL
sshd configuration
To get SSHD working we are managing most of our security configuration (defined in /etc/ssh/sshd_config) in two MATCH blocks, one for the internal network, one for external. Note, if you have PasswordAuthentication set to yes or no in the file, comment it out – we will manage it on a network address MATCH basis (but do make sure your MATCH blocks catch *all* possible network addresses, as the default is ‘yes’).
You WILL need to set the following in the file before the MATCH blocks as trying to set this inside the blocks fails (I found different ‘man’ entries for this option saying it can be set inside a MATCH block, and that it cannot; on CentOS8 version, it cannot). This setting enables the ssh login coming back and asking for your 2FA key:
ChallengeResponseAuthentication yes
In this setup’s example there is very little set in sshd’s config other than ChallengeResponseAuthentication before the MATCH blocks – your local configuration may of course vary, but you will need to have setup key auth.
Local Network MATCH:
The first MATCH block in sshd_config applies to local network users only. Note I have used specific users here, as I am only enabling two – you can use AllowGroups if you prefer. Note these users do not have to have google_authenticator run *unless* they are also in the external MATCH AllowUsers list.
Match Address 10.0.0.0/24
AllowUsers user1 user2
AuthenticationMethods password
PasswordAuthentication yes
Outside Network MATCH:
The second MATCH block applies to external network users only.
Note the leading asterisk in the address match, it is important – you are saying “mark any (‘*’) address except (‘!’) 10.0.0.0/24” as external in this block.
Within the block, the options are saying: if the block is triggered only allow user1 to try and login, and only succeed if they have secure ssh keys setup and working *and* google-authenticator configured and successfully passed. Password authentication is definitely NO here!
Match Address *,!10.0.0.0/24
AllowUsers user1
AuthenticationMethods publickey,keyboard-interactive
PasswordAuthentication no
Restart sshd – BUT keep your ssh session active in case you have messed something up and cannot log back in.
Logging in!
OK – we are ready to test 2FA for secure ssh on CentOS8…
Local Network first!
You should be able to login from the local network as user1 and user2, with passwords only.
- user1 tries to log in to ssh from a local network address
- sshd_config’s MATCH block triggers for local network
- In that MATCH block user1 is on the AllowUsers list, so we proceed
- PAM checks ‘access-local.conf’ file, and the ‘+’ entry says “OK, you are any user (“ALL”) coming in from my local network subnet, skip bringing in google-authenticator”
- user1 is prompted for their password, enters it
- sshd is happy (‘PasswordAuthentication yes’) and PAM are both happy – login!
External Network
You should be able to login from an external network as user1 only, and only using (already working) ssh keys, and only with the newly configured 2FA code.
- user1 tries to log in to ssh from an external network address
- sshd_config’s MATCH block triggers for external network
- In that MATCH block user1 is on the AllowUsers list, so we proceed
- But now we are proceeding with ‘PasswordAuthentication No’ and ‘AuthenticationMethods publickey,keyboardinteractive’
- PAM checks ‘access-local.conf’ file, and the ‘-‘ entry says “You are not a user coming in from my local network subnet, so we need google-authenticator”
- For PAM and sshd to accept this user, the secure keys have to have been accepted, and user1 is now also prompted for their 2FA code
- If 2FA is successful sshd and PAM are both happy – login
From an internal or external network location try to login to ssh as a user not in the AllowUsers list and sshd should immediately reject it.
Hopefully this guide on setting 2FA for secure ssh on CentOS8 was useful. Enjoy!