How to unit test 802.1X WiFi connection with eapol_test ?
Hello, I've got a Freeradius instance on a debian 13 host. It is used as a backend with a (rather old) Unifi WPA2 Entreprise. WiFi rely on PEAP/MSCHAPv2. My goal is to unit test connections from an Android device. I'm using the followiing script on the debian 13: cat > ${TMP_CFG} << EOF network={ ssid="${SSID}" key_mgmt=WPA-EAP eap=PEAP identity="${CLIENT_ID2}" password="${CLIENT_PWD}" phase2="autheap=MSCHAPV2" ca_cert="${SYSTEM_CA_CERT}" domain_match="${DOMAIN}" } EOF eapol_test -v -c ${TMP_CFG} -a ${SERVER_IP} -A ${SERVER_IP} -s ${RADIUS_SECRET} >> ${LOG_FILE} Last line returns 0 as exit code. Now with my smartphone, I must disable CA Certificate validation to successfully connect to WiFi. 1. What do you think of the above script ? Can I use it reliably to test WiFi connections from Android, Windows, iOS and MacOS devices ? 2. What could explain difference between live testing with a real Android device and the above script ? Best regards
On Thu, 22 Oct 2026, at 15:44, Olivier wrote:
I've got a Freeradius instance on a debian 13 host. It is used as a backend with a (rather old) Unifi WPA2 Entreprise. WiFi rely on PEAP/MSCHAPv2.
Which if I remember, Microsoft is mostly breaking this month with a security update[3].
My goal is to unit test connections from an Android device. I'm using the followiing script on the debian 13:
cat > ${TMP_CFG} << EOF network={ ssid="${SSID}" key_mgmt=WPA-EAP eap=PEAP identity="${CLIENT_ID2}" password="${CLIENT_PWD}" phase2="autheap=MSCHAPV2" ca_cert="${SYSTEM_CA_CERT}" domain_match="${DOMAIN}" } EOF eapol_test -v -c ${TMP_CFG} -a ${SERVER_IP} -A ${SERVER_IP} -s ${RADIUS_SECRET} >> ${LOG_FILE}
Last line returns 0 as exit code.
As a suggestion, a pattern I like to lean on in my Shell scripts: ---- TMP_CONF=$(mktemp) cleanup () { rm "$TMP_CONF" } trap cleanup EXIT INT ---- Means this is safe to run in parallel, plus no files are left behind when the script exits. Always a good idea to punt code through a linter, ShellCheck[1] is not a terrible choice here.
Now with my smartphone, I must disable CA Certificate validation to successfully connect to WiFi.
Then you need to install the root CA(s) on to your Android device.
1. What do you think of the above script ? Can I use it reliably to test WiFi connections from Android, Windows, iOS and MacOS devices ?
The script tests if EAP can authenticate over RADIUS, it does help you emulate real devices. In practice though this is what most (all?) of us use as if the wifi/wired leg is not working, that is unlikely to be a RADIUS/EAP issue and more "put your venduh kit (or device) in the bin". What you may find useful, I have some work[2] up on Github for automating testing Windows running in QEMU strapped to hostapd that then sends RADIUS packets to FreeRADIUS. This is probably as close as you can get for an end-to-end test as it exercises *wired* 802.1X pretty much true to form.
2. What could explain difference between live testing with a real Android device and the above script ?
Either your Android device is using the wrong CA(s) or the chain you are presenting to the device is broken. You should use 'openssl verify ...' to figure out if your cert chain is valid which means your server certificate at the top followed by the sub-CAs, but do not include the root CA at the end of the list as it will only slow down authentication and actually make it less reliable (IP fragmentation). Something like the following should start you off: openssl verify -show_chain -CApath /path/to/your/root/ca.pem -untrusted /path/to/the/sub/ca.pem -purpose sslserver -verify_hostname whatever.example.com /path/to/the/server.pem Inspect the output from the '-show_chain', it should tell you if you mixed things up. Also may be good to turn *off* 'auto_chain' in your eap.conf file to avoid any silly business from OpenSSL muddying the waters. Regards Alex [1] https://www.shellcheck.net/ [2] https://github.com/jimdigriz/winci/tree/main/examples/dot1x [3] https://www.theregister.com/security/2026/10/16/networking-problems-after-th...
1. What do you think of the above script ? Can I use it reliably to test WiFi connections from Android, Windows, iOS and MacOS devices ?
The script tests if EAP can authenticate over RADIUS, it does help you emulate real devices.
Missed the 'not', should read "does *not* help you emulate real devices". Cheers
Thank you very much, Alexander, for replying. I'll study it in detail and I'll add my findings here. Le sam. 24 oct. 2026 à 10:46, Alexander Clouter via Freeradius-Users <freeradius-users@lists.freeradius.org> a écrit :
1. What do you think of the above script ? Can I use it reliably to test WiFi connections from Android, Windows, iOS and MacOS devices ?
The script tests if EAP can authenticate over RADIUS, it does help you emulate real devices.
Missed the 'not', should read "does *not* help you emulate real devices".
Cheers - List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html
Alexander Clouter via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
On Thu, 22 Oct 2026, at 15:44, Olivier wrote:
I've got a Freeradius instance on a debian 13 host. It is used as a backend with a (rather old) Unifi WPA2 Entreprise. WiFi rely on PEAP/MSCHAPv2.
Which if I remember, Microsoft is mostly breaking this month with a security update[3].
Partially. There's a registry key to undo the change, and it only affects you if you are using host auths or passing Windows Login screen creds through to the supplicant. If the OS considers your wifi creds to not be the same as the Windows Login creds, you are fine. FWIW the following is a summary I typed up earlier on where things now stand. Comments or corrections are welcome: Yes, we all know EAP-TLS exists. For those that wish to preserve the use of usernames and require a responsible user, rather then trust individual machines not to find their way into the wrong hands, EAP-TTLS-PAP seems to be the only viable multi-OS path forward. PEAP-MSCHAPv2 might stay viable for non-SSO applications for quite some time yet, moreso if your credential store is not in Active Directory, but the writing is on the wall that Microsoft and the rest of the industry intends to make life difficult for anyone trying to use it (also, for anyone using public CAs.) It just seems to bug them to no end that the inner tunnel protocol was "cracked" and as a PR move they intend to replace it with... cleartext. Can't crack that. It would be funny if we weren't the ones cleaning up after them. And of course, despite having years to do so, they have offered no real replacement that satisfies the concerns of those that don't feel confident trusting their RADIUS servers with unpacked plaintext passwords. MPSK/IPSK will not translate to WPA3 the way many might think, barring locking yourself into a vendor-specific hack. So if you are fine with each user having a separate "wifi password" and hoped to have them use it to salt an SAE per-device credential as a portal-less onboarding mehcanism... nope. You'll need a portal based onboarding flow to properly authenticate on first use, sorry. The industry has spoken thus and told you to get bent. EAP-TTLS-PAP comes with some caveats: 1) For Windows to properly TOFU, TLSv1.3 must be turned off. Use TLSv1.2, it supports crypto suites required by WPA3 just fine. Microsoft Windows's session resumption for TLS1.3 is broken anyway, so there's even more reason to turn it off. TLS1.2 will be viable up and until post-quantum initiatives make life difficult again, since the standards bodies have explicitly stated they will not be adding PQ cyphers to TLS1.2. That is a ways off though. 2) You must configure your inner tunnel to detect EAP-GTC inner protocol and map it to PAP for the Apple clients to TOFU to a EAP-TTLS-PAP network. 3) For those storing their creds in an actual Microsoft AD system, you *could* hink things up to use the PAP credentials in many different ways. Don't choose a way that involves NTLM. It will break as soon as Microsoft decides to push server-side changes designed to kill NTLM. They have provided ample (if mealy mouthed) warnings that they intend to do so. You should be looking to rlm_krb5. 4) KRB5 tickets are not designed for the use-once-throw-away mode they are used for in RADIUS. As such, backend server load may become a problem. The more immediate issue, though, is that KRB5 intrinsically lacks the protections against account lockout after a password change (where a device still using an old password locks the user's account.) The rlm_cache module inside FreeRADIUS can restore similar protections as well as reduce the load on KRB5 backend servers. This does involve a larger memory profile for the server as the cache can get pretty big. (I'd share the config we have worked up for this, but seeing as it is untested except for some lab experiments, I'll hold off till we get some mileage on it.) 5) Android users end up getting hit with the most BYOD onboarding friction, since they currently default to EAP-TTLS-MSCHAP when EAP-TTLS is detected. Maybe this will get fixed at some point (...but given the decades of bungling by all parties involved...) Certain phone vendors will absolutely require an app to connect to networks using a private CA, as they lack the "Trust on First Use" option. Not-so-EAP-TTLS-PAP specific things: The use of public CA certificates for server roots will become more and more untenable as CAs tighten renewal intervals on leaf certificates. TOFU behavior has improved, and may be the best choice for low-friction onboarding, and it mostly works for private roots at this point with the notable exception being those abovementioned Android-based phone brands. The main drawback to public CA it is that TOFU supplicants pin to a specific leaf certificate, and so users must re-TOFU on every certificate renewal and it gets users into the bad habit of accepting certificate warnings. As such, proper use of an onboarding tool to set DN validation is still highly advisable when using public CAs (and yes, EAP-TLS folks, you need to do so too, not just install a client cert.) Private CAs can stretch their server cert out to a bit under 2 years. Extend that beyond 2 years, and you will have Apple issues. This longer duration helps prevent TOFU-based users from getting accustomed to approving popups. DN validation could still be used if you find the installation of that worth the effort... it adds barely any security over a root that is just for your server and nothing else; it just makes leaf cert renewals transparent to the user. For the longer duration which the installed intermediate certificate can have over the leaf certificate this might not be worth the technical debt. For MS host (machine) auths, ye shall use EAP-TLS going forward. So sayeth Microsoft and the rest of the maniacs driving this bus seem to agree on this as well. You can offer EAP-TLS as a second protocol option on the same SSID and take the hit on roaming latency, or run another SSID for your managed devices. Pick your poison. Finally, if you find TOFU tasteless and you use .mobileconfig for installing a private CA on Apples, install an intermediate not a root CA. Unless you actually want your Apple users trusting your RADIUS server cert for web browsing security.
On Mon, 26 Oct 2026, at 15:22, Brian Julin wrote:
Yes, we all know EAP-TLS exists. For those that wish to preserve the use of usernames and require a responsible user, rather then trust individual machines not to find their way into the wrong hands, EAP-TTLS-PAP seems to be the only viable multi-OS path forward.
Might shift your stance a little, Windows does tie client certificates to the TPM which may help alleviate some of those concerns. Of course, though a whole other world of pain[1], TEAP lets you do authentication chaining so you can treat machine and machine+user authentication differently. On a related note, it is a real crime Google, Apple and Microsoft completely undermined EAP-GTC[3] and do not show the challenge message and cache the response as a static credential. We could have all gotten easy access to MFA.
1) For Windows to properly TOFU, TLSv1.3 must be turned off. Use TLSv1.2, it supports crypto suites required by WPA3 just fine. Microsoft Windows's session resumption for TLS1.3 is broken anyway, so there's even more reason to turn it off.
They are fixing this[2]. I am/was trying to kick the tires on it but wired 802.1X on Windows is absolutely 'hot garbage'. The bigger issue with resumption is that most people consider "TLS resumption == authentication + authorization" when really it is neither and all it _reliably_ does is established a TLS jacket faster. TTLS for TLS 1.2 (RFC5281, section 7.5) makes the server responsible for making "resumption == authentication" and no authentication related traffic may occur in the tunnel for a resumed session. In an, albeit easy to pick apart as are we really saving here, argument a user that changed their password recently or mistyped it the first time has to restart the TLS jacket from afresh. For TLS 1.3 (RFC9427, section 4), you are allowed to redo the inner methods after resumption....no idea if supplicants actually support this though. For authorization, you as the admin must drag out and duplicate the important parts of your inner-tunnel authorization section to either your outer post-auth[5] section or your tls-cache 'load' section. Not the end of the world, but something the FR codebase recently highlighted[6] so hopefully the message gets out there.
2) You must configure your inner tunnel to detect EAP-GTC inner protocol and map it to PAP for the Apple clients to TOFU to a EAP-TTLS-PAP network.
I suspect this is a sound technical decision as there is no way to negotiate PAP/CHAP/MSCHAP/MSCHAPv2, it's just sent by the supplicant immediately without solicitation once the TLS jacket is established. When you select an EAP method instead it can now do the whole identity?->identity bob->method A->NAK try B->method B dance so you can negotiate things there.
The rlm_cache module inside FreeRADIUS can restore similar protections as well as reduce the load on KRB5 backend servers. This does involve a larger memory profile for the server as the cache can get pretty big. (I'd share the config we have worked up for this, but seeing as it is untested except for some lab experiments, I'll hold off till we get some mileage on it.)
I use this for LDAP bind()'s and Entra ID ROPC authentications backed by memcached, it works well. Overhead seems to be about 250 to 600 bytes per entry compared to ~3kB for TLS resumption caching. Tallies up with the information in the memcached documentation[4] reasonably well. Cheers Alex [1] "ask how I know", but be warned you will be picking up the drinks tab :) [2] https://lists.freeradius.org/hyperkitty/list/freeradius-users@lists.freeradi... [3] though I have always had a soft spot for RFC2289 [4] https://docs.memcached.org/serverguide/performance/#how-memory-gets-allocate... [5] which is the only place you find the resumed cached reply items as well as EAP-Session-Resumed [6] https://github.com/FreeRADIUS/freeradius-server/commit/45584ec2930e09aa92629...
On Oct 28, 2026, at 10:33 AM, Alexander Clouter via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
On a related note, it is a real crime Google, Apple and Microsoft completely undermined EAP-GTC[3] and do not show the challenge message and cache the response as a static credential. We could have all gotten easy access to MFA.
No one at those companies gets promoted for making Wi-Fi easier to use. So Wi-Fi has been a disaster for coming on 20 years.
The bigger issue with resumption is that most people consider "TLS resumption == authentication + authorization" when really it is neither and all it _reliably_ does is established a TLS jacket faster.
I'm trying to change that in https://datatracker.ietf.org/doc/draft-dekok-emu-tls-tickets/ There is interest from hostap && Cisco. Separating TLS resumption from inner authentication can also help enormously with PQC certificates. Those certs are large, and might need 80 round trips (!). For someone roaming in eduroam, this could mean that authentication takes 6 seconds or more.
TTLS for TLS 1.2 (RFC5281, section 7.5) makes the server responsible for making "resumption == authentication" and no authentication related traffic may occur in the tunnel for a resumed session. In an, albeit easy to pick apart as are we really saving here, argument a user that changed their password recently or mistyped it the first time has to restart the TLS jacket from afresh. For TLS 1.3 (RFC9427, section 4), you are allowed to redo the inner methods after resumption....no idea if supplicants actually support this though.
hostap supports this, sort of. My draft above tries to find ways to do this automatically, without breaking interoperability with legacy supplicants.
For authorization, you as the admin must drag out and duplicate the important parts of your inner-tunnel authorization section to either your outer post-auth[5] section or your tls-cache 'load' section. Not the end of the world, but something the FR codebase recently highlighted[6] so hopefully the message gets out there.
We're working on getting that fixed. Or at least, making it a lot easier to use. Alan DeKok.
Alexander Clouter <alex+freeradius@coremem.com> wrote:
On Mon, 26 Oct 2026, at 15:22, Brian Julin wrote:
Yes, we all know EAP-TLS exists. For those that wish to preserve the use of usernames and require a responsible user, rather then trust individual machines not to find their way into the wrong hands, EAP-TTLS-PAP seems to be the only viable multi-OS path forward.
Might shift your stance a little, Windows does tie client certificates to the TPM which may help alleviate some of those concerns.
That's great for managed systems that host auth and who's OS-enforced user account authentication you trust. Even better if that OS can competently handle using different certs when different users are logged in. But, we are waaayyy over half BYOD clients here, no GPO or Intune. It's less about whether the cert may leave the machine and more about whether the machine may leave the user.
On a related note, it is a real crime Google, Apple and Microsoft completely undermined EAP-GTC[3] and do not show the challenge message and cache the response as a static credential. We could have all gotten easy access to MFA.
Yeah, add the native IPSEC clients and the whole XAuth dead-end to that complaint for VPN, too. These days I pretty much operate under the assumption that the 3rd-party MFA VARs are giving kickbacks. True or not, it accurately predicts what to expect the native clients to do: completely ruin anything that makes MFA stuff, or even basic secure single auth, work OOTB.
1) For Windows to properly TOFU, TLSv1.3 must be turned off. Use TLSv1.2, it supports crypto suites required by WPA3 just fine. Microsoft Windows's session resumption for TLS1.3 is broken anyway, so there's even more reason to turn it off.
They are fixing this[2]. I am/was trying to kick the tires on it but wired 802.1X on Windows is absolutely 'hot garbage'.
In addition to the broken resumption, the Windows client when it detects TLSv1.3 swaps its backend logic to some newer flow that IIRC triggers either Credential Guard or some other cutoff that prevents a TOFU connection from completing, even though the creds are obviously not NTLM hashes. With or without session resumption turned off, it fails.
The bigger issue with resumption is that most people consider "TLS resumption == authentication + authorization" when really it is neither and all it _reliably_ does is established a TLS jacket faster.
Yes it's an optimization for roaming and that's really all it is. You have to balance your security requirements with how long you want to let the user kite along on a previous authentication/authorization decision, and keep track of what that decision was yourself, and it's a messy config.
The rlm_cache module inside FreeRADIUS can restore similar protections as well as reduce the load on KRB5 backend servers. This does involve a larger memory profile for the server as the cache can get pretty big. (I'd share the config we have worked up for this, but seeing as it is untested except for some lab experiments, I'll hold off till we get some mileage on it.)
I use this for LDAP bind()'s and Entra ID ROPC authentications backed by memcached, it works well. Overhead seems to be about 250 to 600 bytes per entry compared to ~3kB for TLS resumption caching.
Our prototype system is dedicated to just user/pass pairs, doesn't combine any authorization stuff. It still lets password guessing of many different passwords through to lock the account, and combined with a few other caches, handles AAA DoS attempts and spraying as much as possible. We hash the user/pass before storing the cache entry, and rotate the hash seed, which under FR3 is mere theater but once we are in FR4, where objects are wiped when freed, it will greatly reduce the theoretical exposure of retained creds in the AS server. It's a bit paranoid. Frankly you're pretty boned if someone is in far enough to read FR's memory space or freed segments. But you know, "security in depth", right? It should be a very nice stress test for rlm_cache once it's in production, but we're a long way off from that with the transition off PEAP still to come.
On Thu, 29 Oct 2026, at 17:07, Brian Julin wrote:
But, we are waaayyy over half BYOD clients here, no GPO or Intune. It's less about whether the cert may leave the machine and more about whether the machine may leave the user.
I would be surprised if those users had not set up for their machine to cache/remember their password. At least with the certificate it only provides them with network access, not then potentially everything (non-MFA'd) else.
Our prototype system is dedicated to just user/pass pairs, doesn't combine any authorization stuff. It still lets password guessing of many different passwords through to lock the account, and combined with a few other caches, handles AAA DoS attempts and spraying as much as possible.
Though you may know this already, others might not so here for them to read... If you check the *good* password cache before the bad, you avoid the lockout problem as well as creating a whole bunch of single use caching keys[2], though it assumes the user logged in at least once before it all kicked off. Maybe an option for you is to move the lockout from your IdP and into FreeRADIUS and instruct the IdP to not apply lock out (or at a much higher threshold) for authentications through your RADIUS server?
We hash the user/pass before storing the cache entry, and rotate the hash seed, which under FR3 is mere theater but once we are in FR4, where objects are wiped when freed, it will greatly reduce the theoretical exposure of retained creds in the AS server.
Not really sure what you mean about "rotating the seed". What I do is give each user their own salt and cache the following: &control:Crypt-Password := "%{crypt:$6$%{randstr:32s}:%{User-Password}}"
It's a bit paranoid. Frankly you're pretty boned if someone is in far enough to read FR's memory space or freed segments. But you know, "security in depth", right?
Looks like you got Alan's attention though[1] and that is no bad thing :) Memory freed back to the OS is a problem, I think less so if FR still holds onto it. If you have a bad actor in there, they can just read the credentials in the clear as they pass through or export the TLS keying material. Cheers Alex [1] https://github.com/FreeRADIUS/freeradius-server/commit/f3b8d2a67a54d9bec5551... [2] ideally fronting the cache insertion with a Bloomfilter so you only insert on the *second* hit to prevent this but you would need to either use Redis as your backend (which has support for sketches) or call out to Perl or (eugh) Python for that; the latter will not work as well for distributed caches but better than nothing.
Alexander Clouter via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
On Thu, 29 Oct 2026, at 17:07, Brian Julin wrote:
But, we are waaayyy over half BYOD clients here, no GPO or Intune. It's less about whether the cert may leave the machine and more about whether the machine may leave the user.
I would be surprised if those users had not set up for their machine to cache/remember their password.
Oh absolutely. Of course, any who actually have good reason to care enough at least have the option not to, which they would not with certs. It's much easier to train our helpdesk/users about locked/expired account passwords than dealing with familiarizing them with the process of renewing/invalidating a particular machine certificate. Passwords eventually expire and users know how to deal with that. With certs you either set up an auto-renewal process, in which case a mis-appropriated machine will never lose access, or you have to hassle your users more often to go through re-onboarding since the password aging and cert lifetimes are not intrinsically syncronized. Actual stolen machines are, yes, a concern but the more common concern is one user helping another get their machine online resulting in the latter running around with the wrong set of privileges. Which is an AUP violation, but happens.
At least with the certificate it only provides them with network access, not then potentially everything (non-MFA'd) else.
True in most cases but just because user+password is used does not mean the user+password is used for something other than network. Had I my druthers, we'd have separate wifi passwords.
Maybe an option for you is to move the lockout from your IdP and into FreeRADIUS and instruct the IdP to not apply lock out (or at a much higher threshold) for authentications through your RADIUS server?
We do want lockouts, and the backend has to be the thing that does it, as it's used for more than serving RADIUS.
We hash the user/pass before storing the cache entry, and rotate the hash seed, which under FR3 is mere theater but once we are in FR4, where objects are wiped when freed, it will greatly reduce the theoretical exposure of retained creds in the AS server.
Not really sure what you mean about "rotating the seed".
We use a single-entry cache to host two seeds, the current and retiring one, and periodically age out the old seed. The cache that tracks whether a username+password tuple is a successful combination or how many times it has recently failed has a hash of the user+password seeded with one of these seeds as its key. If we did not rotate the seed, we'd have a bunch of hashes that could be dictionary attacked offline if swiped along with the seed, sitting in yet-to-be-recycled rbtree slots. With the rotation, we have many less of those, anything old is using a long since forgotten seed. The rest of the caches protect the tuple has from being filled up by (possibly unintentional) DoS as well as other AAA flooding vectors. Imperfectly, but with the realization that anyone who is doing so is, if they are smart, also rotating through usernames and calling-station-ids, so one of them is just a rate limit on called-station-id since they probably can't afford a physically distributed system from which to launch a spraying attack.
Looks like you got Alan's attention though[1] and that is no bad thing :)
LoL.
Memory freed back to the OS is a problem, I think less so if FR still holds onto it.
Agreed. But I nuke things from orbit. It's the only way to be sure :-)
participants (4)
-
Alan DeKok -
Alexander Clouter -
Brian Julin -
Olivier