Home > Linux > Setting up Google Auth 2FA on Debian

Setting up Google Auth 2FA on Debian

In a previous post, I went through setting up a Google Authenticator TOTP on a Yubikey. Let’s expand that to installing Google’s TOTP PAM Module, so we can use the Yubikey or Google Authenticator App as a TOTP based 2 factor authentication method to access our server. In this post, we’ll be setting this up on a Debian Jessie AWS server.

There are a number of ways that you can configure your  Google TOTP setup –  one is to generate a seed for your users that they themselves cannot change, and the other is to let your users generate their own seed which they can change at any time. I will cover both in this tutorial as “admin generated” and “user generated” seeds. These steps are also the same for any Debian derived distros, like Ubuntu.

Since the server was just provisioned, we’ll need to make sure it is running the latest of all installed packages. Do this as root with

root@calculon:~# apt-get update && apt-get upgrade -y

Next, we’ll install the PAM module.

root@calculon:~# sudo apt-get install libpam-google-authenticator
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
  libqrencode3
The following NEW packages will be installed:
  libpam-google-authenticator libqrencode3
0 upgraded, 2 newly installed, 0 to remove and 1 not upgraded.
Need to get 65.8 kB of archives.
After this operation, 216 kB of additional disk space will be used.
Do you want to continue? [Y/n] y
Get:1 http://httpredir.debian.org/debian/ jessie/main libqrencode3 amd64 3.4.3-1 [33.8 kB]
Get:2 http://httpredir.debian.org/debian/ jessie/main libpam-google-authenticator amd64 20130529-2 [32.1 kB]
Fetched 65.8 kB in 0s (133 kB/s)
Selecting previously unselected package libqrencode3:amd64.
(Reading database ... 37159 files and directories currently installed.)
Preparing to unpack .../libqrencode3_3.4.3-1_amd64.deb ...
Unpacking libqrencode3:amd64 (3.4.3-1) ...
Selecting previously unselected package libpam-google-authenticator.
Preparing to unpack .../libpam-google-authenticator_20130529-2_amd64.deb ...
Unpacking libpam-google-authenticator (20130529-2) ...
Processing triggers for man-db (2.7.0.2-5) ...
Setting up libqrencode3:amd64 (3.4.3-1) ...
Setting up libpam-google-authenticator (20130529-2) ...
Processing triggers for libc-bin (2.19-18+deb8u4) ...

Admin generated seed

First we’ll need to create a folder that will store all our google authenticator codes, on a per user basis, and set the folders permissions accordingly.

root@calculon:~# mkdir /var/lib/google-auth
root@calculon:~# chmod 755 /var/lib/google-auth

Using the helper app, as root, you run

root@calculon:~$ google-authenticator

You’ll then be presented with a few options:

Do you want authentication tokens to be time-based (y/n) y

Select yes. You’ll then be given a link to a QR code, and some other pieces of information. The link will also be presented as ASCII art to your terminal so you can read it with a QR code reader. The ASCII art doesn’t copy and paste well.

https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/nick@calculon%3Fsecret%3DU6K7VEMFHUP3RESD

Screen Shot 2016-08-09 at 12.06.18

Your new secret key is: U6K7VEMFHUP3RESD
Your verification code is 769054
Your emergency scratch codes are:
  23933334
  20976218
  18022466
  48236448
  56343656

The next part is where the seed is stored, by default it’s .google_authenticator in your $HOME, and we’ll change this later – so we’ll save it for now.

Do you want me to update your "/root/.google_authenticator" file (y/n) y

Disallowing multiple uses of the same token is up to you – I recommend disallowing the use of the same token as it helps stop replay attacks.

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

Answer no at this part – time skew is a pain in the ass, and we can help mitigate it being an issue by selecting no.

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) n

Select yes for rate limiting. You should already have fail2ban installed as a matter of standard security, so this is making doubly sure your machine is hardened.

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y

The Google TOTP seed and emergency codes are stored in the seed file. The 5 numbers at the bottom of this file are your emergency codes. They are one time only codes, and the Google TOTP module will remove them on each use.

