It’s taken some time to get up to speed, but two-factor authentication is finally gaining the momentum it should have had a long time ago.
Joe Consumer is able to get a taste of proper authentication now that Google has added it to Gmail, and system administrators have been able to add Google Authenticator to SSH via PAM for a while now.
Armed with an Apache module called google-authenticator-apache-module, it is possible to integrate Google Authenticator with HTTP authentication.
For the purposes of this article, I’m going to assume that you have a working instance of SSH that has Google Authenticator integrated.
First thing that needs doing: head over and fetch a copy of the module, in binary or source form. For reasons that will become apparent, I highly recommend that you download the source version.
Following the instructions on the project’s wiki page, you need to add the following line to the Modules section of your http.conf:
Loadmodule authn_google_module modules/mod_authn_google_authenticator.so
And add an entry based on the example below in your Directory section:
<Directory "/srv/http/auth_test"> Options FollowSymLinks Indexes ExecCGI AllowOverride All Order deny,allow Allow from all AuthType Basic AuthName "Auth Test" AuthBasicProvider "google_authenticator" Require valid-user GoogleAuthUserPath /etc/httpd/ga_auth GoogleAuthCookieLife 3600 GoogleAuthEntryWindow 2 </Directory>
The final step needed so that we can test this is to copy across a .google_authenticator file generated for SSH/PAM authentication. The file needs to be renamed to the username that we will be logging in as, ie, /etc/httpd/ga_auth/testuser for the user called testuser.
Now we are ready to roll: restart Apache, break out your Google Authenticator app on your phone, and see if you can log in. It’s not working, is it?
A glance at the error log shows:
[notice] Apache/2.2.22 (Unix) mod_ssl/2.2.22 OpenSSL/1.0.1c DAV/2 PHP/5.4.6 configured -- resuming normal operations [error] [client 127.0.0.1] **** PW AUTH at T=1346644020 user "test" [error] [client 127.0.0.1] (2)No such file or directory: check_password: Could not open password file: /etc/httpd/ga_auth/(null) [error] [client 127.0.0.1] user test: authentication failure for "/auth_test/": Password Mismatch
The reason we are seeing this error is that the module is not looking for the correct filename. In fact, for any username, it is looking for a file called “(null)”. Not the best operating software ever created.
Fortunately, a fix exists on the project’s issue tracker that provides replacements that work correctly. I’d use the source version as a security measure, especially if I were to put this into production.
Once the patched module is reinstalled and Apache restarted, a look into the error log should see the module working as expected, and trying to match the correct access codes with the user input. From a user perspective, the code is used instead of the password, and needs to be typed into the password field on the HTTP authentication dialog.
[error] [client 127.0.0.1] Checking codes @ T=44888256 "864881" vs. "18281" [error] [client 127.0.0.1] Checking codes @ T=44888256 "67751" vs. "18281" [error] [client 127.0.0.1] Checking codes @ T=44888256 "945532" vs. "18281" [error] [client 127.0.0.1] Checking codes @ T=44888256 "502130" vs. "18281" [error] [client 127.0.0.1] Checking codes @ T=44888256 "18281" vs. "18281" [error] [client 127.0.0.1] Created cookie expires 1346651300 (time = 3600) hash is DSA8+AyT5QlcvbsF7JHX75bi37c= Cookie: google_authn=testuser:1346651300:DSA8+AyT5QlcvbsF7JHX75bi37c=
Will this system replace your existing authentication system?
It has a couple of limitations in not being able to make use of scratch codes and not invalidation codes, once they are used. Another issue to be aware of is the compromise that must be made in configuration between the window size of existing codes to allow and the lifetime of the cookie. In its strictest form, it would be possible to demand a new authentication code each minute; this could be acceptable, provided that the clocks between the user and the server were synchronised — hardly the easiest task in the world.
Personally, I wouldn’t like being responsible for a system that produced a new Google authenticator file for each and every user of a publicly facing server. The amount of work needed on the user side, such as loading a key or scanning a QR code with a mobile phone, is quite high and is not helped by the lack of emergency scratch code support. It would make “forgetting a password” a truly horrid experience — especially when compared to the alternatives.
Where this could be useful is in locking down some super-sensitive internal information, where the pain of setup and management is worth it, and the user session is not too long.
In this scenario, the module is a nice augmentation to an existing authentication solution.