rlm_rest: update attributes on http/401 response?
GIRSTMAIR Tobias
tobias.girstmair at tirol.gv.at
Wed Mar 25 08:14:50 UTC 2026
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/raddb/mods-available/rest.html#_recv
[2]: https://www.freeradius.org/documentation/freeradius-server/4.0.0/reference/raddb/mods-available/rest.html#:~:text=except%20in%20the%20case%20of%20a%20401%20code
>
> > 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&calledStationId=FF-EE-DD-CC-BB-AA"
(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 at 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 at 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 at 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 at 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 at example.com'
(14) User-Name := &TLS-Client-Cert-Common-Name -> 'user at example.com'
(14) session-state:mycompany-Common-Name := &TLS-Client-Cert-Common-Name -> 'user at 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&calledStationId=FF-EE-DD-CC-BB-AA"
(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 at 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 at 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 at 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 at example.com/<via Auth-Type = eap>] (from client nas01.example.com port 0 cli AABBCCDDEEFF)
(14) Login incorrect (rest: Server returned:): [user at 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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 2791 bytes
Desc: S/MIME Cryptographic Signature
URL: <http://lists.freeradius.org/pipermail/freeradius-users/attachments/20260325/1d2a121d/attachment-0001.bin>
More information about the Freeradius-Users
mailing list