Hi! On Wed, 2026-03-25 at 11:48 +1300, Alan DeKok wrote:
Hm... the code looks like it processes the response and adds attributes for the 401 case. But I haven't tried it in detail myself.
That was our original understanding from reading the table[1] in the documentation as well. But while researching why this doesn't work, I noticed that apparent exception noted in [2]. [1]: https://www.freeradius.org/documentation/freeradius-server/4.0.0/reference/r... [2]: https://www.freeradius.org/documentation/freeradius-server/4.0.0/reference/r...
However, when a client is rejected, we would like to also update `mycompany-reject-reason` but with the rejection message coming from the REST call, which returns a http/401 like this:
{ "control:mycompany-reason": "why the client was rejected" }
That sounds useful, yes.
However, this does not work: no attributes are updated, and this gets pritned to the logs:
Perhaps try running the server in debug mode, as suggested pretty much everywhere. Set up a test system. VMs are free.
Sorry, should have done that at the start. I attached a rejection trace at the end. I believe the relevant parts are these: (14) # Executing section post-auth from file /etc/raddb/sites-enabled/default (14) post-auth { ... rlm_rest (rest): Reserved connection (0) (14) rest: Expanding URI components (14) rest: Sending HTTP POST to "http://localhost:5001/api/postauth/cert/user%40example.com?nas=10.11.12.13&c..." (14) rest: Processing response header (14) rest: Status : 401 (UNAUTHORIZED) (14) rest: Type : json (application/json) (14) rest: Adding reply:REST-HTTP-Status-Code = "401" (14) rest: ERROR: Server returned: (14) rest: ERROR: {"control:mycompany-reason":"Unknown CN user@example.com"} rlm_rest (rest): Released connection (0) (14) [rest] = invalid ... (14) Using Post-Auth-Type Reject (14) # Executing group from file /etc/raddb/sites-enabled/default (14) Post-Auth-Type REJECT { (14) update { (14) No attributes updated for RHS &control:mycompany-reason (14) } # update = noop The "ERROR: Server returned:" message looks like Freeradius did not expect any payload for a 401 :/ Thanks, tobi. The full log starts here: (14) Received Access-Request Id 113 from 10.11.12.13:53424 to 10.10.10.10:1812 length 201 (14) Service-Type = Framed-User (14) Framed-MTU = 1400 (14) User-Name = "anon@example.com" (14) State = 0x26125fd92b1d52e97be8526948788de2 (14) NAS-Port-Id = "1" (14) NAS-Port-Type = Wireless-802.11 (14) Acct-Session-Id = "82d000b3" (14) Calling-Station-Id = "AA-BB-CC-DD-EE-FF" (14) Called-Station-Id = "FF-EE-DD-CC-BB-AA" (14) EAP-Message = 0x020f00060d00 (14) NAS-Identifier = "nas01.example.org" (14) NAS-IP-Address = 10.11.12.13 (14) Message-Authenticator = 0x591b844ab0ca0b6cc3e43ce71146f962 (14) Restoring &session-state (14) &session-state:Framed-MTU = 994 (14) &session-state:TLS-Session-Information = "(TLS) recv TLS 1.3 Handshake, ClientHello" (14) &session-state:TLS-Session-Information = "(TLS) send TLS 1.2 Handshake, ServerHello" (14) &session-state:TLS-Session-Information = "(TLS) send TLS 1.2 Handshake, Certificate" (14) &session-state:TLS-Session-Information = "(TLS) send TLS 1.2 Handshake, ServerKeyExchange" (14) &session-state:TLS-Session-Information = "(TLS) send TLS 1.2 Handshake, CertificateRequest" (14) &session-state:TLS-Session-Information = "(TLS) send TLS 1.2 Handshake, ServerHelloDone" (14) &session-state:TLS-Session-Information = "(TLS) recv TLS 1.2 Handshake, Certificate" (14) &session-state:TLS-Session-Information = "(TLS) recv TLS 1.2 Handshake, ClientKeyExchange" (14) &session-state:TLS-Session-Information = "(TLS) recv TLS 1.2 Handshake, CertificateVerify" (14) &session-state:TLS-Session-Information = "(TLS) recv TLS 1.2 Handshake, Finished" (14) &session-state:TLS-Session-Information = "(TLS) send TLS 1.2 ChangeCipherSpec" (14) &session-state:TLS-Session-Information = "(TLS) send TLS 1.2 Handshake, Finished" (14) &session-state:TLS-Session-Cipher-Suite = "ECDHE-RSA-AES128-GCM-SHA256" (14) &session-state:TLS-Session-Version = "TLS 1.2" (14) # Executing section authorize from file /etc/raddb/sites-enabled/default (14) authorize { (14) policy filter_username { (14) if (&User-Name) { (14) if (&User-Name) -> TRUE (14) if (&User-Name) { (14) if (&User-Name =~ / /) { (14) if (&User-Name =~ / /) -> FALSE (14) if (&User-Name =~ /@[^@]*@/ ) { (14) if (&User-Name =~ /@[^@]*@/ ) -> FALSE (14) if (&User-Name =~ /\.\./ ) { (14) if (&User-Name =~ /\.\./ ) -> FALSE (14) if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/)) { (14) if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/)) -> FALSE (14) if (&User-Name =~ /\.$/) { (14) if (&User-Name =~ /\.$/) -> FALSE (14) if (&User-Name =~ /@\./) { (14) if (&User-Name =~ /@\./) -> FALSE (14) } # if (&User-Name) = notfound (14) } # policy filter_username = notfound (14) suffix: Checking for suffix after "@" (14) suffix: Looking up realm "example.com" for User-Name = "anon@example.com" (14) suffix: Found realm "example.com" (14) suffix: Adding Stripped-User-Name = "anon" (14) suffix: Adding Realm = "example.com" (14) suffix: Authentication realm is LOCAL (14) [suffix] = ok (14) if (!&Realm) { (14) if (!&Realm) -> FALSE (14) eap: Peer sent EAP Response (code 2) ID 15 length 6 (14) eap: No EAP Start, assuming it's an on-going EAP conversation (14) [eap] = updated (14) } # authorize = updated (14) Found Auth-Type = eap (14) # Executing group from file /etc/raddb/sites-enabled/default (14) Auth-Type eap { (14) eap: Expiring EAP session with state 0x26125fd92b1d52e9 (14) eap: Finished EAP session with state 0x26125fd92b1d52e9 (14) eap: Previous EAP request found for state 0x26125fd92b1d52e9, released from the list (14) eap: Peer sent packet with method EAP TLS (13) (14) eap: Calling submodule eap_tls to process data (14) eap_tls: (TLS) Peer ACKed our handshake fragment. handshake is finished (14) eap: Sending EAP Success (code 3) ID 15 length 4 (14) eap: Freeing handler (14) [eap] = ok (14) if (handled && (Response-Packet-Type == Access-Challenge)) { (14) if (handled && (Response-Packet-Type == Access-Challenge)) -> FALSE (14) } # Auth-Type eap = ok (14) # Executing section post-auth from file /etc/raddb/sites-enabled/default (14) post-auth { (14) update { (14) session-state:mycompany-Outer-Name := &User-Name -> 'anon@example.com' (14) } # update = noop (14) if (&control:Proxy-To-Realm) { (14) if (&control:Proxy-To-Realm) -> FALSE (14) else { (14) if (EAP-Message) { (14) if (EAP-Message) -> TRUE (14) if (EAP-Message) { (14) if (EAP-Type == TLS) { (14) if (EAP-Type == TLS) -> TRUE (14) if (EAP-Type == TLS) { (14) update { (14) Stripped-User-Name := &TLS-Client-Cert-Common-Name -> 'user@example.com' (14) User-Name := &TLS-Client-Cert-Common-Name -> 'user@example.com' (14) session-state:mycompany-Common-Name := &TLS-Client-Cert-Common-Name -> 'user@example.com' (14) session-state:mycompany-Auth-Method := "cert" (14) } # update = noop (14) update control { (14) &mycompany-Rest-Type := "cert" (14) } # update control = noop rlm_rest (rest): Reserved connection (0) (14) rest: Expanding URI components (14) rest: EXPAND http://localhost:5001 (14) rest: --> http://localhost:5001 (14) rest: EXPAND /api/postauth/%{control:mycompany-Rest-Type}/%{Stripped-User-Name}?nas=%{Packet-Src-IP-Address}&calledStationId=%{Called-Station-Id} (14) rest: --> /api/postauth/cert/user%40example.com?nas=10.11.12.13&calledStationId=FF-EE-DD-CC-BB-AA (14) rest: Sending HTTP POST to "http://localhost:5001/api/postauth/cert/user%40example.com?nas=10.11.12.13&c..." (14) rest: EXPAND restapiuser (14) rest: --> restapiuser (14) rest: EXPAND restapipassword (14) rest: --> restapipassword (14) rest: Processing response header (14) rest: Status : 401 (UNAUTHORIZED) (14) rest: Type : json (application/json) (14) rest: Adding reply:REST-HTTP-Status-Code = "401" (14) rest: ERROR: Server returned: (14) rest: ERROR: {"control:mycompany-reason":"Unknown CN user@example.com"} rlm_rest (rest): Released connection (0) (14) [rest] = invalid (14) } # if (EAP-Type == TLS) = invalid (14) } # if (EAP-Message) = invalid (14) } # else = invalid (14) } # post-auth = invalid (14) Using Post-Auth-Type Reject (14) # Executing group from file /etc/raddb/sites-enabled/default (14) Post-Auth-Type REJECT { (14) if (&control:Proxy-To-Realm) { (14) if (&control:Proxy-To-Realm) -> FALSE (14) update { (14) No attributes updated for RHS &control:mycompany-reason (14) session-state:mycompany-Outer-Name := &User-Name -> 'user@example.com' (14) } # update = noop (14) if (EAP-Message && !&session-state:mycompany-reason) { (14) if (EAP-Message && !&session-state:mycompany-reason) -> TRUE (14) if (EAP-Message && !&session-state:mycompany-reason) { (14) update { (14) session-state:mycompany-reason := &Module-Failure-Message -> 'rest: Server returned:' (14) } # update = noop (14) } # if (EAP-Message && !&session-state:mycompany-reason) = noop (14) policy rewrite_calling_station_id { (14) if (&Calling-Station-Id && (&Calling-Station-Id =~ /^([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})$/i)) { (14) if (&Calling-Station-Id && (&Calling-Station-Id =~ /^([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})$/i)) -> TRUE (14) if (&Calling-Station-Id && (&Calling-Station-Id =~ /^([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})$/i)) { (14) update request { (14) EXPAND %{toupper:%{1}%{2}%{3}%{4}%{5}%{6}} (14) --> 3A0412A35FD6 (14) &Calling-Station-Id := 3A0412A35FD6 (14) } # update request = noop (14) [updated] = updated (14) } # if (&Calling-Station-Id && (&Calling-Station-Id =~ /^([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})[^0-9a-f]?([0-9a-f]{2})$/i)) = updated (14) ... skipping else: Preceding "if" was taken (14) } # policy rewrite_calling_station_id = updated (14) attr_filter.access_reject: EXPAND %{User-Name} (14) attr_filter.access_reject: --> user@example.com (14) attr_filter.access_reject: Matched entry DEFAULT at line 11 (14) [attr_filter.access_reject] = updated (14) } # Post-Auth-Type REJECT = updated (14) Rejected in post-auth: [user@example.com/<via Auth-Type = eap>] (from client nas01.example.com port 0 cli AABBCCDDEEFF) (14) Login incorrect (rest: Server returned:): [user@example.com/<via Auth-Type = eap>] (from client nas01.example.com port 0 cli AABBCCDDEEFF) (14) Sent Access-Reject Id 113 from 10.10.10.10:1812 to 10.11.12.13:53424 length 44 (14) EAP-Message = 0x030f0004 (14) Message-Authenticator = 0x00000000000000000000000000000000 (14) Finished request