Sunday, April 07, 2013

Setting up WPA2-Enterprise + AES with Ubuntu 12.04.2 + FreeRADIUS with EAP-TLS only (The Definitive Guide)

After spending a LOT of time researching, waiting, more researching, and almost giving up on WiFi, I've finally figured out a secure enough WiFi setup that I think has a pretty good chance of standing up to scrutiny. It is called EAP-TLS and it is serious Kung Fu (aka REAL security). Unfortunately, to implement said Kung Fu, the protocol requires a RADIUS server. And, to get said RADIUS server at an affordable price, FreeRADIUS is needed and therefore Linux is necessary. And the easiest Linux to use is (usually) Ubuntu. But, after scouring the Internets, I've also determined that there are NO good tutorials on setting up a basic FreeRADIUS EAP-TLS system at home under Ubuntu 12.04.2 using the apt-get packages for FreeRADIUS. This, therefore, is the definitive guide mostly cobbled together from a number of different sources.

I'm assuming a half-decent understanding of Linux command-line editing, a fresh installation of Ubuntu Server 12.04.2 LTS on a computer on the network where it will reside (usually plugged into the upstream router), an el-cheapo router with WPA2-Enterprise AES support (some "consumer grade" routers have this baked in), and a willingness to follow directions. If you really want this to work without hassle, I recommend getting an el-cheapo DD-WRT capable router from Newegg and loading DD-WRT onto it (you'll only be out $25). DD-WRT implements RADIUS correctly and the authors actually test it, which is kind of critical. Without further ado...

Make the Ubuntu box have a static IP address instead of being issued via DHCP. Run 'ifconfig' and 'route -n' to obtain the current network setup. Then edit '/etc/network/interfaces':

# The primary network interface
auto eth0
iface eth0 inet dhcp
To become something like:

# The primary network interface
auto eth0
iface eth0 inet static
Your IP address information will be different and based on your 'ifconfig' and 'route -n' output. Be sure to fire up the router admin and locate where to reserve the Ubuntu box's IP address so DHCP doesn't hand it out to some other device on the network in the future and cause havoc. (Or just change the server IP to something else outside the DHCP address range but still in the same subnet - less fuss.)

Next, install FreeRADIUS on the Ubuntu box:

apt-get install freeradius
Next, back up all the files in the /etc/freeradius/ directory in case something goes wrong (makes it easy to restore stuff). The next steps have to more or less be completed before the server will be ready for use, so the ability to completely revert to the original config files is nice.

Next, disable proxies in '/etc/freeradius/radiusd.conf':

proxy_requests  = no
#$INCLUDE proxy.conf
And optionally comment out the "accounting port" in the same file ('/etc/freeradius/radiusd.conf'):

#  This second "listen" section is for listening on the accounting
#  port, too.
#listen {
#       ipaddr = *
##      ipv6addr = ::
#       port = 0
#       type = acct
##      interface = eth0
##      clients = per_socket_clients
That closes at least one open port on the FreeRADIUS server that isn't usually necessary, especially for smaller networks.

Next, disable the default client in '/etc/freeradius/clients.conf':

#client localhost {
#       ipaddr =
#       secret          = testing123
#       require_message_authenticator = no
#       nastype     = other     # localhost isn't usually a NAS...
Set up a new client in '/etc/freeradius/clients.conf':

