Installing Google Authenticator on CentOS

After a conversation at work about how easy it is to use Google Authenticator for two-factor authentication, I decided that installing Google Authenticator on my CentOS server would be my next project. After all, I had recently automated download of website log files, and I needed to find the next to-do at home. Installing Google Authenticator took a morning and was not instantaneous, but it was simple enough as you will see below.

If you want to install Google Authenticator on Linux, there are many how-tos online. Most of them start by telling people to install the package using apt-get or yum if your repositories are set up properly. Sure, you can do this if you want, but there are problems if you go down this path: the biggest one is that the default packaged version does not support all options, including the use of “nullok” in your PAM file. Instead, build it from source the old-fashioned way. Here is one guide to doing so: the key is in these lines:

[codesyntax lang=”bash” lines=”no” container=”div” capitalize=”no” strict=”yes” blockstate=”expanded” doclinks=”0″]

wget https://google-authenticator.googlecode.com/files/libpam-google-authenticator-1.0-source.tar.bz2 
tar jvxf libpam-google-authenticator-1.0-source.tar.bz2 
cd libpam-google-authenticator-1.0/ 
make 
make install

[/codesyntax]

Once you have it installed, you need to run google-authenticator as the user you want to set up two-factor for. Since I wanted to set it up for my root account, I ran it as root, answering yes to all of the questions. It will create a ~/.google_authenticator file, output your secret key and emergency scratch codes, prompting you to write them down. You can use the link to create a QR code that you scan with your authenticator app, and once done, you will have the code generator ready to use on your mobile device.

Next you’ll have to configure PAM to ask for the authenticator code. Open up /etc/pam.d/ssdh and add the line below to the top of the file:

auth required pam_google_authenticator.so nullok

The “nullok” at the end of the line will allow users that do not have Google Authenticator set up for them to still log in with their password; as stated before, if you install the package this option won’t work (trust me, Iearned the hard way). I also learned the hard way that the above line needs to be at the top of the sshd file and not the end, otherwise PAM doesn’t get down that far. You’ll also need to edit /etc/vim/ssh/ssdh_config to set ChallengeResponseAuthentication to yes if not already done so. Finall, restart the sshd service, but before you do, either log on locally or keep another session open, because….

If you have SELinux enabled, this won’t work!

The common answer to dealing with this problem with Google Authenticator is to turn off SELinux, but that’s the easy, insecure way out. It’s actually not that hard to configure SELinux to make this work, and this is what I did.

The root of the problem is that SELinux prevents sshd from writing to the .google_authenticator file in your user’s home directory. You can see the actual error log in the /var/log/audit directory. For me, I had a bunch of lines like this:

[codesyntax lang=”bash” lines=”no”]

type=AVC msg=audit(1411221199.631:102852): avc:  denied  { create } for  pid=6102 comm="sshd" name=".google_authenticator~" scontext=unconfined_u:system_r:sshd_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:admin_home_t:s0 tclass=file

[/codesyntax]

I am no SELinux expert at all. So I went to this site to learn a bit more about what the audit log was saying. It wasn’t too hard to interpret: sshd was denied from creating the .google_authenticator~ file (which then overwrites the .google_authenticator file). The reason is a bit trickier and you have to know a bit about SELinux security contexts to understand, but the gist of it is that the sshd_t type is not allowed to access the admin_home_t type. Well, that’s intuitive, isn’t it?

SELinux has the concept of policies that govern access controls for different types, and so the solution is to create a new policy to allow this interaction; I’ll leave aside the real concerns about letting sshd access security-related things in your home directory, but feel free to read the lively debate on the bug submission. Creating a new policy is not that hard, and this is the site I used to help me do it. You need to create two files, one ending in .te and one ending in .fc. This is my google_authenticator.te file, edited from the above link:

[codesyntax lang=”text”]

# Name and version, every module should have this. 
policy_module(sshd_google_authenticator, 0.0.2)

# List of the types, class and everything else you are going to use in your module that is not defined in this .te file. 
# If you are getting any errors when you compile your module that it is unable to find a type, you probably forgot to declare it here. 
require { 
  type sshd_t; 
  type user_home_dir_t; 
  type admin_home_t; 
}

# This is where we define our type. A good practise is to append _t for all types. 
# This is the type we are going to give our .google_authenticator file. 
type sshd_google_authenticator_t;

# What role our type should have. This is almost always going to be object_r 
role object_r types sshd_google_authenticator_t;

# What sshd_t (the context the ssh daemon runs as) should be able to do with our type (sshd_google_authenticator_t), 
# as a file. rename, create and unlink are base definitions, rw_file_perms is a set of rules. 
# The rw_file_perms group is defined in /usr/share/selinux/devel/include/support/obj_perm_sets.spt with a lot of other 
# groups. Reading this files give you a good overview of what they allow. 
allow sshd_t sshd_google_authenticator_t:file { rename create unlink rw_file_perms };

# Without this, SELinux will be way too strict as default, as it won't know what this type really is. 
# Remember that SELinux doesn’t only deal with files, but sockets and other filetypes as well. 
# Leaving this out will still allow sshd_t to do its stuff, but you, in your shell will see a weird file. 
# The only thing you will see is the file name. Even permissions will be hidden from you. (a fun trick to pull on your friends.. :] ) 
# An overview of this is located at http://oss.tresys.com/docs/refpolicy/api/kernel_files.html. 
files_type(sshd_google_authenticator_t) 
filetrans_pattern(sshd_t, user_home_dir_t, sshd_google_authenticator_t, file, ".google_authenticator") 
filetrans_pattern(sshd_t, admin_home_t, sshd_google_authenticator_t, file, ".google_authenticator")

[/codesyntax]

You’ll notice that I’ve added a few things to the default. First, I’ve added required types of user_home_dir_t and admin_home_t because I reference them at the bottom; without this you get an error. Second, I’ve used the trick at the bottom of that blog post to set the context of any newly-created .google_authenticator file to the right type (the sshd_google_authenticator_t type) in both a normal user’s home directory as well as the root home directory.

The google_authenticator.fc file is, as you would expect, one line long:

[codesyntax lang=”text”]

/root/.google_authenticator     --  gen_context(system_u:object_r:sshd_google_authenticator_t,s0)

[/codesyntax]

I did it this way instead of using HOME_DIR/ because for the root home directory, that variable was not expanding correctly so I had to use the actual path.

I compiled the module as stated and used semodule –i to load it, and lo and behold it worked! Not only that, but because I used the nullok option, when I log in as a normal user I don’t get prompted for Google Authenticator, as expected.

And that’s it! I now have two-factor authentication enabled for my root account when I ssh in. Pretty neat. What’s next? Well, I still want to get chef up and running…