Using a RADIUS Server on Ubuntu 14.04 for WIFI Authentication

Using a RADIUS Server on Ubuntu 14.04 for WIFI Authentication

Updated Feb 2016 - Changes in "openssl verify" required pointing at the CAfile instead of the CApath.

I've also released a new version of this document targeting Freeradius 3.x. The package provided with Ubuntu 14.04 has many bugs, so try out the Freeradius 3.x version instead.

Let's be honest. Your wifi password is probably something pretty simple. When you have to both be able to verbally convey the password to new users and type it in on a mobile device, it's not like the password can be 25 random characters including special characters, can it?

To have real security, you should be using a pre-shared encryption certificate. This moves the security from "something you know", aka your password, to "something you have", an encryption certificate file. Most modern Wireless Access Points (WAP) support WPA2-Enterprise which expects that you are running a RADIUS server for authenticating clients.

Now there's many ways that a RADIUS server can be configured but I'm going to suggest one that generates signed certificates for users that then must be installed on their devices. Other RADIUS implementations allow you to store a unique user login in an LDAP database backend for instance, but rather than providing attackers a huge list of possible passwords to crack we want to eliminate the password as a possible login method entirely.

RADIUS is an acronym for "Remote Authentication Dial In User Service." You may be able to tell by the name that it's not a new technology - the last time I used a dial-in connection was probably 14 years ago. Fortunately for us this means that RADIUS is a well established and tested standard and because it's also flexible, we can now use it for authenticating users in many different scenarios, including WIFI.

A few pre-requisites I'm going to assume

I'm going to assume that you have a WAP that supports WPA2-Enterprise.

Your WAP must be configured with a static IP address.

You also need a server with a static IP address running Ubuntu 14.04 that you intend to run a RADIUS server on.

You have a basic knowledge of administering Linux and Ubuntu servers already.

Install FreeRADIUS

First, let's install the RADIUS server, FreeRADIUS. On the server that is going to host it do:

sudo apt-get install freeradius make

There are many different ways that FreeRADIUS can be configured, and honestly I don't understand most of them. Here's what I found that worked for me.

Turn off the proxy feature on the server (unless you know you need it) by editing /etc/freeradius/radiusd.conf and changing the following line:

proxy_requests = no

You may also want to look over the logging features in that configuration file to set what gets logged and where. I use "auth=yes" in the log{} block so that I log every time someone connects to the wifi. It also tells me which access point they connected to.

What is a Radius client?

FreeRADIUS may throw you for a loop talking about servers and clients. The client is NOT what you think - it's not the user's laptop or phone. The client is the WAP, because it performs the authentication request against the server. By default, FreeRADIUS will set up the localhost of the server as a client as well, and we won't be needing that so let's disable it in the '/etc/freeradius/clients.conf' file by commenting it out.

#client localhost {
        #  Allowed values are:
        #       dotted quad (1.2.3.4)
        #       hostname    (radius.example.com)
#       ipaddr = 127.0.0.1

        #  OR, you can use an IPv6 address, but not both
        #  at the same time.
#       ipv6addr = ::   # any.  ::1 == localhost
...
#}

And now we add an entry for our WAP in that. Create a new random password that you'll enter into the WAP itself that it uses to authenticate against the RADIUS server. Again in the '/etc/freeradius/clients.conf' file:

client mywap {
        ipaddr = 192.168.1.100
        secret = myRandomP@55w05D
        require_message_authenticator = yes
}

Please be sure you've changed the parts I put in bold. You will need a client entry for each WAP that is on your network. I recommend a different password for each one, in addition to needing the static IP address of the WAP.

EAP Configuration

Next we want to edit the configuration for the Extensible Authentication Protocol (EAP). Rather than telling you what to comment, I'm going to just show you what you need. Edit '/etc/freeradius/eap.conf' to look something like this:

# -*- text -*-
# Example /etc/freeradius/eap.conf file
eap {
	default_eap_type = tls
	timer_expire = 60
	ignore_unknown_eap_types = no
	cisco_accounting_username_bug = no
	max_sessions = 4096
	tls {
		certdir = ${confdir}/certs
                cadir = ${confdir}/certs
                private_key_password = myserverkeypassword
                private_key_file = ${certdir}/server.key
		certificate_file = ${certdir}/server.pem
		CA_path = ${cadir}
		CA_file = ${cadir}/ca.pem
		dh_file = ${certdir}/dh
		random_file = /dev/urandom
		cipher_list = "HIGH"
		make_cert_command = "${certdir}/bootstrap"
		ecdh_curve = "prime256v1"
		cache {
			enable = no # Optionally enable
			lifetime = 24 # hours
			max_entries = 255
		}
		verify {
			tmpdir = /tmp/radiusd
			client = "/usr/bin/openssl verify -CAfile ${..CA_file} %{TLS-Client-Cert-Filename}"
		}
		ocsp {
			enable = no # optionally enable
			override_cert_url = yes
			url = "http://127.0.0.1/ocsp/"
		}
	}
	ttls {
		default_eap_type = md5
		copy_request_to_tunnel = no
		use_tunneled_reply = no
		virtual_server = "inner-tunnel"
	}
}

The "myserverkeypassword" above will need to match the password you use when generating the server's keys later.

What we've done mostly is disable other protocols like LEAP and PEAP and MSCHAPv2 among others, none of which are good ideas to use. Only EAP-TLS will be used.

Disable all the default servers