client your_router_name {
       ipaddr = your_routers_ip_address
       secret = random_string
       require_message_authenticator = yes
The client entry is really custom for your environment and you can have more than one. The IP address, within the average home network, is the gateway IP address of the Ubuntu box. See the default client that was just commented out for details on various options. For 'secret' and other strings that should be random, use a good tool or the random string generator. Note that your router might have hard character limits (e.g. 32 bytes) while FreeRADIUS will accept just about anything.

Next, edit '/etc/freeradius/eap.conf'. Comment out all subsections inside the 'eap' block except for 'tls'. The following lines need to be changed from this:

eap {
  default_eap_type = md5
  # Supported EAP-types
  md5 {
  leap {
  gtc {
  ttls {
  peap {
  tls {
    cipher_list = "DEFAULT"
    make_cert_command = "${certdir}/bootstrap"
    verify {
#      tmpdir = /tmp/radiusd
#      client = "/usr/bin/openssl verify -CApath ${..CA_path} %{TLS-Client-Cert-Filename}"
  mschapv2 {
Into something more like this:

eap {
  default_eap_type = tls
  # Supported EAP-types
#  md5 {
#  }
#  leap {
#  }
#  gtc {
#  }
#  ttls {
#  ...
#  }
#  peap {
#  }
  tls {
    cipher_list = "HIGH"
#    make_cert_command = "${certdir}/bootstrap"
    verify {
      tmpdir = /var/tmp/radiusd
      client = "/usr/bin/openssl verify -CApath ${..CA_path} %{TLS-Client-Cert-Filename}"
#  mschapv2 {
#  }
Options not mentioned are to be left alone (except new protocols). The CA private key password will be used later when the CA is set up. The above mostly just disables EAP-MD5, LEAP, EAP-GTC, EAP-TTLS, PEAP, and EAP-MSCHAPv2 (all of those are broken, weak, a bad idea, or a combination of those words), commenting out the bootstrap line (as documented in the config), and setting the allowed ciphers to something rational/sane. Only EAP-TLS is truly secure and all major OSes (including Windows) support it.

Next, create '/var/tmp/radiusd' as root:

mkdir /var/tmp/radiusd
chown freerad /var/tmp/radiusd
chgrp freerad /var/tmp/radiusd
chmod 700 /var/tmp/radiusd
Next, disable the default virtual servers:

cd /etc/freeradius/sites-enabled/
rm *
Next, copy the 'default' virtual server and edit the new file (change 'your_ap_name' to the router's SSID or something similar):

cd /etc/freeradius/sites-available/
cp default your_ap_name_default
vim your_ap_name_default
In the new file, comment out everything except 'preprocess', 'eap', 'expiration', and 'logintime' from the 'authorize' section. Comment out everything but 'eap' from the 'authenticate' section. Comment or delete the 'accounting' sections if you commented out the accounting port earlier.

Next, enable the new default file:

cd /etc/freeradius/sites-enabled/
ln -s ../sites-available/your_ap_name_default your_ap_name_default
Next, stop the currently running FreeRADIUS server and run FreeRADIUS in debug mode:

/etc/init.d/freeradius stop
freeradius -X
If all goes well, you should have an enabled EAP-TLS only FreeRADIUS installation with just port 1812 open, a client that is your router (access point), and the sentence "Ready to process requests" on the screen. If you get an error message, then try to fix it (paste error messages into Google searches). Once it works, press Ctrl-C to exit the server. At this point, you can breathe a sigh of relief because the only thing left is to generate SSL certificates.

Next, if you don't have it installed, install 'make':

apt-get install make
Next, remove the default certificates:

cd /etc/freeradius/certs/
rm *.pem
rm *.key
Next, copy the tools to set up FreeRADIUS certificates to a reasonable directory:

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 *
Next, clean up any previous attempts and initialize the required baseline files:

make destroycerts
make index.txt
make serial
Next, edit 'ca.cnf' and set various options to your heart's content. At the very least, change: 'md5' to 'sha1', up the 'default_bits' from 2048 to at most 4096, and 'default_days' to something longer than 365 days (unless you want to change all certificates every year). (I tried 8192 for 'default_bits', but, even though the year is 2013, I ran into TLS handshake issues - rebuilding all the certs and using 4096 bits fixed the handshake problems.) Keep various text strings anonymous. Change 'output_password' to a random string ('input_password' does not appear to be used by the Makefile but it doesn't hurt to make both passwords the same). You can always recreate the CA later if you don't like some setting.

Next, generate 'ca.pem':

make ca.pem
make ca.der
make printca
If you get an error while generating 'ca.pem', there are a number of things that could have gone wrong, but it is generally best to start over with the 'make destroycerts' command, edit 'ca.cnf' after searching Google for hints, and try again. The last command lets you confirm that the CA cert contains the information you want at the strength you want.

Next, edit 'server.cnf' and repeat the editing process.

Next, generate 'server.pem':

make server.pem
Next, edit 'client.cnf' and repeat the editing process. But this time only issue the client a cert for the default 365 days and emailAddress and commonName should be the e-mail address of the user who will use it. (If you don't want to use e-mail addresses, edit 'Makefile' and locate the 'USER_NAME' field and change " | grep '@'" to " | grep -v optional" or all files will end up named '.pem'). The official README says to use an e-mail address, but it can be anything that can also be a valid filename. Note that the user is going to have to manually enter the password used in 'output_password' to add the certificate to their certificate store. Best to keep it simple.

Actually, edit the 'Makefile' anyway (vim Makefile). Locate:

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:

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
Make sure indented lines are 'tabs' and not 'spaces'. This copies the PKCS#12 file too (why should PEM files have all the fun), which is necessary for some OSes and adds a special case for Android, which bundles the CA certificate with the PKCS#12 file and gives it a "friendly name". Next, generate the client certificate, private key, and everything else needed:

make client.pem
make client_android.p12
You can generate as many client certificates as you need. Just open the 'client.cnf' file up and edit a few things before running the above 'make' commands. If you plan on generating lots of certs, you might want to write a script to do the editing and generating.

Assuming everything has gone well to this point, you now have Ubuntu 12.04.2 + FreeRADIUS + EAP-TLS only (with "HIGH" ciphers) and a brand new certificate authority, a server certificate, and a client certificate. But the latter aren't connected (yet). Time to put on the finishing touches:

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
cd /etc/freeradius/
vim eap.conf
Note that you should run the first four lines after generating each client SSL certificate. OpenSSL likes to 'chmod' files as 644.

Locate the line in 'eap.conf' containing 'private_key_password'. This should be changed from "whatever" to the value of 'output_password' from '/var/certs/freeradius/server.cnf'. Save and close. Run 'freeradius -X' and make sure everything still works. Again, Google is your friend for resolving issues.

At this point, the Ubuntu FreeRADIUS server is set up. Come on, it wasn't THAT bad (plus I did all the digging around the configs for you). The only missing piece is your router configuration. Each router is different, but dig around the settings for a while to locate a way to switch to WPA2-Enterprise + AES (don't use Wifi Protected Setup or WPS or whatever "automatic configuration" setup your router offers). A few extra fields will show up. Plug in your Ubuntu server's static IP address and the shared secret from '/etc/freeradius/clients.conf'.

Hooray! The router and RADIUS parts are done. Time to move onto the first client. Fire up 'freeradius -X' during testing so you can debug issues more easily. Extract the client certificate (the .p12 and .pem files) from the server as well as the 'ca.der' and 'ca.pem' certificates. Install the CA cert on the client. Then install the client certificate. This might take some trial and error depending on all sorts of factors - you might have to rename the file extension to get the OS to recognize the certificates. Searching Google for "[OS name] install certificate" usually turns up relevant results. However, there are "recipes" below that you might want to try (and if you come up with a good client recipe, write it down in the comments). After that, try to connect to the WiFi router using the newly installed certificate. Both errors and successful connections will show up in the FreeRADIUS output. However, once it works, you've got the most secure WiFi on the block!

Once you are satisfied with the setup, you can terminate the command-line debug mode and do the '/etc/init.d/freeradius start' thing so that FreeRADIUS runs as a background service.


For Windows: Use 'ca.der' and '(name).p12'. Double-click the 'ca.der' file. Click "Install Certificate..." Click "Next". Select "Place all certificates in the following store" and select "Trusted Root Certification Authorities" from the dialog. Click "Next". Click "Finish". A dialog will appear warning about the certificate, click "Yes". A dialog saying "The import was successful" will appear. Click "OK". Double-click on the '(name).p12' file. Click "Next". Click "Next". Enter the password. Click "Next". Click "Next". Click "Finish". A dialog saying "The import was successful" will appear. Next, add the WiFi network manually for WPA2-Enterprise + AES and then somewhere in the settings for the connection is an option for "Network authentication method" that defaults to PEAP. Change it to "Smart Card or other certificate". Open the "Settings" dialog. Use the "Use a certificate on this computer" option and the "Use simple certificate selection (Recommended)" option. Check the imported CA cert in the "Trusted Root Certification Authorities" box. Click "OK". Now it is possible to try to connect normally. If all goes well, the connection will be established.

For Linux (Ubuntu GUI): Use 'ca.pem' and '(name).p12'. The GUI acts a little weird because it first asks for the client public key under the WPA2 Enterprise + TLS setup. Skip that field. The 'ca.pem' file goes into the CA certificate field. Put the '(name).p12' into the private key field and type in the password. The 'Connect' button becomes available when the correct information is in the right fields.

For Android: Use '(name)_android.p12'. First get the certificate on the device by putting the certificate in the root directory of the SD card. Then, go into Settings -> Security -> Credential Storage -> Install from storage. Enter the password. A dialog will appear offering to install three things (CA public cert, client public cert, and client private key). Next go into WiFi settings and connect to the network using 802.1x EAP, the EAP method as TLS, no Phase 2 auth, use the same certificate for both CA and user certificates (will probably be the only option in the dropdown), supply the same string for the Identity and Anonymous Identity you want the device to use, and don't use a password.


If you need help, offer a geek/nerd a free dinner in exchange for their help.

Connecting just two devices together flawlessly is apparently too difficult for the software/hardware industry to figure out most of the time (and they like to abuse the "finger of blame" rather than fix the problems that exist). So imagine the complexity of three devices: Your computer/tablet/phone (multiple OSes) + your router (multiple OS flavors of varying levels of quality of feature support) + your Ubuntu box (with FreeRADIUS having a zillion configuration options). When something goes wrong, it will go spectacularly wrong and be nearly impossible to diagnose. A connection that constantly drops could be bad hardware, too many WiFi devices in range on the same channel, and a zillion other factors. Or, in my case, my first router simply couldn't handle RADIUS packets correctly - out of hundreds and hundreds of attempts by the client, it only passed along packets to the Ubuntu box maybe three times which sent FreeRADIUS into fits and spasms of "got connection, dropping connection, got connection, dropping connection, error, got connection, error, error, error". I have no idea what it was doing wrong. I found a cheap $25 router that supported DD-WRT, put DD-WRT on it, and FreeRADIUS was MUCH more reliable. Only then was I able to diagnose that the issue was with using 8192 bit certificates.

The point is that this stuff is (unnecessarily) complicated to debug. You need to have friends who are serious nerds and/or be one yourself should you run into issues and the fact is, even though this guide exists to get a specific setup working, there's a pretty good chance it won't work at all. So you have to be serious about getting this to work. If you need a nerd's help, make them a meal. Free food is decent compensation, especially if they are the slightest bit interested in a crazy EAP-TLS installation.

(Plus tons of digging around the configuration files for myself.)

Other thoughts: This makes for a nice multi-weekend project. Some people do woodworking on weekends. Others wrestle with Linux.