1. Preliminary Steps
  2. Initialize Certificates
  3. Create the CRL
  4. Edit the Configuration Files
  5. Create a Client Certificate
  6. Revoke a Client Certificate
  7. Monitoring and Debugging
  8. Backup & Restore
  9. Resources

The following steps show how to get a RADIUS server running in order to use WPA2/WPA3 Enterprise security with EAP-TLS authentication on a home wireless network. A Raspberry Pi 4 running Raspberry Pi OS (bullseye) was used running FreeRADIUS 3.0.x, but the steps should be similar for any Debian-based Linux system. You can either SSH into the system or log in directly and open a terminal for most of these steps.


Preliminary Steps


These steps are specific to a Raspberry Pi 4 and help to make the system more secure. This system should not be used for anything other than running freeradius. Connect it directly to the LAN via an ethernet port and assign it a static IP address. It can even be placed on an isolated VLAN so that it is only accessible to the router - and perhaps a machine that needs ssh access.


  • Disable Bluetooth and Wi-Fi in /boot/config.txt

    dtoverlay=disable-bt
    dtoverlay=disable-wifi
    
  • Stop wpa_supplicant (this is used for wifi)

    sudo systemctl disable --now wpa_supplicant
    
  • Use https for software updates

    • Edit /etc/apt/sources.list - switch to a mirror that supports https (this is only an example, use any one you like)
      deb https://raspbian.freemirror.org/raspbian/ bullseye main contrib non-free rpi
      
    • Edit /etc/apt/sources.list.d/raspi.list - switch archive.raspberrypi.org over to https
      deb https://archive.raspberrypi.org/debian/ bullseye main
      
  • Update all of the packages (this should also be done periodically)

    sudo apt update
    sudo apt full-upgrade
    
  • Install and enable a firewall - this blocks all ports by default (we'll open some for freeradius later on)

    sudo apt install ufw
    sudo ufw enable
    
  • Make sudo require a password

    sudo visudo /etc/sudoers.d/010_pi-nopasswd
    

    Update NOPASSWD to PASSWD so it looks like the following

    pi ALL=(ALL) PASSWD: ALL
    
  • Remove / disable unnecessary networking services

    • avahi (mDNS) - a Zeroconf implementation similar to "Bonjour"
    • cups (printing services)
    sudo apt purge avahi-daemon
    sudo apt purge cups cups-daemon
    sudo apt autoremove
    sudo systemctl disable --now cups-browsed
    sudo systemctl disable --now cups.path
    sudo systemctl disable --now cups.socket
    sudo systemctl disable --now cups
    
  • Reboot

    sudo reboot
    
  • Install freeradius

    sudo apt install freeradius
    
  • Disable IPv6 unless you really need it for some reason

    NOTE: Once this change is made and you reboot, freeradius won't start until you have removed the ipv6 listens from the configuration.

    Add the following to end of the line in /boot/cmdline.txt

    ipv6.disable=1
    
  • Enable SSH - optional

    • Edit /etc/ssh/sshd_config
    • Start the ssh server and allow incoming ssh connections to pass through the firewall
      sudo systemctl enable --now ssh
      
      # x.x.x.x is your LAN address
      # <port_number> is the port you specified above in /etc/ssh/sshd_config
      sudo ufw allow from x.x.x.x/24 to any port <port_number> proto tcp
      
    • Copy over an ssh key (for example with ssh-copy-id) from the machine you will be connecting from
      ssh-keygen -t ed25519
      # x.x.x.x is the ip address of the radius server
      # <port_number> is the port you selected above
      ssh-copy-id pi@x.x.x.x -p <port_number>
      
    • Edit /etc/ssh/sshd_config
      • Disallow passwords so only ssh keys can be used to log in
        ChallengeResponseAuthentication no
        PasswordAuthentication no
        

        Leave UsePAM yes - it handles session disconnection better, when you reboot a system for example, and the two updates above are sufficient to disable password access

    • Restart ssh to pick up the authentication changes
      sudo systemctl restart ssh
      

Initialize Certificates


First we'll generate the necessary certificates for EAP-TLS. For some context, view the README file in /etc/freeradius/3.0/certs

Important - Something to be aware of is that the much of the information in these certificates, including the client identity (email address), is visible over the air during the initial TLS handshake. This will be improved when TLS 1.3 is finalized for EAP-TLS. For now, I would advise against putting any personal information in these certificates and using example.com or example.org for email addresses even if you have your own domain. For example: greenbean@example.com



SSH into or open a terminal on the server, get a root shell and go into the certificate directory

sudo su
cd /etc/freeradius/3.0/certs

Edit ca.cnf, server.cnf and client.cnf

  • Change all the passwords and store them somewhere safe
    • Be careful with special characters in passwords, some will cause strange errors
      • These work: .,;_-
      • These do not: $#&=
    • Only the output_password seems to be used, you can use the same value for input_password
    • inner-server.cnf is for the inner-tunnel and will not be used
  • Change default_days and default_crl_days to something large - this is the lifespan of your certificates
    default_days            = 3650
    default_crl_days        = 3650
    
  • Note: The organizationName field needs to be the same in these three files

Edit client.cnf

  • This file will be used to create an initial client certificate, which can be for a system or just used for testing and then revoked - we'll create more later
  • Change the emailAddress and commonName in client.conf to something distinct, but not personal - this will be the initial client's identity
  • Update the information in the [client] section - but don't make it too personal

Edit ca.cnf

  • Remove the crlDistributionPoints lines - this will be done locally
  • Update the information in the [certificate_authority] section - but don't make it too personal

Edit server.cnf

  • Update the information in the [server] section - but don't make it too personal
  • Add the following to the end of the [ req ] section - if it's not there already
    req_extensions          = v3_req
    
  • Add the following to the end of the file - if it's not there already
    [ v3_req ]
    basicConstraints = CA:FALSE
    keyUsage = nonRepudiation, digitalSignature, keyEncipherment
    subjectAltName = @alt_names
    
    #  This should be a host name of the RADIUS server.
    #  Note that the host name is exchanged in EAP *before*
    #  the user machine has network access.  So the host name
    #  here doesn't really have to match anything in DNS.
    [alt_names]
    DNS.1 = radius.example.com
    
    # NAIRealm from RFC 7585
    otherName.0 = 1.3.6.1.5.5.7.8.8;FORMAT:UTF8,UTF8:*.example.com
    

Edit xpextensions

  • Add the following to the end of the [ xpserver_ext ] section - if it's not there already
    subjectAltName = @alt_names
    
  • Add the following to the end of the file - if it's not there already
    [alt_names]
    DNS.1 = radius.example.com
    
    # NAIRealm from RFC 7585
    otherName.0 = 1.3.6.1.5.5.7.8.8;FORMAT:UTF8,UTF8:*.example.com
    

The value of DNS.1 in xpextensions and server.cnf is what you use for the domain when connecting with a client. Right now it seems like the value in xpextensions is what actually gets placed in the server's certificate, though I'd keep them the same in both files in case this changes in the future.


Create the certificates and private keys for the CA (certificate authority), server (freeradius), and client (first wifi client). Make sure you change the owner of the generated files using the chown command shown below - freeradius may not start otherwise!

make
chown freerad:freerad *

Clean up - make sure to execute these commands in this order, as make clean will regenerate passwords.mk

make clean
rm passwords.mk

It's a good idea to remove the password from client.cnf after generating a client certificate, so it's not accidentally re-used.

You can remove the password from server.cnf, but it will need to go in a freeradius configuration file later.

The password for the CA needs to remain in ca.cnf for the make client.pem command to run correctly when generating a client certificate. You can remove it and put it back in before generating a client certificate, but these files are pretty well secured. It's mostly a defense against someone who could physically remove the disk (and has a lot of technical knowledge) from generating their own certificates.

If you do this, or make any other changes to the ca.cnf file after the initial make command has been issued, it will create problems that are very tricky to debug. Basically, the make client.pem command that we will issue later will see that the ca.cnf file has been updated and will regenerate the CA certificate and private key! Afterwards, the new client certificate will not work. This is because it was issued by a different CA, and it's not obvious from the errors in the log.

Fri Oct  1 00:00:00 2021 : ERROR: (99) eap_tls: ERROR: TLS Alert read:fatal:decrypt error
Fri Oct  1 00:00:00 2021 : Auth: (99) Login incorrect (eap_tls: TLS Alert read:fatal:decrypt error): [name@example.com] (from client router port 9 cli 00-00-00-00-00-00)

The following command will make it appear that ca.cnf has not been modified, preventing the issue.

# change the update time of ca.cnf to precede ca.pem so we don't create a new CA
stat -c '%y' ca.pem | xargs -I '{}' touch --date='{} -1 minute' ca.cnf

To start from scratch, effectively invalidating all the certificates you have generated, issue the following command in the /etc/freeradius/3.0/certs directory.

rm -f *.pem *.der *.csr *.crt *.key *.p12 serial* index.txt*

Create the CRL


Creating a Certificate Revocation List will allow you to revoke client certificates later - if a machine is compromised for example.


SSH into or open a terminal on the server, get a root shell and go into the certificate directory

sudo su
cd /etc/freeradius/3.0/certs

Create the CRL configuration

cp ca.cnf crl.cnf

Edit crl.cnf

  • Change the passwords

Create the CRL and merge it with the CA (Certificate Authority)

openssl ca -gencrl -keyfile ca.key -cert ca.pem -out crl.pem -config crl.cnf
# enter the password from the ca.cnf file
cat ca.pem crl.pem > cacrl.pem
chown freerad:freerad *

Edit the Configuration Files


Now we'll configure freeradius to use EAP-TLS and disable all other authentication methods. Full examples of these files are provided here to show what they should basically look like after these changes have been made.


SSH into or open a terminal on the server and get a root shell

sudo su

Edit /etc/freeradius/3.0/sites-enabled/default

  • Comment out the ipv6 listen sections
  • By default freeradius listens on port 1812 (for authentication) and 1813 (for accounting). We can change that in this file for a bit more security. Look for the port entries in the listen sections for both authentication (type = auth) and accounting (type = acct). Initially they are set to 0, which will use the defaults. These can be changed to other values, preferably between 49152 and 65535.
    listen {
           ...
           type = auth
           ...
           port = <some_high_port>
           ...
    }
    ...
    listen {
           ...
           port = <another_high_port>
           ...
           type = acct
           ...
    }
    
  • Comment out the following auth methods, they are located in 2 separate sections: authorize & authenticate
    • pap
    • chap
    • ms-chap
    • digest
  • Comment out any conditional loading of sql: -sql
  • Comment out any conditional loading of ldap: -ldap

The inner tunnel is not needed for EAP-TLS. It's a good idea to disable it to reduce surface area, even though it only listens on localhost.

rm /etc/freeradius/3.0/sites-enabled/inner-tunnel

Remove unused modules from mods-enabled

rm /etc/freeradius/3.0/mods-enabled/pap
rm /etc/freeradius/3.0/mods-enabled/chap
rm /etc/freeradius/3.0/mods-enabled/mschap
rm /etc/freeradius/3.0/mods-enabled/digest
rm /etc/freeradius/3.0/mods-enabled/ntlm_auth  # references mschap

Edit /etc/freeradius/3.0/mods-enabled/eap

  • Delete or comment out all other EAP-types except tls-config and tls
  • Look for and update the following entries in the tls-config section to these values - uncomment them if they are commented out
    default_eap_type = tls
    private_key_password = <output_password in server.cnf>
    private_key_file = ${certdir}/server.key
    certificate_file = ${certdir}/server.crt
    ca_file = ${cadir}/cacrl.pem
    random_file = /dev/random
    check_crl = yes
    cipher_list = "HIGH"
    cipher_server_preference = yes
    ecdh_curve = "secp384r1"
    
  • Uncomment the following line to make sure the identity provided by the client matches the certificate
    check_cert_cn = %{User-Name}
    

Edit /etc/freeradius/3.0/clients.conf

  • The "clients" in this file are the router and any machines being used for testing - these are different from the "clients" that we create certificates for, which are users of the wifi network
  • Add the router - specify the exact ip address (x.x.x.x) and a shared secret (<shared_secret>)
    client router {
       ipaddr          = x.x.x.x
       secret          = <shared-secret>
    }
    
  • Comment out or remove the localhost client (near the top of the file)
  • Test clients should be removed after testing is complete

Edit /etc/freeradius/3.0/radiusd.conf

  • Disable proxy_requests - it opens a random high UDP port and is not needed
    proxy_requests = no
    #$INCLUDE proxy.conf
    
  • Log authentication requests to the log file
    auth = yes
    

Update the firewall rules to allow the router and any test client to connect to freeradius (x.x.x.x is the exact ip address of the router or test machine). Use the port values that you set in /etc/freeradius/3.0/sites-enabled/default earlier.

ufw allow from x.x.x.x to any port <auth_port> proto udp
ufw allow from x.x.x.x to any port <acct_port> proto udp

Restart freeradius

systemctl restart freeradius

This is a good time to reboot the server to make sure everything is working correctly - especially if you disabled ipv6.

reboot

Once the server is running again, you can log back in and make sure freeradius is running.

systemctl status freeradius 
# it should be active

sudo netstat -tulpn | grep freeradius
# you should see the two freeradius ports

sudo ufw status
# you should see rules allowing the router to access the freeradius ports

Now you can use this radius server on your router for a WPA2/WPA3 Enterprise network.


Create a Client Certificate


This is how you create a certificate for each client that will connect to your wireless network.


SSH into or open a terminal on the server, get a root shell and go into the certificate directory

sudo su
cd /etc/freeradius/3.0/certs

Edit client.cnf

  • Update the passwords - use the same value for both and store it somewhere safe, you'll need it on the client machine to read the private key
  • Update the emailAddress
  • Update the commonName to match the emailAddress

Create the output pem file, which contains both the certificate and private key - it will be named after the email address in the client.cnf file.

make client.pem
chown freerad:freerad *

Copy the CA certificate and client pem file somewhere - user/group pi and /home/pi are examples, but are valid.

install -o pi -g pi -m 600 ca.pem /home/pi
install -o pi -g pi -m 600 "name@example.com.pem" /home/pi

For an Android client, copy and rename client.p12 along with the CA certificate instead.

install -o pi -g pi -m 600 ca.pem /home/pi
install -o pi -g pi -m 600 client.p12 "/home/pi/name@example.com.p12"

Clean up

  • Remove extra generated files - make sure to execute these commands in this order, as make clean will regenerate passwords.mk
    make clean
    rm passwords.mk
    
  • The client's pem file should always remain in this directory in case it needs to be revoked
  • Remove the password values from client.cnf
    • This is mostly to avoid accidentally using the same password for another client
    • Replace the values with TODO or something similar as a reminder that they need to be updated

Now you just have to get the CA certificate and client pem (or p12) file to the client machine from /home/pi somehow. There are many different ways to do this. I'll present one option using scp (OpenSSH secure file copy) to get them to another machine. This assumes you have ssh running on the radius server. From there you can use whatever method you like to get them to the destination client.

# on another Linux machine
# x.x.x.x is the ip of the radius server
# <ssh_port> is the port used for making an ssh connection to the radius server
scp -P <ssh_port> pi@x.x.x.x:/home/pi/ca.pem .
scp -P <ssh_port> pi@x.x.x.x:/home/pi/name@example.com.pem .

Clean up (again)

  • It's a good idea to remove the client pem file everywhere it was copied once it's on the intended client. Leave the copy on the radius server in /etc/freeradius/3.0/certs, it will be needed to revoke the certificate and is also useful for keeping track of all the client certificates you have generated.
  • If the client is a Linux machine, it's a good idea to make sure the permissions of the client pem file are restricted to the owner.
    # on the client machine (the machine connecting to wifi)
    chmod 600 "name@example.com.pem"
    
  • The ca.pem file does not need to be protected in the same way, since it does not contain the private key, but it doesn't hurt.
    # on the client machine (the machine connecting to wifi)
    chmod 600 ca.pem
    

See the Clients page for help using these files to connect to a WPA2/WPA3 Enterprise network.


Revoke a Client Certificate


If you no longer want a particular certificate to be allowed to authenticate a client, it can be revoked.


SSH into or open a terminal on the server

sudo su
cd /etc/freeradius/3.0/certs
openssl ca -revoke "name@example.com.pem" -keyfile ca.key -cert ca.pem -config ca.cnf
# enter the password from the ca.cnf file
openssl ca -gencrl -keyfile ca.key -cert ca.pem -out crl.pem -config crl.cnf
# enter the password from the ca.cnf file again
cat ca.pem crl.pem > cacrl.pem
chown freerad:freerad *
systemctl restart freeradius

You can check the status of all the client certificates in the index.txt file. If a line starts with an R it means that the certificate has been revoked.


Monitoring and Debugging


The ufw (firewall) log file can be checked to see if connection attempts are being blocked.

  • /var/log/ufw.log

The freeradius log file will show authentication attempts.

  • /var/log/freeradius/radius.log

The freeradius accounting records will show more client details if accounting is enabled on the router.

  • /var/log/freeradius/radacct/ - these are only visible with a root shell

Backup & Restore


An encrypted backup of all the certificates can easily be created with 7-Zip.

sudo su
apt install p7zip-full
cd /etc/freeradius/3.0/certs
7z a -p -mhe=on radius.7z *.pem *.der *.csr *.crt *.key *.p12 serial* index.txt*

The certificates can then be restored on any server.

7z x radius.7z

Resources


Site Index

Router Security

FreeRADIUS Documentation

Securing your Raspberry Pi


pi