root@calculon:~$ cat .google_authenticator
U6K7VEMFHUP3RESD
" RATE_LIMIT 3 30 1470747565
" DISALLOW_REUSE
" TOTP_AUTH
  23933334
  20976218
  18022466
  48236448
  56343656

Now we need to move this file to the correct place and set the appropriate permissions. This seed will be used for the user nick, so

root@calculon:~# mv .google_authenticator /var/lib/google-auth/nick
root@calculon:~# chown root:root /var/lib/google-auth/nick
root@calculon:~# chmod 400 /var/lib/google-auth/nick

Changing Seed

Let’s say we want to edit the seed of U6K7VEMFHUP3RESD to be JBSWY3DPEHPK3PXP, which is the seed we previously set up when we configured the Yubikey. You might need to change the seed if, for example, someone’s phone is stolen that contains the seed in the Google Authenticator app or even as a matter of security policy / compliance. You can also remove the emergency codes by deleting them – they are the last 5 numbers of the seed file.

Replace the seed in the TOTP seed file and save – e.g.

root@calculon:~$ cat /var/lib/google-auth/nick
JBSWY3DPEHPK3PXP
" RATE_LIMIT 3 30 1470747565
" DISALLOW_REUSE
" TOTP_AUTH
  23933334
  20976218
  18022466
  48236448
  56343656

Now try to SSH to the machine again – you should notice that your old TOTP seed no longer works, and the new one does.

nick@Hedonismbot ~]$ ssh nick@calculon.example.com -i Fuck_You.pem
Authenticated with partial success.
Verification code:
Verification code:
 
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
 
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Aug  9 12:59:34 2016 from 89.101.22.185
nick@calculon:~$

User generated seed

Using the helper app, the user logs in and runs

nick@calculon:~$ google-authenticator

You’ll then be presented with a few options:

Do you want authentication tokens to be time-based (y/n) y

Select yes. You’ll then be given a link to a QR code, and some other pieces of information. The link will also be presented as ASCII art to your terminal so you can read it with a QR code reader. The ASCII art doesn’t copy and paste well.

https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/nick@calculon%3Fsecret%3DU6K7VEMFHUP3RESD

Screen Shot 2016-08-09 at 12.06.18

Your new secret key is: U6K7VEMFHUP3RESD
Your verification code is 769054
Your emergency scratch codes are:
  23933334
  20976218
  18022466
  48236448
  56343656

The next part is where the seed is stored, by default it’s .google_authenticator in your $HOME

Do you want me to update your "/home/nick/.google_authenticator" file (y/n) y

Disallowing multiple uses of the same token is up to you – I recommend disallowing the use of the same token as it helps stop replay attacks.

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

Answer no at this part – time skew is a pain in the ass, and we can help mitigate it being an issue by selecting no.

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) n

Select yes for rate limiting. You should already have fail2ban installed as a matter of standard security, so this is making doubly sure your machine is hardened.

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y

The Google TOTP seed and emergency codes are stored in .google_authenticator. The 5 numbers at the bottom of this file are your emergency codes. They are one time only codes, and the Google TOTP module will remove them on each use.

nick@calculon:~$ cat .google_authenticator
U6K7VEMFHUP3RESD
" RATE_LIMIT 3 30 1470747565
" DISALLOW_REUSE
" TOTP_AUTH
  23933334
  20976218
  18022466
  48236448
  56343656

Changing Seed

Let’s say we want to edit the seed of U6K7VEMFHUP3RESD to be JBSWY3DPEHPK3PXP, which is the seed we previously set up when we configured the Yubikey. You might need to change the seed if, for example, someone’s phone is stolen that contains the seed in the Google Authenticator app or even as a matter of security policy / compliance.

Replace the seed in the .google_authenticator file and save – e.g.

nick@calculon:~$ cat .google_authenticator
JBSWY3DPEHPK3PXP
" RATE_LIMIT 3 30 1470747565
" DISALLOW_REUSE
" TOTP_AUTH
  23933334
  20976218
  18022466
  48236448
  56343656

