Android 13, GrapheneOS, freeradius, Wi-Fi authentication, and Let's Encrypt
I am using freeradius to authenticate users to a particular SSID, to steer them onto the right VLAN.
When I set up my new phone - a Pixel 6 running GrapheneOS - I could connect to Wi-Fi, entering my username and password, accept the proffered certificate, and it worked.
But if I switched off Wi-Fi, or even went out of range of the WAP, it would not reconnect. I needed to “forget” the Wi-Fi settings, and re-enter them.
Which was a pain.
Since the same set-up worked just fine on iOS, macOS, tvOS, Linux, and my other Android (10) phone, I had assumed it was an issue with GrapheneOS. But I was wrong.
Alert read:fatal:certificate expired
The freeradius server radius.log gave me a (strong) clue:
ERROR: (152) eap_peap: ERROR: (TLS) Alert read:fatal:certificate expired
And, sure enough, when I checked my cert with openssl x509 -in ca.pem -text
, I got:
Validity
Not Before: Sep 25 16:21:57 2021 GMT
Not After : Nov 24 16:21:57 2021 GMT
I set up freeradius’s default X.509 infrastructure using the supplied bootstrap
and, since it worked (and continued to work, and continued to work), I never bothered to change it.
But it seems that, while pretty much every other device I own does not care about the expiry of the certificate, Android 13 does.
So I needed to build a valid X.509 infrastructure, with a new, not-expired, certificate.
I kind of wanted it to “just work” until I had a chance to do that, so I did what I wasn’t supposed to do, and ran bootstrap
again. And it did what it was supposed to do and failed, since I already had a certificate of the same name.
freeradius with Let’s Encrypt
Rather than setting up and managing my own CA, I had wondered about using Let’s Encrypt. And it seemed that others had done it, successfully, so I gave it a try.
It’s a bit of a pain, in that my DNS provider doesn’t support DNS-based challenges, so I need to generate the certs via a web server. Not the end of the world, and I’ve automated copying the certificates from where they land with Let’s Encrypt into `/etc/freeradius/certs/letsencrypt’ and setting ownership to ‘freerad’.
With that in place, I just needed to modify /etc/freeradius/mods-enabled/eap
, to reference the new certificates. The only change I made was:
tls-config tls-common {
private_key_file = /etc/freeradius/certs/letsencrypt/privkey.pem
certificate_file = /etc/freeradius/certs/letsencrypt/fullchain.pem
and then I restarted freeradius (service freeradius restart
).
Configuring Android 13 to use Let’s Encrypt for RADIUS authentication
While tailing radius.log, I set up the Android client.
And it authenticated and connected.
Excellent. So far, so good - the Let’s Encrypt-based CA seemed to be working.
So I turned off Wi-Fi, turned it back on again, and … it failed.
But with a different error in radius.log:
ERROR: (60) eap_peap: ERROR: (TLS) Alert read:fatal:unknown CA
Weird, I thought, as since I’m using Let’s Encrypt, which is handled by the system’s own certificates, so the device should be coping with just fine.
But I hadn’t told the device to rely on the system certificates. I revisited the Wi-Fi settings on the phone, and changed:
- “CA certificate” to “Use system certificates”
- “Domain” (which became a mandatory field) to the domain I was using for the Let’s Encrypt certificate
and hit “Save”.
And it authenticated and connected.
So I turned off Wi-Fi, turned it back on again, and … it worked!
What will happen after three months?
Let’s Encrypt certificates are valid for three months.
That’s fine, since I’ve automated renewal and re-provisioning into freeradius. So it should be seamless on the server side.
But what about clients? Will I be forced to accept a new certificate on each client every three months, or will it be handled invisibly?
I forced a renewal of the (still valid) Let’s Encrypt cert:
certbot --force-renewal -d [domain name] --preferred-chain="ISRG Root X1"
(I’m not sure if I really need --preferred-chain="ISRG Root X1"
; one day, I will test it, but since it works for now…)
And turned off Wi-Fi on my phone, and turned it back on again.
It connected seamlessly.
So, all good with Android 13.
Same with Linux - it was invisible.
For Apple devices (iOS, iPadOS, tvOS), Wi-Fi refused to connected. I had to click into the Wi-Fi Settings, and trust the certificate again. That’s a bit of a pain, but it will only be for every three months, so it might be tolerable…
Impact on other devices
-
iPadOS: when I switched from the bootstrapped CA to the Let’s Encrypt CA, Wi-Fi stopped working. I needed to go into Settings, Wi-Fi, select the SSID, trust the new certificate, and it worked.
-
Linux: it carried on working seamlessly. I didn’t need to accept a new certificate.
-
tvOS (i.e. the Apple TV): as with iPadOS, I needed to go into Wi-Fi settings, click on the SSID, trust the new certificate, and it worked.
You may also like:
- Installing GrapheneOS on a Google Pixel 6
- Bypassing captive portal detection on Android 10
- Unsuspending a £50 120GB EE data SIM after six months of use
- Installing Debian 11 on a Microsoft Surface Go: secure boot, mokutil, Wi-Fi, and libinih1
- Adding music from an ssh-accessible remote server to an Android phone via Debian 11
- PinePhone: WireGuard, dns-over-https, and other thoughts
- My initial - and very positive - impressions of the pine64 PineTime smartwatch
- freeradius-allocated VLAN on Wi-Fi on an Apple TV via a .mobileconfig profile
- Using freeradius to assign VLANs for UniFi Wi-Fi
- Wi-Fi on a 2012 Mac Mini, running Debian 11