FreeRADIUS Google Dual Factor Authenticator Introduction Google Authenticator ( is a great free dual factor authentication system. "The Google Authenticator project includes implementations of one-time passcode generators for several mobile platforms". It can be used in conjunction with FreeRADIUS to provide Free 2 factor authentication, something that usually costs a ton of money. This all works because of a library called PAM. PAM is "Pluggable Authentication Modules" for Linux system user and password authentication. For more information on PAM see Google Authenticator has a PAM module that is included as part of the project. PAM is the glue that allows FreeRADIUS to talk to Google Authenticator. FreeRADIUS is a popular open source radius server. Radius is a standardized authentication system that can be used to authenticate many different devices including VPNs, Routers, Switches, Computers, and much more. For more information on FreeRADIUS see Tutorial Now, how to set it up. For the purpose of this tutorial I will be using Ubuntu 12.04 Server, but this should be able to adapt to many different distributions. Yes, I know there is a package... Don't install it. If you do install it, remove it. We need to compile the source. So start out with this: NTP Time Sync Install (Optional, but Highly Recommended) The default behavior of the Google Authenticator is to use a time based token. Because of this you will likely want to install NTP so that your server time is always correct. This is especially important on a virtual machine, due to the increased time drift that is often a problem with Virtual Machine environments. sudo bash apt-get update apt-get install ntp Install FreeRADIUS and other Necessary Packages sudo bash apt-get update apt-get install build-essential libpam0g-dev freeradius git libqrencode3 Download Google Authenticator Pam Module Source cd ~ git clone cd google-authenticator/libpam/ make make install Configure Local Unix Groups We will need to add a group called 'radius-disabled' to drop users in, when you want to disable access (rather than removing them entirely) addgroup radius-disabled Configure FreeRADIUS FreeRADIUS must run as root for this to work. Yeah, I know it's not good practice, but thats the way this works. In other words, you may want to limit the use of this box to FreeRADIUS authentication only. The reason for this is so that FreeRADIUS can access the .google_authenticator token in each home directory. Otherwise FreeRADIUS does not have access. First, edit /etc/freeradius/radusd.conf You need to locate the following lines: # user/group: The name (or #number) of the user/group to run radiusd as. # # If these are commented out, the server will run as the user/group # that started it. In order to change to a different user/group, you # MUST be root ( or have root privleges ) to start the server. # # We STRONGLY recommend that you run the server with as few permissions # as possible. That is, if you're not using shadow passwords, the # user and group items below should be set to radius'. # # NOTE that some kernels refuse to setgid(group) when the value of # (unsigned)group is above 60000; don't use group nobody on these systems! # # On systems with shadow passwords, you might have to set 'group = shadow' # for the server to be able to read the shadow password file. If you can # authenticate users while in debug mode, but not in daemon mode, it may be # that the debugging mode server is running as a user that can read the # shadow info, and the user listed below can not. # # The server will also try to use "initgroups" to read /etc/groups. # It will join all groups where "user" is a member. This can allow # for some finer-grained access controls. # user = freerad group = freerad and then change the user & group lines to look like the following user = root group = root Once you have done this, save and close the file. Next edit, /etc/freeradius/users You need to locate the following lines: # # Deny access for a group of users. # # Note that there is NO 'Fall-Through' attribute, so the user will not # be given any additional resources. # We will start by creating a group that you can add users to and disable their access. Directly after these lines add the following code: DEFAULT Group == "radius-disabled", Auth-Type := Reject Reply-Message = "Your account has been disabled." Now, we will add the default rule to use the PAM libraries to authenticate users Directly after the previous lines of code, add the following: DEFAULT Auth-Type := PAM Now edit, /etc/freeradius/sites-enabled/default Locate the following lines of code: authenticate { # # PAP authentication, when a back-end database listed # in the 'authorize' section supplies a password. The # password can be clear-text, or encrypted. Auth-Type PAP { pap } # # Most people want CHAP authentication # A back-end database listed in the 'authorize' section # MUST supply a CLEAR TEXT password. Encrypted passwords # won't work. Auth-Type CHAP { chap } # # MSCHAP authentication. Auth-Type MS-CHAP { mschap } # # If you have a Cisco SIP server authenticating against # FreeRADIUS, uncomment the following line, and the 'digest' # line in the 'authorize' section. digest # # Pluggable Authentication Modules. # pam # # See 'man getpwent' for information on how the 'unix' # module checks the users password. Note that packets # containing CHAP-Password attributes CANNOT be authenticated # against /etc/passwd! See the FAQ for details. # # For normal "crypt" authentication, the "pap" module should # be used instead of the "unix" module. The "unix" module should # be used for authentication ONLY for compatibility with legacy # FreeRADIUS configurations. # unix # Uncomment it if you want to use ldap for authentication # # Note that this means "check plain-text password against # the ldap database", which means that EAP won't work, # as it does not supply a plain-text password. # Auth-Type LDAP { # ldap # } # # Allow EAP authentication. eap # # The older configurations sent a number of attributes in # Access-Challenge packets, which wasn't strictly correct. # If you want to filter out these attributes, uncomment # the following lines. # # Auth-Type eap { # eap { # handled = 1 # } # if (handled && (Response-Packet-Type == Access-Challenge)) { # # handled # override the "updated" code from attr_filter # } # } } Uncomment the line with "pam" so it should look like this: # Pluggable Authentication Modules. pam Configure PAM PAM must be configured to use the local Unix password in combination with the Google Authenticator password. Edit /etc/pam.d/radiusd Currently the file should look like this: # # /etc/pam.d/radiusd - PAM configuration for FreeRADIUS # # We fall back to the system default in /etc/pam.d/common-* # @include common-auth @include common-account @include common-password @include common-session We need to comment out all the lines that start with @ and then add the following code: auth requisite forward_pass auth required use_first_pass So in the end, the file should look like this: # # /etc/pam.d/radiusd - PAM configuration for FreeRADIUS # # We fall back to the system default in /etc/pam.d/common-* # #@include common-auth #@include common-account #@include common-password #@include common-session auth requisite forward_pass auth required use_first_pass Setup a local test user adduser test choose a easy password to remember, for this example I used "test123" cd /home/test/ su test google-authenticator If everything worked right you should see something that looks like this: test@FreeDualFactor:~$ google-authenticator Do you want authentication tokens to be time-based (y/n) y|0&cht=qr&chl=otpauth://totp/test@FreeDualFactor%3Fsecret%3DXQH7L6A7W6OK3JYS Your new secret key is: XQH7L6A7W6OK3JYS Your verification code is 158428 Your emergency scratch codes are: 60469537 95985887 80580778 52386153 52951956 Do you want me to update your "/home/test/.google_authenticator" file (y/n) y 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 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 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 You can configure yours how you see fit. Scan once you get your custom QR Code, scan it in to your Google Authenticator App. The App should generate a rotating code that you can use for your dual factor authentication. Test your Configuration service freeradius restart You will want to use a command called "radtest" to test your configuration. radtest localhost 18120 testing123 testing123 is a default secret for the localhost client, used for testing purposes. You can find this in /etc/freeradius/clients.conf so since my password is "test123" and the current google authenticator key is "696720" my test looks like this: radtest test test123696720 localhost 18120 testing123 If it works right, you should get something like this: root@FreeDualFactor:~# radtest test test123696720 localhost 18120 testing123 Sending Access-Request of id 154 to port 1812 User-Name = "test" User-Password = "test123696720" NAS-IP-Address = NAS-Port = 18120 rad_recv: Access-Accept packet from host port 1812, id=154, length=20 Debugging if for some strange reason it doesn't work. You can stop freeradius and start it up in debugging mode like this: service freeradius stop freeradius -XXX Good luck, and hopefully this works as well for you as it has for me.