Configure Google Authenticator on CentOS 7 – Part 2

Build and Install Google Authenticator

Download the codebase

As I stated in the intro, many of the guides used an outdated codebase from the now defunct Google Code Project Hosting service or they used an RPM repository that I didn’t find completely trustworthy.  To resolve those problems, I decided to just rely on the GitHub repository that is maintained by the Google developer team.

The first step is to download the code.  The GitHub repository for the PAM module is located at:

[root@server ~]# cd /opt[root@server opt]# wget -q

Compile and install

NOTE: as part of the build and install process, I have included a directive to change the base directory that is used for the installation.  The reason for this is due to the need to create two symbolic links.  This will be covered in the next section.

The following are the steps as laid out in the PAM Module Instructions from the wiki:

make install

To anyone that has compiled from source, these are likely to be sufficient, however I have included an abbreviated version of my code snippet for this since it may provide additional information for those that aren’t quite so familiar with the process.

[root@server ~]# cd /opt/google-authenticator-master/libpam/
[root@server libpam]# ./
libtoolize: putting auxiliary files in AC_CONFIG_AUX_DIR, `build'.
libtoolize: copying file `build/'
libtoolize: putting macros in AC_CONFIG_MACRO_DIR, `build'.
libtoolize: copying file `build/libtool.m4'
libtoolize: copying file `build/ltoptions.m4'
libtoolize: copying file `build/ltsugar.m4'
libtoolize: copying file `build/ltversion.m4'
libtoolize: copying file `build/lt~obsolete.m4' installing 'build/config.guess' installing 'build/config.sub' installing 'build/install-sh' installing 'build/missing' installing 'build/depcomp'
parallel-tests: installing 'build/test-driver'

[root@server libpam]# ./configure --prefix=/usr
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables...


google-authenticator version 1.01
Prefix.........: /usr
Debug Build....:
C Compiler.....: gcc -std=gnu99 -g -O2
Linker.........: /bin/ld -m elf_x86_64 -ldl

[root@server libpam]# makemake all-ammake[1]: Entering directory `/opt/google-authenticator-master/libpam'


[root@server libpam]# make installmake[1]: Entering directory `/opt/google-authenticator-master/libpam'
/bin/mkdir -p '/usr/bin'
/bin/sh ./libtool --mode=install /bin/install -c google-authenticator '/usr/bin'
libtool: install: /bin/install -c google-authenticator /usr/bin/google-authenticator
/bin/mkdir -p '/usr/share/doc/google-authenticator'


