FreeRadius EAP-TLS configuration: Difference between revisions
Sillysausage (talk | contribs) |
m ('openssl dhparam' can only accept the bit-length argument after all options) |
||
(14 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
= Introduction = | = Introduction = | ||
A more secure way than using pre-shared keys (WPA2) is to use [https://en.wikipedia.org/wiki/Extensible_Authentication_Protocol#EAP-TLS EAP-TLS] and use separate certificates for each device. In the previous tutorial [[Linux Router with VPN on a Raspberry Pi]] I mentioned I'd be doing this with a | A more secure way than using pre-shared keys (WPA2) is to use [https://en.wikipedia.org/wiki/Extensible_Authentication_Protocol#EAP-TLS EAP-TLS] and use separate certificates for each device. In the previous tutorial [[Linux Router with VPN on a Raspberry Pi]] I mentioned I'd be doing this with a | ||
([ | ([https://wiki.openwrt.org/toh/ubiquiti/unifi Ubiquiti UniFi AP]). I have tested this with two phones running CyanogenMod 11 (Android 4.4.4). | ||
= Installation = | = Installation = | ||
Install | Install FreeRadius: | ||
{{cmd|apk add freeradius freeradius-eap}} | |||
{{cmd|apk add freeradius freeradius-eap | |||
= Certificates = | = Certificates = | ||
You will want to create your certificates. The easiest way to do that is to use the scripts provided by FreeRadius. The scripts allow you to easily create a CA (certificate authority), Server certificate, and Client certificates. Remember to increase the expiry time from 60 days if that doesn't suit you and fill in the other information in the .cnf files like the README says. | You will want to create your certificates. The easiest way to do that is to use the scripts provided by FreeRadius. The scripts allow you to easily create a CA (certificate authority), Server certificate, and Client certificates. Remember to increase the expiry time from 60 days if that doesn't suit you and fill in the other information in the .cnf files like the README says. | ||
The readme for that script is in /etc/raddb/certs/README or can be found [https://github.com/FreeRADIUS/freeradius-server/ | The readme for that script is in /etc/raddb/certs/README or can be found [https://github.com/FreeRADIUS/freeradius-server/tree/v3.0.x/raddb/certs here]. | ||
= Certificate Revocation List = | = Certificate Revocation List = | ||
Line 91: | Line 86: | ||
{{cmd|cat ca.pem crl.pem > cacrl.pem}} | {{cmd|cat ca.pem crl.pem > cacrl.pem}} | ||
= Create the Diffie-Hellman file = | = Create the Diffie-Hellman nonce file = | ||
{{cmd|openssl dhparam -check -text -5 | {{cmd|openssl dhparam -check -text -5 -out /etc/raddb/certs/dh 1024}} | ||
Or you can use a larger one, eg (this can take a while if you're unlucky!). | Or you can use a larger one, eg (this can take a while if you're unlucky!). | ||
{{cmd|openssl dhparam -check -text -5 | {{cmd|openssl dhparam -check -text -5 -out /etc/raddb/certs/dh 4096}} | ||
= Server config = | |||
The [https://www.wi-fi.org/system/files/WPA3%20Specification%20v3.1.pdf WPA3 specification] (p12), has a few changes to EAP. Even if you're not using WPA3 it still effects your EAP RADIUS authentication, particularly for clients such as Android 11+. Further discussion was [https://reddit.com/comments/l4fdzp here]. | |||
{{Box|WPA 3.1 Page 12| | |||
5.1.2 The STA is configured with EAP credentials that explicitly specify a CA root certificate that matches the root certificate in the received Server Certificate message and, if the EAP credentials also include a domain name | |||
(FQDN or suffix-only), it matches the domain name (SubjectAltName dNSName if present, otherwise SubjectName CN) of the certificate [2] in the received Server Certificate message. | |||
5.1.3 The STA is configured with EAP credentials that include a domain name (FQDN or suffix-only) that matches the domain name (SubjectAltName dNSName if present, otherwise SubjectName CN) of the certificate [2] in the received Server Certificate message, and the root certificate of that certificate is present in the STA's trust root store.}} | |||
Without these changes you'll see errors in logcat like: | |||
<pre>EAP: Status notification: remote certificate verification (param=self signed certificate in certificate chain) | |||
SSL: SSL3 alert: write (local SSL3 detected an error):fatal:unknown CA | |||
EAP: Status notification: local TLS alert (param=unknown CA)</pre> | |||
== /etc/raddb/server.cnf == | |||
<pre>[ 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</pre> | |||
Then you'd need to put "radius.example.com" in the "Domain" box when you add the WiFi Network. It's also known as "domain_suffix_match" in wpa_supplicant.conf or NetworkManager configuration files. | |||
== /etc/raddb/certs/xpextensions == | |||
Then you need to update xpextensions before making your configs. | |||
<pre>+ [ xpserver_ext] | |||
+ extendedKeyUsage = 1.3.6.1.5.5.7.3.1 | |||
+ crlDistributionPoints = URI:http://www.example.com/example_ca.crl | |||
+ subjectAltName = @alt_names | |||
and then at the bottom: | |||
+ [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</pre> | |||
= Configuration = | = Configuration = | ||
Line 223: | Line 271: | ||
= IPtables rules = | = IPtables rules = | ||
Next up you're going to want some iptables rules. | Next up you're going to want some iptables rules. | ||
<pre>#Accept incoming connections from client FreeRadius | <pre>#Accept incoming connections from client FreeRadius | ||
iptables -A | iptables -A IN_ETH0 -p tcp -s 192.168.1.10/24 --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT | ||
iptables -A IN_ETH0 -p udp -s 192.168.1.10/24 --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT</pre> | |||
iptables -A | |||
iptables -A | I also noticed with the Ubiquiti devices you need to allow this for AP adoption to work: | ||
<pre>#Ubiquiti UAP Device Discovery Broadcast | |||
iptables -A IN_ETH0 -p udp -s 192.168.1.10/24 --dport 10001 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT</pre> | |||
Note that these rules depend on the chains originally created here [[Linux Router with VPN on a Raspberry Pi#Basic IPtables firewall with routing]]. | |||
= Configure AP = | = Configure AP = | ||
Line 265: | Line 318: | ||
{{cmd|cat ca.pem crl.pem > cacrl.pem}} | {{cmd|cat ca.pem crl.pem > cacrl.pem}} | ||
{{cmd|service radiusd | |||
You need to restart FreeRadius after revoking certificates. | |||
{{cmd|service radiusd restart}} | |||
You can verify that a certificate is revoked with: | You can verify that a certificate is revoked with: | ||
{{cmd|openssl crl -in /etc/raddb/certs/cacrl.pem -text}} | {{cmd|openssl crl -in /etc/raddb/certs/cacrl.pem -text}} | ||
If no certificates are revoked you'll see: | |||
<pre>No Revoked Certificates.</pre> | |||
<pre>No Revoked Certificates.</pre> | |||
If one or more certificates are revoked you'll see: | |||
<pre>Revoked Certificates: | <pre>Revoked Certificates: | ||
Serial Number: <number of your cert></pre> | Serial Number: <number of your cert></pre> | ||
Line 279: | Line 334: | ||
= References = | = References = | ||
* https://forums.freebsd.org/threads/howto-wpa2-enterprise-with-freeradius.28467 | * https://forums.freebsd.org/threads/howto-wpa2-enterprise-with-freeradius.28467 | ||
[[Category:Server]] |
Latest revision as of 22:54, 25 March 2024
Introduction
A more secure way than using pre-shared keys (WPA2) is to use EAP-TLS and use separate certificates for each device. In the previous tutorial Linux Router with VPN on a Raspberry Pi I mentioned I'd be doing this with a (Ubiquiti UniFi AP). I have tested this with two phones running CyanogenMod 11 (Android 4.4.4).
Installation
Install FreeRadius:
apk add freeradius freeradius-eap
Certificates
You will want to create your certificates. The easiest way to do that is to use the scripts provided by FreeRadius. The scripts allow you to easily create a CA (certificate authority), Server certificate, and Client certificates. Remember to increase the expiry time from 60 days if that doesn't suit you and fill in the other information in the .cnf files like the README says.
The readme for that script is in /etc/raddb/certs/README or can be found here.
Certificate Revocation List
The CRL is not created by the script, you have to do that one manually.
I created a file called crl.cnf:
[ ca ] default_ca = CA_default [ CA_default ] dir = ./ certs = $dir crl_dir = $dir/crl database = $dir/index.txt new_certs_dir = $dir certificate = $dir/ca.pem serial = $dir/serial crl = $dir/crl.pem private_key = $dir/ca.key RANDFILE = $dir/.rand name_opt = ca_default cert_opt = ca_default default_days = 730 default_crl_days = 730 default_md = sha256 preserve = no policy = policy_match crlDistributionPoints = URI:http://www.example.com/example_ca.crl [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] prompt = no distinguished_name = cacrl default_bits = 2048 input_password = <password1> output_password = <password2> x509_extensions = v3_ca [certificate_authority] countryName = <COUNTRY_CODE> stateOrProvinceName = Radius localityName = <REGION> organizationName = FreeRadius emailAddress = freeradius@localhost commonName = "FreeRadius Certificate Authority" [v3_ca] subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always basicConstraints = CA:true crlDistributionPoints = URI:http://www.example.com/example_ca.crl
Create the revocation list:
openssl ca -gencrl -keyfile ca.key -cert ca.pem -out crl.pem -config crl.cnf
Finally, create new file which will hold both CA and revoked certificates:
cat ca.pem crl.pem > cacrl.pem
Create the Diffie-Hellman nonce file
openssl dhparam -check -text -5 -out /etc/raddb/certs/dh 1024
Or you can use a larger one, eg (this can take a while if you're unlucky!).
openssl dhparam -check -text -5 -out /etc/raddb/certs/dh 4096
Server config
The WPA3 specification (p12), has a few changes to EAP. Even if you're not using WPA3 it still effects your EAP RADIUS authentication, particularly for clients such as Android 11+. Further discussion was here.
5.1.2 The STA is configured with EAP credentials that explicitly specify a CA root certificate that matches the root certificate in the received Server Certificate message and, if the EAP credentials also include a domain name (FQDN or suffix-only), it matches the domain name (SubjectAltName dNSName if present, otherwise SubjectName CN) of the certificate [2] in the received Server Certificate message.
5.1.3 The STA is configured with EAP credentials that include a domain name (FQDN or suffix-only) that matches the domain name (SubjectAltName dNSName if present, otherwise SubjectName CN) of the certificate [2] in the received Server Certificate message, and the root certificate of that certificate is present in the STA's trust root store.Without these changes you'll see errors in logcat like:
EAP: Status notification: remote certificate verification (param=self signed certificate in certificate chain) SSL: SSL3 alert: write (local SSL3 detected an error):fatal:unknown CA EAP: Status notification: local TLS alert (param=unknown CA)
/etc/raddb/server.cnf
[ 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
Then you'd need to put "radius.example.com" in the "Domain" box when you add the WiFi Network. It's also known as "domain_suffix_match" in wpa_supplicant.conf or NetworkManager configuration files.
/etc/raddb/certs/xpextensions
Then you need to update xpextensions before making your configs.
+ [ xpserver_ext] + extendedKeyUsage = 1.3.6.1.5.5.7.3.1 + crlDistributionPoints = URI:http://www.example.com/example_ca.crl + subjectAltName = @alt_names and then at the bottom: + [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
Configuration
/etc/raddb/clients.conf
First we're going to add a client, this is your WiFi AP:
client home { ipaddr = 192.168.1.10 proto = * secret = <PASSWORD USED BY YOUR AP TO AUTHENTICATE WITH THIS RADIUS SERVER> shortname = <YOUR_SSID> require_message_authenticator = no nas_type = other limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } }
/etc/raddb/mods-enabled/eap
Next we configure eap. Note the + and - represent lines removed and added, don't include them in your config!
You're going to want to make these changes:
- default_eap_type = md5 + default_eap_type = tls
- private_key_password = whatever + private_key_password = <Password you set output_password in server.cnf> private_key_file = ${certdir}/server.pem
- ca_file = ${cadir}/ca.pem + ca_file = ${cadir}/cacrl.pem
- random_file = /dev/urandom + random_file = /dev/random
- # check_crl = yes + check_crl = yes
Reduce cipher list from DEFAULT to HIGH, or even a specific list:
- cipher_list = "DEFAULT" + cipher_list = "HIGH"
Or a shorter list if you decide (might cause some device compatibility issues)
+ cipher_list = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-RSA-CAMELLIA128-SHA"
Change ecdh curve to something stronger:
- ecdh_curve = "prime256v1" + ecdh_curve = "secp384r1"
You can also increase the curve to a higher bit (521), but this may cause compatibility problems.
+ ecdh_curve = "secp521r1"
These all worked with Android 4.4.4, but if you have older stuff you may need to set the list to HIGH or DEFAULT.
Couple of other things to change:
- #name = "EAP module" + name = "EAP-TLS"
- #persist_dir = "${logdir}/tlscache" + persist_dir = "${logdir}/tlscache"
/etc/raddb/sites-enabled/default
Change the listening port to what suits you
- ipaddr = * + ipv4addr = 192.168.1.1
Disable chap
# The chap module will set 'Auth-Type := CHAP' if we are # handling a CHAP request and Auth-Type has not already been set - chap + # chap
Disable mschap
# the MS-CHAP-Challenge attribute, and add 'Auth-Type := MS-CHAP' # to the request, which will cause the server to then use # the mschap module for authentication. - mschap + # mschap
Disable pap
# This module should be listed last, so that the other modules # get a chance to set Auth-Type for themselves. - pap + #pap
Disable the auth types we're not using
- Auth-Type PAP { - pap - } + #Auth-Type PAP { + # pap + #} - Auth-Type CHAP { - chap - } + #Auth-Type CHAP { + # chap + #} - Auth-Type MS-CHAP { - mschap - } + #Auth-Type MS-CHAP { + # mschap + #}
Enable eap
-# eap + eap
/etc/raddb/sites-available/tls
tls { - private_key_password = whatever + private_key_password = <Password you set input_password in server.cnf> private_key_file = ${certdir}/server.pem
IPtables rules
Next up you're going to want some iptables rules.
#Accept incoming connections from client FreeRadius iptables -A IN_ETH0 -p tcp -s 192.168.1.10/24 --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT iptables -A IN_ETH0 -p udp -s 192.168.1.10/24 --dport 1812 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
I also noticed with the Ubiquiti devices you need to allow this for AP adoption to work:
#Ubiquiti UAP Device Discovery Broadcast iptables -A IN_ETH0 -p udp -s 192.168.1.10/24 --dport 10001 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
Note that these rules depend on the chains originally created here Linux Router with VPN on a Raspberry Pi#Basic IPtables firewall with routing.
Configure AP
You're going to want to configure your access point to talk to your new Radius server.
Using the secret and shortname from clients.conf enter them into your access point administration panel.
Start Radius
service radiusd start
Add to default run level.
rc-update add radiusd default
You can debug it with radiusd -X from the console, or check /var/log/radius/radius.log if that didn't work.
Configure a device
On Android I go into "Settings > Security > Install from Storage" and select ca.pem
I then do "Settings > Security > Install from Storage" and select client.p12"
After putting in the correct passwords it should work. On Android you may see a warning such as "Network May Be Monitored by an Unknown Third Party". You can fix this by moving the CA from /data/misc/keychain/cacerts-added to /system/etc/security/cacerts make sure the user and group are root and that the permissions are set to 644, ie readable by everyone, only root has permissions to write to the files. Keep it in /sdcard/ so you can move it back if you re-flash the phone with a newer ROM.
Revoke a certificate
If in the future you want to revoke the certificates of a particular user you can do this by:
openssl ca -revoke user@example.com.pem -keyfile ca.key -cert ca.pem -config ca.cnf
<enter output_password from ca.cnf>
Now, take a moment and open index.txt and you should see "R" next to cert index number. If you ever need to make this cert valid again, you would edit line with "R" to match other certs format.
Now you need to create crl list again, just like it was done at the beginning of tutorial:
openssl ca -gencrl -keyfile ca.key -cert ca.pem -out crl.pem -config crl.cnf
<enter output_password from ca.cnf>
cat ca.pem crl.pem > cacrl.pem
You need to restart FreeRadius after revoking certificates.
service radiusd restart
You can verify that a certificate is revoked with:
openssl crl -in /etc/raddb/certs/cacrl.pem -text
If no certificates are revoked you'll see:
No Revoked Certificates.
If one or more certificates are revoked you'll see:
Revoked Certificates: Serial Number: <number of your cert>