Now try to SSH to the machine again – you should notice that your old TOTP seed no longer works, and the new one does.

nick@Hedonismbot ~]$ ssh nick@calculon.example.com -i Fuck_You.pem
Authenticated with partial success.
Verification code:
Verification code:
 
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
 
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Aug  9 12:59:34 2016 from 89.101.22.185
nick@calculon:~$

Setting up OpenSSH

We need to configure OpenSSH and PAM to allow the use of the Google Auth PAM module. Here we have two options – either we make the use of Google’s Auth module mandatory or optional.

As root, add the following to /etc/pam.d/sshd file based on if you want optional or mandatory, as well as disabling the ability to common auth.

Admin Generates

TOTP Optional

# Use Google Auth -- Optional
auth required pam_google_authenticator.so nullok secret=/var/lib/google-auth/${USER} user=root
# Standard Un*x authentication.
#@include common-auth

The full file will, therefore, look like the following:

# PAM configuration for the Secure Shell service
 
# Use Google Auth -- Optional
auth required pam_google_authenticator.so nullok secret=/var/lib/google-auth/${USER} user=root
 
# Standard Un*x authentication.
#@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
 
# SELinux needs to be the first session rule.  This ensures that any
# lingering context has been cleared.  Without this it is possible that a
# module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so close
 
# Set the loginuid process attribute.
session    required     pam_loginuid.so
 
# Create a new session keyring.
session    optional     pam_keyinit.so force revoke
 
# 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
session    optional     pam_motd.so noupdate
 
# 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
 
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
session    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.
session    required     pam_env.so user_readenv=1 envfile=/etc/default/locale
 
# SELinux needs to intervene at login time to ensure that the process starts
# in the proper default security context.  Only sessions which are intended
# to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so open
 
# Standard Un*x password updating.
@include common-password

TOTP Mandatory

Edit the /etc/pam.d/sshd file to include

# Use Google Auth -- Mandatory
auth required pam_google_authenticator.so secret=/var/lib/google-auth/${USER} user=root
 
# Standard Un*x authentication.
#@include common-auth

The full file will, therefore, be:

# PAM configuration for the Secure Shell service
 
# Use Google Auth -- Mandatory
auth required pam_google_authenticator.so secret=/var/lib/google-auth/${USER} user=root
 
# Standard Un*x authentication.
#@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
 
# SELinux needs to be the first session rule.  This ensures that any
# lingering context has been cleared.  Without this it is possible that a
# module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so close
 
# Set the loginuid process attribute.
session    required     pam_loginuid.so
 
# Create a new session keyring.
session    optional     pam_keyinit.so force revoke
 
# 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
session    optional     pam_motd.so noupdate
 
# 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
 
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
session    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.
session    required     pam_env.so user_readenv=1 envfile=/etc/default/locale
 
# SELinux needs to intervene at login time to ensure that the process starts
# in the proper default security context.  Only sessions which are intended
# to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so open
 
# Standard Un*x password updating.
@include common-password

 

User Genreates

TOTP Optional

# Use Google Auth -- Optional
auth required pam_google_authenticator.so nullok
# Standard Un*x authentication.
#@include common-auth

The full file will, therefore, look like the following:

# PAM configuration for the Secure Shell service
 
# Use Google Auth -- Optional
auth required pam_google_authenticator.so nullok
 
# Standard Un*x authentication.
#@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
 
# SELinux needs to be the first session rule.  This ensures that any
# lingering context has been cleared.  Without this it is possible that a
# module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so close
 
# Set the loginuid process attribute.
session    required     pam_loginuid.so
 
# Create a new session keyring.
session    optional     pam_keyinit.so force revoke
 
# 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
session    optional     pam_motd.so noupdate
 
# 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
 
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
session    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.
session    required     pam_env.so user_readenv=1 envfile=/etc/default/locale
 
# SELinux needs to intervene at login time to ensure that the process starts
# in the proper default security context.  Only sessions which are intended
# to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so open
 
# Standard Un*x password updating.
@include common-password

TOTP Mandatory

Edit the /etc/pam.d/sshd file to include