sudo rm /etc/freeradius/sites-enabled/*

Create a new server config file at /etc/freeradius/sites-available/mynetwork that contains something like the following:

######################################################################
authorize {
	preprocess
	eap {
		ok = return
	}
	expiration
	logintime
}

authenticate {
	eap
}

preacct {
	preprocess
	acct_unique	
	suffix
	files
}

accounting {
	detail
	unix
	radutmp
	exec
	attr_filter.accounting_response
}

session {
	radutmp
}

post-auth {
	exec
	Post-Auth-Type REJECT {
		attr_filter.access_reject
	}
}

pre-proxy {

}

post-proxy {
	eap
}

Now, link that to the sites-enabled directory like so:

sudo su -
cd /etc/freeradius/sites-enabled/
ln -s ../sites-available/mynetwork ./mynetwork

And then stop FreeRADIUS and restart it in debug mode to make sure all is loading correctly:

service freeradius stop
freeradius -X

Hopefully it starts up just fine without any errors. You should see "Ready to process requests". Any error messages you see now is the time to do some research and find out why. Once you've finished the test type CTRL-C to stop the server.

Configure SSL Certificates

Assuming all went well, now we start generating SSL certificates. First we want to remove the default certificates and set up a basic framework:

cd /etc/freeradius/certs/
rm *.pem
rm *.key
mkdir /var/certs
mkdir /var/certs/freeradius
chgrp ssl-cert /var/certs/freeradius
chmod 710 /var/certs/freeradius
cp /usr/share/doc/freeradius/examples/certs/* /var/certs/freeradius/
cd /var/certs/freeradius/
rm bootstrap
chmod 600 *
make destroycerts
make index.txt
make serial

Next, edit the 'ca.cnf' file and change a few of the defaults. Change the following lines:

[ CA_default ]
..
default_days = 1825
default_md = sha1
..
[ req ]
default_bits = 4096
input_password = myserverkeypassword
output_password = myserverkeypassword
..

That sets certificates good for 5 years (you can change the days to whatever you like) and increases the certificate security considerably from the defaults. The "myserverkeypassword" above should match the one in the eap.conf file at the beginning and should be a randomly generated string. You'll never have to enter this in a client so make it complicated.

Now, generate the 'ca.pem' file:

make ca.pem
make ca.der
make printca

Now, edit the "server.cnf" file and do similar changes:

[ CA_default ]
..
default_days = 1825
default_md = sha1
..
[ req ]
..
default_bits = 4096
..

Under the "[server]" tag put in your appropriate contact information. Now generate the 'server.pem' file:

make server.pem

Create User Certificates

Now we generate client certificates, but there's something we need to change in the Makefile first. "nano Makefile".

Locate the lines that say:

client.p12: client.crt
  openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12  -passin pass:$(PASSWORD_CLIENT) -passout pass:$(PASSWORD_CLIENT)

client.pem: client.p12
  openssl pkcs12 -in client.p12 -out client.pem -passin pass:$(PASSWORD_CLIENT) -passout pass:$(PASSWORD_CLIENT)
  cp client.pem $(USER_NAME).pem

Change it to read:

client.p12: client.crt
        openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12  -passin pass:$(PASSWORD_CLIENT) -passout pass:$(PASSWORD_CLIENT)
        cp client.p12 $(USER_NAME).p12

client.pem: client.p12
        openssl pkcs12 -in client.p12 -out client.pem -passin pass:$(PASSWORD_CLIENT) -passout pass:$(PASSWORD_CLIENT)
        cp client.pem $(USER_NAME).pem

client_android.p12: client.crt
        openssl pkcs12 -export -in client.crt -inkey client.key -certfile ca.pem -name "$(USER_NAME)" -out client_android.p12  -passin pass:$(PASSWORD_CLIENT) -passout pass:$(PASSWORD_CLIENT)
        cp client_android.p12 $(USER_NAME)_android.p12

Note that the above lines will wrap in your browser but if you copy and paste them they should be formatted correctly.

Make sure indented lines are tabs and not spaces or the file will not work. This change creates a special case for Android certificates and renames the files to be easier to identify.

Edit 'client.cnf' to set your defaults just like you did before but this time for any client certificates. You probably want to shorten the default_days to 365 (will need to regenerate keys for the device in a year) but again change the default_md to sha1 and default_bits to 4096.

In the "[ req ]" section of client.cnf is the "input_password" and "output_password". Set these both to the same, and keep in mind that this password will be needed when the certificate is installed on the client so keep in mind using mobile keyboards to type it. The "[client]" section uniquely identifies the user in log files, so be sure the emailAddress and commonName are set properly.

Now create the client certificate with:

make client.pem
make client_android.p12

Simply edit the client.cnf file for the next certificate and run those commands again to generate certificates for each device that will have WIFI access.

Finally, lets set the proper permissions for certificates and create the links we need:

chmod 600 *
chmod 640 ca.pem
chmod 640 server.pem
chmod 640 server.key
chgrp ssl-cert ca.pem
chgrp ssl-cert server.pem
chgrp ssl-cert server.key
cd /etc/freeradius/certs/
ln -s /var/certs/freeradius/ca.pem ca.pem
ln -s /var/certs/freeradius/server.pem server.pem
ln -s /var/certs/freeradius/server.key server.key

After creating new client certificates, the first four lines of the above command should be run again to ensure the security is set properly.

The Right Certificates in the Right Places

Here's the files needed:

  • Windows: ca.der and [user].p12
  • Linux: ca.pem and [user].p12
  • Android: [user]_android.p12

Note that recent versions of Android will constantly display a warning that your connection is monitored while using wifi with a RADIUS server. There are ways on a rooted device to install the certificate in the root store, but regardless it doesn't actually affect the operation of the device.

A lot of this method was sourced and distilled down from an older article on Ubuntu 12.04 by Thomas Hruska.
Posted by Tony on Mar 27, 2015 | Servers, Network Security