Adding Two-Factor Authentication to FreeRADIUS

In my previous post, I talked about enabling two-factor authentication (2FA) for my public facing Linux host. In today’s post, I will talk about integrating Google Authenticator PAM to FreeRADIUS. As a result, any hosts that are pointed to my RADIUS server will have the 2FA functionality.

Update: Migrated FreeRADIUS with Google Authenticator to a Docker container
Update: FreeRADIUS 3.0 with Two-Factor Authentication (2FA)

Installing FreeRADIUS and Google Authenticator PAM

While there are several RADIUS software out there, FreeRADIUS is one of the most popular RADIUS software of choice in Linux. Since it has PAM library, this is also perfect for integrating it with Google Authenticator PAM. If you want to know more about FreeRADIUS, you might want to check this book out. I have not read it so read through the reviews to see if that will work for your needs.

Installing FreeRADIUS and Google Authenticator on Ubuntu 16.04 is very easy. All we need is to issue one line command. I added NTP package here since my Google Authenticator configuration is TOTP based. If one went through the Ubuntu installation properly, there might not be a need for this so long as the system is syncing to the time correctly.

$ sudo apt-get install freeradius libpam-google-authenticator -y

Configuring FreeRADIUS

After the package installation, the next step is to set up FreeRADIUS by editing configuration files. There are four config files we need to edit to complete this setup. By no means, one needs to follow the order.

First config file

The first config file that we need to edit is the /etc/freeradius/radiusd.conf file. There are two ways in configuring this and it seems that the most popular option is the one with FreeRADIUS running as root. For some people, this is not acceptable so I included instructions below where we’ll leave it as the default configuration.

Option 1 – Run as root

According to my limited research, the need to change the user and group to root is because of how both FreeRADIUS and Google Authenticator PAM works. My observation seems to indicate that FreeRADIUS will also need to access the secret key (.google_authenticator) in each user’s home directory – I could be totally wrong with this. My Linux boxes have encrypted home directories so only the owner and root can access these. That said, letting FreeRADIUS run as root will have access to the necessary files.

$ sudo vi /etc/freeradius/radiusd.conf

We’ll now need to find the lines user = and group =. The default configuration is set to freerad. Change both of them to root.

user = root
group = root

Option 2 – Use default configuration

As mentioned, we can just leave the file as default. I will explain more about this once we get to the section where we need to edit the /etc/pam.d/radiusd file.

Second config file

The the next config file that we need to edit is the /etc/freeradius/users file. This file will instruct FreeRADIUS to use PAM libraries to authenticate users as the default.

$ sudo vi /etc/freeradius/users

Add the lines found below. I usually like to add lines at the end of the file. Add the line after all the commented text of the file, just before the DEFAULT Framed Protocol == PPP line. This will ensure that this line will take precedence. I found out the hard way when I was troubleshooting an issue with L2TP over IPsec authentication.

# Instruct FreeRADIUS to use PAM to authenticate users
DEFAULT Auth-Type := PAM

Third config file

The second to the last config file on our list to be edited is the /etc/freeradius/sites-enabled/default file. This file tells FreeRADIUS to enable PAM authentication. We just need to edit one line here.

$ sudo vi /etc/freeradius/sites-enabled/default

Once the file is open, look for the following lines:

        #  Pluggable Authentication Modules.
#        pam

We now need to uncomment the pam line to enable it. It should look like this now:

        #  Pluggable Authentication Modules.

Fourth config file

Finally, the last FreeRADIUS config file that we need to change is the /etc/freeradius/clients.conf. This is where we can set up our secret key that is used by the clients to connect to the RADIUS server. Please change the default secret key to random alphanumeric characters. Use a key generator to generate the secret to make things life a little easier. For demo purposes, I will be using the default secret. To change the secret, look for secret = testing123 line.

$ sudo vi /etc/freeradius/clients.conf
<-- Output omitted for brevity -->
secret          = my_super_awesome_strong_secret

