Setup
- Preliminary Steps
- Initialize Certificates
- Create the CRL
- Edit the Configuration Files
- Create a Client Certificate
- Revoke a Client Certificate
- Monitoring and Debugging
- Backup & Restore
- 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 httpsdeb https://archive.raspberrypi.org/debian/ bullseye main
- Edit
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
toPASSWD
so it looks like the followingpi ALL=(ALL) PASSWD: ALL
Remove / disable unnecessary networking 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
- Change the ssh port, preferably to something between 49152 and 65535
Port <port_number>
- Change the ssh port, preferably to something between 49152 and 65535
- 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 fromssh-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
- Disallow passwords so only ssh keys can be used to log in
- Restart ssh to pick up the authentication changes
sudo systemctl restart ssh
- Edit
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
orexample.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:
$#&=
- These work:
- Only the
output_password
seems to be used, you can use the same value forinput_password
inner-server.cnf
is for the inner-tunnel and will not be used
- Be careful with special characters in passwords, some will cause strange errors
- Change
default_days
anddefault_crl_days
to something large - this is the lifespan of your certificatesdefault_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
andcommonName
inclient.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 alreadyreq_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 alreadysubjectAltName = @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 thelisten
sections for both authentication (type = auth
) and accounting (type = acct
). Initially they are set to0
, 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
andtls
- Look for and update the following entries in the
tls-config
section to these values - uncomment them if they are commented outdefault_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 neededproxy_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 regeneratepasswords.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