# Use Google Auth -- Mandatory
auth required pam_google_authenticator.so
 
# Standard Un*x authentication.
#@include common-auth

The full file will, therefore, be:

# PAM configuration for the Secure Shell service
 
# Use Google Auth -- Mandatory
auth required pam_google_authenticator.so
 
# Standard Un*x authentication.
#@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
 
# SELinux needs to be the first session rule.  This ensures that any
# lingering context has been cleared.  Without this it is possible that a
# module could execute code in the wrong domain.
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so close
 
# Set the loginuid process attribute.
session    required     pam_loginuid.so
 
# Create a new session keyring.
session    optional     pam_keyinit.so force revoke
 
# 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
session    optional     pam_motd.so noupdate
 
# 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
 
# Read environment variables from /etc/environment and
# /etc/security/pam_env.conf.
session    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.
session    required     pam_env.so user_readenv=1 envfile=/etc/default/locale
 
# SELinux needs to intervene at login time to ensure that the process starts
# in the proper default security context.  Only sessions which are intended
# to run in the user's context should be run after this.
session [success=ok ignore=ignore module_unknown=ignore default=bad]        pam_selinux.so open
 
# Standard Un*x password updating.
@include common-password

Configure OpenSSH

Next, we need to configure OpenSSH to support the Google Auth module as a form of authentication. Open up /etc/ssh/sshd_config in your favourite text editor. We’ll need to set the following:

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes

and

# Allow keyboard-interactive for TOTP 
AuthenticationMethods publickey,keyboard-interactive

This makes our full sshd_config as:

# Package generated configuration file
# See the sshd_config(5) manpage for details
 
# Allow keyboard-interactive for TOTP 
AuthenticationMethods publickey,keyboard-interactive
 
# What ports, IPs and protocols we listen for
Port 22
# Use these options to restrict which interfaces/protocols sshd will bind to
#ListenAddress ::
#ListenAddress 0.0.0.0
Protocol 2
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
#Privilege Separation is turned on for security
UsePrivilegeSeparation yes
 
# Lifetime and size of ephemeral version 1 server key
KeyRegenerationInterval 3600
ServerKeyBits 1024
 
# Logging
SyslogFacility AUTH
LogLevel INFO
 
# Authentication:
LoginGraceTime 120
PermitRootLogin without-password
StrictModes yes
 
RSAAuthentication yes
PubkeyAuthentication yes
#AuthorizedKeysFile	%h/.ssh/authorized_keys
 
# Don't read the user's ~/.rhosts and ~/.shosts files
IgnoreRhosts yes
# For this to work you will also need host keys in /etc/ssh_known_hosts
RhostsRSAAuthentication no
# similar for protocol version 2
HostbasedAuthentication no
# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication
#IgnoreUserKnownHosts yes
 
# To enable empty passwords, change to yes (NOT RECOMMENDED)
PermitEmptyPasswords no
 
# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)
ChallengeResponseAuthentication yes
 
# Change to no to disable tunnelled clear text passwords
PasswordAuthentication no
 
# Kerberos options
#KerberosAuthentication no
#KerberosGetAFSToken no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
 
# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
 
X11Forwarding yes
X11DisplayOffset 10
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
#UseLogin no
 
#MaxStartups 10:30:60
#Banner /etc/issue.net
 
# Allow client to pass locale environment variables
AcceptEnv LANG LC_*
 
Subsystem sftp /usr/lib/openssh/sftp-server
 
# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
UsePAM yes
UseDNS no

Now all you have to do is restart openssh:

root@calculon:~# service sshd restart

Now when you SSH to the machine, you should see the following:

[nick@Hedonismbot ~]$ ssh nick@calculon.example.com -i Fuck_You.pem
Authenticated with partial success.
Verification code:
 
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
 
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Aug  9 12:50:18 2016 from 89.101.22.185
nick@calculon:~$

We’ve now been forced to send our TOTP or one of our emergency tokens for access to the machine, along with our SSH key. If you don’t use SSH keys and ban passwords, you’re a fucking moron – AWS forces you to use SSH keys for a reason.

TOP