Netgate Discussion Forum
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Search
    • Register
    • Login

    FreeRADIUS EAP-TLS, works but and "Check Client Certificate CN" doesn't

    Scheduled Pinned Locked Moved pfSense Packages
    4 Posts 3 Posters 2.5k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • L
      Lobanz
      last edited by

      So I have WPA2/Enterprise with EAP-TLS working on Windows and Android with Unifi APs against FreeRADIUS on pfsense+.

      BUT, there is an EAP-TLS sessing in Services -> FreeRADIUS -> EAP -> EAP-TLS called "Check Client Certificate CN". The description says "When enabled, the Common Name of the client certificate must match the username set in 'FreeRADIUS > Users'."

      Can't get this setting to work. I can make a user with username "foo" and a cert with common name "foo" and everything works great. Problem is that I can change the "foo" user's FreeRADIUS username to "bob" and it still works. On Windows and Android WiFi clients, you have to specify an "identity" (username) but it seems to be ignored by FreeRADIUS.

      SO, it looks like that when EAP-TLS is in use that the FreeRADIUS users don't really matter. I CAN revoke the certificate (and then bounce FreeRADIUS) and the user can't login.

      In addition, there doesn't seem to be a way to turn off other EAP auth methods like EAP-TTLS, so IF there is a user defined in FreeRADIUS and I revoke their cert so they can't use EAP-TLS, they can switch to EAP-TTLS (no client cert) as long as they know the password for the user.

      Am I not getting something?

      BTW, here's some things I had to do to get it working.

      • Use a publicly trusted SSL server cert for FreeRADIUS. I used a Let's Encrypt cert via the pfsense acme package (which was awesome)
      • Issue all your certs off your root CA. Don't use an intermediate CA. Everything works with the intermediate except CRL checking.
      • On Windows, define your WiFi connection in Control Panel -> Network & Sharing Center -> Setup New Connection. You dont have the options you need in the half-baked modern GUI.
      • On Android, you have to type in a "domain". This is the FQDN of the publicly trusted FreeRADIUS SSL server cert.
      • EAP-TLS and EAP-TTLS/MS-CHAP-V2 both work. EAP-TTLS uses the FreeRADIUS username/password. EAP-TLS just uses the client cert (but you have to specify an "identity" (username) in both Windows and Android, but, as I mention above, it seems to be ignored by FreeRADIUS.
      P johnpozJ 2 Replies Last reply Reply Quote 0
      • P
        pavannani @Lobanz
        last edited by

        @Lobanz can you share full documentation on how to configure EAP-TLS and what changes need to be done in Windows to connect wifi

        1 Reply Last reply Reply Quote 0
        • johnpozJ
          johnpoz LAYER 8 Global Moderator @Lobanz
          last edited by

          @Lobanz said in FreeRADIUS EAP-TLS, works but and "Check Client Certificate CN" doesn't:

          "When enabled, the Common Name of the client certificate must match the username set in 'FreeRADIUS > Users'."

          Hmmm, I do believe this use to work.. But yeah seems I am able to duplicate the issue. I have that checked.

          check.jpg

          I even completely deleted the freerad user, and still auths just fine..

          An intelligent man is sometimes forced to be drunk to spend time with his fools
          If you get confused: Listen to the Music Play
          Please don't Chat/PM me for help, unless mod related
          SG-4860 24.11 | Lab VMs 2.7.2, 24.11

          P 1 Reply Last reply Reply Quote 0
          • P
            pavannani @johnpoz
            last edited by

            @johnpoz
            @johnpoz

            Below is my configuration for EAP-tls but I am unable to connect to wifi for windows
            radiusd.conf

            prefix = /usr/local
            exec_prefix = ${prefix}
            sysconfdir = ${prefix}/etc
            localstatedir = /var
            sbindir = ${exec_prefix}/sbin
            logdir = ${localstatedir}/log
            raddbdir = ${sysconfdir}/raddb
            radacctdir = ${logdir}/radacct
            name = radiusd
            confdir = ${raddbdir}
            modconfdir = ${confdir}/mods-config
            certdir = ${confdir}/certs
            cadir = ${confdir}/certs
            run_dir = ${localstatedir}/run
            db_dir = ${raddbdir}
            libdir = /usr/local/lib/freeradius-3.2.3
            pidfile = ${run_dir}/${name}.pid
            max_request_time = 30
            cleanup_delay = 5
            max_requests = 1024
            hostname_lookups = no
            regular_expressions = yes
            extended_expressions = yes

            log {
            destination = files
            colourise = yes
            file = ${logdir}/radius.log
            syslog_facility = daemon
            stripped_names = yes
            auth = yes
            auth_badpass = yes
            auth_goodpass = yes
            msg_goodpass = ""
            msg_badpass = ""
            msg_denied = "You are already logged in - access denied"
            }

            checkrad = ${sbindir}/checkrad
            security {
            allow_core_dumps = no
            max_attributes = 200
            reject_delay = 1
            status_server = no
            # Disable this check since it may not be accurate due to how FreeBSD patches OpenSSL
            allow_vulnerable_openssl = yes
            }

            $INCLUDE clients.conf
            thread pool {
            start_servers = 5
            max_servers = 32
            min_spare_servers = 3
            max_spare_servers = 10
            max_queue_size = 65536
            max_requests_per_server = 0
            auto_limit_acct = no
            }

            modules {
            $INCLUDE ${confdir}/mods-enabled/
            }

            instantiate {
            exec
            expr
            expiration
            logintime
            ### Dis-/Enable sql instatiate
            #sql
            daily
            weekly
            monthly
            forever
            }
            policy {
            $INCLUDE policy.d/
            }
            $INCLUDE sites-enabled/


            EAP

            EAP

            eap {
            default_eap_type = tls
            timer_expire = 60
            ignore_unknown_eap_types = no
            cisco_accounting_username_bug = no
            max_sessions = 4096

            DISABLED WEAK EAP TYPES MD5, GTC

            pwd {

            group = 19

            server_id = theserver@example.com

            fragment_size = 1020

            virtual_server = "inner-tunnel"

            }

            tls-config tls-common {
            	# private_key_password = whatever
            	private_key_file = ${certdir}/server_key.pem
            	certificate_file = ${certdir}/server_cert.pem
            	ca_path = ${confdir}/certs
            	ca_file = ${ca_path}/ca_cert.pem
            #	auto_chain = yes
            #	psk_identity = "test"
            #	psk_hexphrase = "036363823"
            	dh_file = ${certdir}/dh
            	random_file = /dev/urandom
            	fragment_size = 1024
            	include_length = yes
            	check_crl = no
            	### check_cert_issuer = "/C=GB/ST=Berkshire/L=Newbury/O=My Company Ltd/emailAddress=test@mycomp.com/CN=myca" ###
            	### check_cert_cn = %{User-Name} ###
            	cipher_list = "DEFAULT"
            	cipher_server_preference = no
            

            disable_tlsv1_2 = no

            	ecdh_curve = "prime256v1"
            	tls_min_version = "1.2"
            	cache {
            		enable = no
            		lifetime = 24
            		max_entries = 255
            		#name = "EAP module"
            		#persist_dir = "/tlscache"
            	}
            	verify {
            #		skip_if_ocsp_ok = no
            #		tmpdir = /tmp/radiusd
            #		client = "/path/to/openssl verify -CApath ${..ca_path} %{TLS-Client-Cert-Filename}"
            	}
            	ocsp {
            		enable = no
            		override_cert_url = no
            		url = "http://127.0.0.1/ocsp/"
            		# use_nonce = yes
            		# timeout = 0
            		# softfail = no
            	}
            }
            tls {
            	tls = tls-common
            #	virtual_server = check-eap-tls
            }
            ttls {
            	tls = tls-common
            	default_eap_type = md5
            	copy_request_to_tunnel = no
            	include_length = yes
            #	require_client_cert = yes
            	virtual_server = "inner-tunnel-ttls"
            	#use_tunneled_reply is deprecated, new method happens in virtual-server
            }	### end ttls
            peap {
            	tls = tls-common
            	default_eap_type = mschapv2
            	copy_request_to_tunnel = no
            #	proxy_tunneled_request_as_eap = yes
            #	require_client_cert = yes
            

            MS SoH Server is disabled

            	virtual_server = "inner-tunnel-peap"
            	#use_tunneled_reply is deprecated, new method happens in virtual-server
            }
            mschapv2 {
            

            send_error = no

            identity = "FreeRADIUS"

            }
            

            fast {

            tls = tls-common

            pac_lifetime = 604800

            authority_identity = "1234"

            pac_opaque_key = "0123456789abcdef0123456789ABCDEF"

            virtual_server = inner-tunnel

            }

            }

            /usr/local/etc/raddb/clients.conf

            client "wifi" {
            ipaddr = 192.168.0.1
            proto = udp
            secret = '123456789'
            require_message_authenticator = no
            nas_type = other
            ### login = !root ###
            ### password = someadminpass ###
            limit {
            max_connections = 16
            lifetime = 0
            idle_timeout = 30
            }
            }


            default

            server default {
            listen {
            type = auth
            ipaddr = *
            port = 1812
            }

            authorize {

            filter_username

            filter_password

            preprocess
            

            operator-name

            cui

            AUTHORIZE FOR PLAIN MAC-AUTH IS DISABLED

            auth_log

            chap
            mschap
            digest
            

            wimax

            IPASS

            suffix
            ntdomain
            eap {
            	ok = return
            

            updated = return

            }
            

            unix

            files
            if ((notfound || noop) && ("%{%{Control:Auth-Type}:-No-Accept}" != "Accept")) {
            		### sql DISABLED ###
            if (true) {
            
            		### ldap ###
            		if (notfound || noop) {
            			reject
            		}
            	}
            }
            
            -daily
            -weekly
            -monthly
            -forever
            # Formerly checkval
            if (&request:Calling-Station-Id == &control:Calling-Station-Id) {
            	ok
            }
            expiration
            logintime
            pap
            Autz-Type Status-Server {
            
            }
            

            }

            authenticate {
            Auth-Type PAP {
            pap
            }
            Auth-Type CHAP {
            chap
            }
            Auth-Type MS-CHAP {
            mschap
            }
            mschap
            Auth-Type MOTP {
            motp
            }
            Auth-Type GOOGLEAUTH {
            googleauth
            }
            digest

            pam

            unix

            #Auth-Type LDAP {
            	#ldap
            	#### ldap2 disabled ###
            #}
            
            eap
            

            Auth-Type eap {

            eap {

            handled = 1

            }

            if (handled && (Response-Packet-Type == Access-Challenge)) {

            attr_filter.access_challenge.post-auth

            handled # override the "updated" code from attr_filter

            }

            }

            }

            preacct {
            preprocess

            ACCOUNTING FOR PLAIN MAC-AUTH DISABLED

            acct_counters64

            update request {
            	&FreeRADIUS-Acct-Session-Start-Time = "%{expr: %l - %{%{Acct-Session-Time}:-0} - %{%{Acct-Delay-Time}:-0}}"
            }
            

            acct_unique

            IPASS

            suffix
            ntdomain
            files
            

            }

            accounting {

            cui

            detail
            ### This makes it possible to run the datacounter_acct module only on accounting-stop and interim-updates
            if ((request:Acct-Status-Type == Stop) || (request:Acct-Status-Type == Interim-Update)) {
            	datacounterdaily
            	datacounterweekly
            	datacountermonthly
            	datacounterforever
            }
            

            unix

            radutmp
            

            sradutmp

            main_pool

            ### sql DISABLED ###
            daily
            weekly
            monthly
            forever
            

            if (noop) {

            ok

            }

            pgsql-voip

            exec
            attr_filter.accounting_response
            Acct-Type Status-Server {
            
            }
            

            }

            session {

            radutmp

            radutmp
            

            }

            post-auth {

            if (!&reply:State) {

            update reply {

            State := "0x%{randstr:16h}"

            }

            }

            update {
            	&reply: += &session-state:
            }
            

            main_pool

            cui

            reply_log

            sql DISABLED

            ldap

            exec
            

            wimax

            update reply {

            Reply-Message += "%{TLS-Cert-Serial}"

            Reply-Message += "%{TLS-Cert-Expiration}"

            Reply-Message += "%{TLS-Cert-Subject}"

            Reply-Message += "%{TLS-Cert-Issuer}"

            Reply-Message += "%{TLS-Cert-Common-Name}"

            Reply-Message += "%{TLS-Cert-Subject-Alt-Name-Email}"

            Reply-Message += "%{TLS-Client-Cert-Serial}"

            Reply-Message += "%{TLS-Client-Cert-Expiration}"

            Reply-Message += "%{TLS-Client-Cert-Subject}"

            Reply-Message += "%{TLS-Client-Cert-Issuer}"

            Reply-Message += "%{TLS-Client-Cert-Common-Name}"

            Reply-Message += "%{TLS-Client-Cert-Subject-Alt-Name-Email}"

            }

            insert_acct_class

            if (&reply:EAP-Session-Id) {

            update reply {

            EAP-Key-Name := &reply:EAP-Session-Id

            }

            }

            remove_reply_message_if_eap
            Post-Auth-Type REJECT {
            	# log failed authentications in SQL, too.
            	# sql
            	attr_filter.access_reject
            	eap
            	remove_reply_message_if_eap
            }
            Post-Auth-Type Challenge {
            
            }
            

            }

            pre-proxy {

            operator-name

            cui

            files

            attr_filter.pre-proxy
            

            pre_proxy_log

            }

            post-proxy {

            post_proxy_log

            attr_filter.post-proxy
            eap
            

            Post-Proxy-Type Fail-Accounting {

            detail

            }

            }
            }


            1 Reply Last reply Reply Quote 0
            • First post
              Last post
            Copyright 2025 Rubicon Communications LLC (Netgate). All rights reserved.