make[1]: Leaving directory `/opt/google-authenticator-master/libpam'

Configure PAM and SSH

The next two subsections cover the creation of two symbolic links (mentioned in the note in the previous section) and the change to the configuration file for the PAM system governing the SSH daemon.  Additionally, there are a couple of modifications that you might need to perform on the SSH daemon configuration as well, depending on your setup.

Add symlinks to 64bit libraries and module

During my installation, I ran into a situation that was caused by the location used for the installation of the PAM library and object files.  I solved this in two steps:

  1. Change the base location of the file installation
  2. Addition of two symbolic links

The first step was covered in the build process by the addition of a parameter to the call to the configure script:

[root@server libpam]# ./configure --prefix=/usr

The second step was accomplished using the following commands:

[root@server ~]# cd /usr/lib64/security/
[root@server security]# ln -s /usr/lib/security/
[root@server security]# ln -s /usr/lib/security/

As you can see from the following directory listing, this results in two symbolic links pointing back to the installed files in /usr/lib/security.

[root@server security]# pwd
[root@server security]# ls -l `find . -maxdepth 1 -type l -print`
lrwxrwxrwx 1 root root 15 May 11  2018 ./ ->
lrwxrwxrwx 1 root root 11 May 11  2018 ./ ->
lrwxrwxrwx 1 root root 11 May 11  2018 ./ ->
lrwxrwxrwx 1 root root 11 May 11  2018 ./ ->
lrwxrwxrwx 1 root root 11 May 11  2018 ./ ->

Update PAM config file for SSH

After you have the files installed in the correct locations, you need to update the PAM configuration for SSH.  Now, there are a number of ways that you can go about doing this.  I chose to use an inline edit with sed.

NOTE: editing PAM configurations can result in denying access to your system if performed incorrectly.  When editing the SSH PAM configuration file, always do the following:

  1. BACKUP the original file
  2. Make sure you have console access
  3. Open a secondary SSH terminal so that if you screw up you are still logged in
[root@server ~]# cd /etc/pam.d/
[root@server pam.d]# cp -p sshd sshd_backup
[root@server pam.d]# sed -i "2iauth required" sshd

If you would like to use HOTP (counter based) instead of TOTP add the following the end of the text to be inserted using the sed command in the above command example:


Update the SSH configuration

In order to make the SSH part work there are three configuration settings that have to be set in a particular way:

  • PasswordAuthentication
  • UsePAM
  • ChallengeResponseAuthentication

The configuration file is located here:


The desired settings for these are as follows:

PasswordAuthentication yes
UsePAM yes
ChallengeResponseAuthentication yes

The first two are likely already set to be to the desired value, but the third one will need to be changed from “no” to “yes” on a default CentOS 7 install.  To check your settings run the following command and the edit the configuration file if needed:

[root@server ~]# egrep "PasswordAuthentication|UsePAM|ChallengeResponseAuthentication" /etc/ssh/sshd_config | egrep -v "#"
PasswordAuthentication yes
ChallengeResponseAuthentication yes
UsePAM yes

Next you need to restart the SSH daemon so that the changes take effect.  Warning: until you have configured the userland piece (see the next section) your SSH users won’t be able to login. (This includes the root user, but you aren’t logging in remotely as root anyway right?)

Configure userland

This step involves configuring the users to use a TOTP token by running the google-authenticator binary and then answering some questions.  Here is the usage statement for the binary:

 [root@external ~]# google-authenticator -h
google-authenticator [<options>]
 -h, --help                     Print this message
 -c, --counter-based            Set up counter-based (HOTP) verification
 -t, --time-based               Set up time-based (TOTP) verification
 -d, --disallow-reuse           Disallow reuse of previously used TOTP tokens
 -D, --allow-reuse              Allow reuse of previously used TOTP tokens
 -f, --force                    Write file without first confirming with user
 -l, --label=<label>            Override the default label in "otpauth://" URL
 -i, --issuer=<issuer>          Override the default issuer in "otpauth://" URL
 -q, --quiet                    Quiet mode
 -Q, --qr-mode={NONE,ANSI,UTF8} QRCode output mode
 -r, --rate-limit=N             Limit logins to N per every M seconds
 -R, --rate-time=M              Limit logins to N per every M seconds
 -u, --no-rate-limit            Disable rate-limiting
 -s, --secret=<file>            Specify a non-standard file location
 -S, --step-size=S              Set interval between token refreshes
 -w, --window-size=W            Set window of concurrently valid codes
 -W, --minimal-window           Disable window of concurrently valid codes
 -e, --emergency-codes=N        Number of emergency codes to generate

The following snippet shows the command output and answers of run of the google-authenticator binary for a regular user (NOTE: I have removed the actual emergency codes and the secret key, also if your terminal will display it, you may be presented with a QR code to scan.):

[root@server ~]# su - johndoe
[johndoe@server ~]$ google-authenticator
Do you want authentication tokens to be time-based (y/n) y
Your new secret key is: SECRET_KEY_HERE
Your verification code is VERIFY_CODE_HERE
Your emergency scratch codes are:EMER_CODE_HERE
Do you want me to update your "/home/johndoe/.google_authenticator" file? (y/n) y
Do you want to disallow multiple uses of the same authenticationtoken? 
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

By default, tokens are good for 30 seconds. 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 +-1min (window size of 3) to about +-4min (window size of
17 acceptable tokens).
Do you want to do so? (y/n) y

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

If you would like to just provide a single command for your users to run to avoid having to tell them the answers as laid out in the above example, use the following:

google-authenticator --time-based --disallow-reuse --window-size=17 --rate-limit=3 --rate-time=30 --force

Extra bits

The following sections detail two useful options.

Whitelist a network

It is possible that you may have a particular network segment, that by it’s very nature is secure enough to not require the additional steps of 2FA.  If this is the case for your installation, here is how you can whitelist a network segment.

  1. Create access control list
  2. Update /etc/pam.d/sshd to use to ACL

The following shows the contents of an ACL that would whitelist the network

[root@server ~]# vi /etc/security/2fa-acl.conf
[root@server ~]# cat /etc/security/2fa-acl.conf
# Allows certain network segments to skip 2FA
+ : ALL :
- : ALL : ALL

Once you have the ACL built add an exclusionary conditional to the SSH PAM configuration file BEFORE the line that was added to activate the module.  For example:

[root@server ~]# sed -i "2iauth [success=1 default=ignore] accessfile=/etc/security/2fa-acl.conf" /etc/pam.d/sshd

Allow unconfigured users

If you have a single user or two it will be easy enough to manage providing the string activate the google-authenticator system for a user, but if you are managing a server with a large number of users, you might want to establish a grace period during which users that have not yet configured their local account to continue to login.

This can be accomplished by adding this parameter to the end of the PAM configuration file line:


If you have already added the line and you just want to easily add the new parameter, then use the following sed command:

[root@server ~]# sed -i '2s/$/ nullok/' /etc/pam.d/sshd


If you encounter login failures during your testing, a good place to start is the content of the following log files:

  • /var/log/secure
  • /var/log/audit/audit.log

Configure Google Authenticator on CentOS 7 – Part 1


As part of the rebuild on my Plex Media Server using CentOS 7, I had intended to configure Google Authenticator but hadn’t gotten around to doing it yet.  As I got into the process recently I discovered that many of the steps that I had used when configuring my CentOS 6 Digital Ocean droplet were out of date to the point of uselessness.

I also discovered that most of the guides that I found either relied on the older 1.0 code release which was also outdated or used a unknown RPM repo.  As such I decided to write up the process that I followed to use the code downloaded from the official GitHub repository.

NOTE: If you are doing this in an enterprise setting, it is likely that your company has particular settings and restrictions that you may need to adhere to (e.g., not running things as the root user). Also, please note that all of my examples use the CentOS defaults unless specifically noted.

Continue reading

Checking your password expiration date (Linux Edition)

While logging into one of the Linux jump boxes at work today, it occurred to me that while I recently got notified about my password expiration from our Active Directory farm, I had no idea when my Linux password would expire or what the password life was.

To find out this information you can easily use the chage command.

Here is what the output looks like:

[user@myserver ~]$ chage -l user
Last password change : Apr 09, 2015
Password expires : Jul 08, 2015
Password inactive : never
Account expires : never
Minimum number of days between password change : 1
Maximum number of days between password change : 90
Number of days of warning before password expires : 7

It may seem like such a simple thing to do, but knowing when your password expires can be a lifesaver on occasion.

Checking your password expiration date (Active Directory Edition)

In many cases your enterprise Active Directory will not involve too many domains, in fact it is quite common for an Active Directory implementation to only include one domain.  In some cases, however, when you have the unfortunate situation of having a username in multliple domains with differing policies on password expiration it is useful to be able to know when your password, or that of another user will expire.  Here is an easy way to accomplish this from the command line.

For the current active user

net user /domain

For a different user

net user /domain _username_here_

Here is an example of the output:

User name afore
Full Name Andrew Fore
User's comment
Country code 000 (System Default)
Account active Yes
Account expires Never
Password last set 1/29/2015 4:38:37 PM
Password expires 4/29/2015 4:38:37 PM
Password changeable 1/29/2015 4:38:37 PM
Password required Yes
User may change password Yes
Workstations allowed All
Logon script
User profile
Home directory
Last logon 3/18/2015 3:27:55 PM
Logon hours allowed All
Local Group Memberships
Global Group memberships *
VMWare Admins *
Domain Users*

If you notice there is a lot of useful information regarding the user account here, but of particular interest in my situation was the value of Password expires since I was trying to ensure that I got my password reset prior to the policy setting so that I would not find myself locked out over the weekend that I went on call when the Helpdesk would be closed.

Updating WordFence WAF Whitelist IP programmatically

WordPress is one of the most popular CMS systems on the Internet. In the United States alone, roughly 30% of the top 1 million websites are powered by this system.

One of the most obvious things that you have to be concerned with on any hosting site is security. A widely used WordPress security plugin is WordFence. One of the featured of this plugin is a WAF, or web application firewall. While having a firewall is very handy and a great method of blocking bad actors, there are often cases in which you would want to whitelist an IP address to ensure that certain traffic sources always make it in.

For example, I try to ensure that the dynamic DNS IP for my home egress is always whitelisted to ensure that I am never blocked from managing my own CMS. With a CMS however you do not always know when your ISP changes your IP.

Fortunately, like all WP plugins, WordFence stores its configuration data in tables in the WP database. This allows for programmatic access via the command line. In order to handle this what I can up with was a script that can be run on manual basis, or via a cronjob, to update this database field.

You can check the script out in my Github repo:

First post! First post!

“First post! First post!” These words echo across the multiverse of forums, blogs, and comment threads all across the Internet.

This blog is intended to be a home for musings, articles, how-to’s, and other items of interest that have a connection to the security of our computers and the information that they contain or serve as a conduit for.

In some cases these articles will be deep dives into the underpinnings of an OS or application, while in other cases they will be overviews or opinions on our society and it’s use of the technology that we interact with every day.