client rtr {
	ipaddr =
	secret = my_super_awesome_strong_secret 

As usual in Linux, when a configuration file has been changed, then the service needs to be restarted for the changes to take effect. To restart FreeRADIUS daemon, issue the sudo service freeradius restart command.

Configuring FreeRADIUS PAM

Since we instructed FreeRADIUS to use PAM to authenticate users, we need to configure the /etc/pam.d/radiusd file and instruct it to integrate Google Authenticator PAM. By default, the file will look something like this:

<-- Output omitted for brevity -->
@include common-auth
@include common-account
@include common-password
@include common-session

Option 1

If you picked the first option in the FreeRADIUS configuration section, then you need to comment those four lines above and add two lines. The file should look like this:

#@include common-auth
#@include common-account
#@include common-password
#@include common-session
auth requisite forward_pass
auth required use_first_pass

Option 2

If you left the /etc/freeradius/radiusd.conf file alone, then it becomes a little bit more complicated setup. Also, you will notice that my instructions are what I will consider a workaround to AppArmor (I am guessing this is the real issue). You will see why later in the next section, after the generating Google Authenticator secret key. Anyway, the /etc/pam.d/radiusd file should look like this:

#@include common-auth
#@include common-account
#@include common-password
#@include common-session
auth requisite forward_pass secret=/etc/freeradius/${USER}/.google_authenticator user=freerad
auth required use_first_pass

Google Authenticator Secret Key

I’ve already covered the generation of the secret key in my previous post, so look for the generating Google Authenticator secret key section. Once you are done generating secret keys, come back to this page. If you picked the first option throughout this tutorial, then skip this section and go to the verification section.

If you picked the second option, then we’ll need to do additional steps to make this work. Again, you do not have to follow the order in which they are listed here.

We first need to create a directory equal to the user account that we’re working on. In this scenario, we’ll use user account named test.

$ sudo mkdir /etc/freeradius/test

Then, we need to change the owner of the directory that we just created.

$ sudo chown freerad:freerad /etc/freeradius/test

The second to the last step is to copy the secret key to the directory that we just created.

$ sudo cp .google_authenticator /etc/freeradius/test/.google_authenticator

Finally, we need to change the owner of the file.

$ sudo chown freerad:freerad /etc/freeradius/test/.google_authenticator

If I ever learn more about AppArmor, then I will update this blog post because I think this is the real issue why it’s failing. I did try creating an AppArmor profile, but testing shows that I was still failing. When I looked at the /var/log/auth.log file, I saw an error message that looked like this:

Oct 10 21:24:53 radius radiusd(pam_google_authenticator)[18433]: Failed to update secret file "/etc/freeradius/test/.google_authenticator"


We now need to test to make sure that we can successfully authenticate. FreeRADIUS software package includes a simple tool that we can use to directly query the daemon with requests. The command format is radtest test <password+google authenticator token> localhost 18120 <RADIUS secret key>. The password and Google Authenticator token should not have space in between. Below shows the syntax that I used to test my configuration and the test result.

$ radtest test testing1234803732 localhost 18120 testing123
Sending Access-Request of id 79 to port 1812
	User-Name = "test"
	User-Password = "testing1234803732"
	NAS-IP-Address =
	NAS-Port = 18120
	Message-Authenticator = 0x00000000000000000000000000000000
rad_recv: Access-Accept packet from host port 1812, id=79, length=20

Final Words

I’d like to speculate that a lot of people would pick the first option since the second option involves a lot more steps. Also, this isn’t a scalable solution so I think it will turn off quite a number of people. Imagine doing the steps above for multiple accounts. It’s going to be a lot of administrative work. For my purpose, this is a perfectly acceptable solution because I only have one account in my RADIUS server. It is probably an overkill to be creating a FreeRADIUS server instance for home use, but some may argue that nothing is overkill in security. The more secure you are, the better.

You might also like to read

FreeRADIUS 3.0 with Two-Factor Authentication
Securing SSH with Google Authenticator

Migrated FreeRADIUS with Google Authenticator to a Docker container
Adding Two-Factor Authentication to TACACS+


Integrating Google Authenticator PAM module with FreeRADIUS Server
Freeradius and Google Authenticator
GitHub Google Authenticator

Disclosure is a participant in the Amazon Services LLC Associates Program, an affiliate advertising program designed to provide a means for sites to earn advertising fees by advertising and linking to

About Andrew Roderos

As an IT professional, I have a strong passion for technology and a desire to learn more about it. Technologies that I am mostly interested in are computer networking, network security, virtualization, and programming. Outside of the information technology world, I enjoy traveling, reading science fiction books and manga, watching movies, and photography.