I’m working on implementing a Simultaneous-Use check using the rediswho module in the FreeRADIUS 3.2 branch. The approach is inspired by the blog post “Preventing Fraudulent Logins with a Session Database” (which unfortunately appears to be offline). At this stage, I’m successfully writing accounting records into Redis through rediswho. However, I haven’t found any documentation or examples describing how to query Redis for Simultaneous-Use checking and verification — specifically, the equivalent of the simul_count_query and simul_verify_query mechanisms used in the SQL module If anyone has implemented this or can provide guidance on best practices for performing Simultaneous-Use checks with rediswho, I would appreciate any insights or references. Additionally, I noticed that the default rediswho configuration in both versions 3.2 and 4.0 uses the same insert operation for all Acct-Status-Type values. As a result, when querying the Redis, it’s not possible to distinguish between sessions that have terminated with an Acct-Status-Type = Stop and those that are still active. Nitzan
On Oct 8, 2025, at 8:46 AM, Nitzan Tzelniker via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
I’m working on implementing a Simultaneous-Use check using the rediswho module in the FreeRADIUS 3.2 branch. The approach is inspired by the blog post “Preventing Fraudulent Logins with a Session Database” (which unfortunately appears to be offline).
That happens. It's also why we don't recommend using third-party documentation. Not only is most of it wrong, but it tends to disappear over time.
At this stage, I’m successfully writing accounting records into Redis through rediswho. However, I haven’t found any documentation or examples describing how to query Redis for Simultaneous-Use checking and verification — specifically, the equivalent of the simul_count_query and simul_verify_query mechanisms used in the SQL module
Hmm, yes. That does appear to be missing. We'd have to add code to the rlm_rediswho module in order to support that.
If anyone has implemented this or can provide guidance on best practices for performing Simultaneous-Use checks with rediswho, I would appreciate any insights or references.
I think it would require code changes. The Simultaneous-Use functionality does some odd internal magic IIRC. You might be able to get away with manual redis queries, but I haven't looked into that.
Additionally, I noticed that the default rediswho configuration in both versions 3.2 and 4.0 uses the same insert operation for all Acct-Status-Type values. As a result, when querying the Redis, it’s not possible to distinguish between sessions that have terminated with an Acct-Status-Type = Stop and those that are still active.
Ah, good point. That should be fixed, too. I'll make some notes in GitHub. Alan DeKok.
In the meantime, I considered the following implementation approach. Please let me know if it sounds reasonable: Session Key Structure: Append the Acct-Unique-Session-Id to the User-Name to form the Redis list name, using a separator such as : (which is invalid in usernames) — e.g. %{User-Name}:%{Acct-Unique-Session-Id}. Interim-Update Handling: Use LSET 0 instead of LPUSH to overwrite the existing session data during Interim-Update events. Stop Records: Use DEL on Stop packets to remove the corresponding session entry. NAS Reload Tracking: For Accounting-On and Accounting-Off events, create Redis keys with the NAS IP address and timestamp, similar to how the SQL nasreload mechanism works. Session Validation Logic (via Python script under session): a. The script performs Redis SCAN 0 MATCH %{User-Name}:* to find all active session keys for the user. b. If the number of sessions returned is less than the configured Simultaneous-Use limit, the Access-Request is accepted. c. Otherwise, for each matching session key: Retrieve the session data using LRANGE and parse it. If the NAS reload timestamp is newer than the last update, accept the Access-Request and Redis DEL the session. Otherwise, run radcheck (or equivalent Python logic) to verify if the user is still actually logged in: If yes → reject the Access-Request. If no → accept the Access-Request and Redis DEL the stale session. By the way, for FreeRADIUS v4.0, what is the recommended method to implement Simultaneous-Use, given that the checksimul functionality has been removed from all modules? Nitzan On Wed, Oct 8, 2025 at 5:35 PM Alan DeKok <alan.dekok@inkbridge.io> wrote:
On Oct 8, 2025, at 8:46 AM, Nitzan Tzelniker via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
I’m working on implementing a Simultaneous-Use check using the rediswho module in the FreeRADIUS 3.2 branch. The approach is inspired by the blog post “Preventing Fraudulent Logins with a Session Database” (which unfortunately appears to be offline).
That happens. It's also why we don't recommend using third-party documentation. Not only is most of it wrong, but it tends to disappear over time.
At this stage, I’m successfully writing accounting records into Redis through rediswho. However, I haven’t found any documentation or examples describing how to query Redis for Simultaneous-Use checking and verification — specifically, the equivalent of the simul_count_query and simul_verify_query mechanisms used in the SQL module
Hmm, yes. That does appear to be missing. We'd have to add code to the rlm_rediswho module in order to support that.
If anyone has implemented this or can provide guidance on best practices for performing Simultaneous-Use checks with rediswho, I would appreciate any insights or references.
I think it would require code changes. The Simultaneous-Use functionality does some odd internal magic IIRC.
You might be able to get away with manual redis queries, but I haven't looked into that.
Additionally, I noticed that the default rediswho configuration in both versions 3.2 and 4.0 uses the same insert operation for all Acct-Status-Type values. As a result, when querying the Redis, it’s not possible to distinguish between sessions that have terminated with an Acct-Status-Type = Stop and those that are still active.
Ah, good point. That should be fixed, too.
I'll make some notes in GitHub.
Alan DeKok.
Catching up on email after traveling too much...
On Oct 8, 2025, at 12:21 PM, Nitzan Tzelniker <nitzan.tzelniker@gmail.com> wrote: In the meantime, I considered the following implementation approach. Please let me know if it sounds reasonable:
Session Key Structure: Append the Acct-Unique-Session-Id to the User-Name to form the Redis list name, using a separator such as : (which is invalid in usernames) — e.g. %{User-Name}:%{Acct-Unique-Session-Id}.
The Acct-Unique-Session-Id should be unique enough, but I understand why it needs the User-Name.
Interim-Update Handling: Use LSET 0 instead of LPUSH to overwrite the existing session data during Interim-Update events.
Mostly, except that accounting packets can come out of order. The session should also have a "last updated time". If the current packet Event-Timestamp (or received time - Acct-Delay-Time) is less than that, then don't change the entry in Redis. Otherwise, update the entry, including the "last updated time".
Stop Records: Use DEL on Stop packets to remove the corresponding session entry.
I wouldn't delete the records. Accounting packets can be out of order. So the "last updated time" check should catch this, too. Records should be deleted if they're (a) closed, and (b) haven't seen an update in a day or so.
NAS Reload Tracking: For Accounting-On and Accounting-Off events, create Redis keys with the NAS IP address and timestamp, similar to how the SQL nasreload mechanism works.
Yes
Session Validation Logic (via Python script under session): a. The script performs Redis SCAN 0 MATCH %{User-Name}:* to find all active session keys for the user.
I'm not sure that a Python script is necessary. You should be able to do this in unlang. Or, if complex queries are needed, use a Lua script which is loaded into the Redis DB.
b. If the number of sessions returned is less than the configured Simultaneous-Use limit, the Access-Request is accepted. c. Otherwise, for each matching session key: Retrieve the session data using LRANGE and parse it. If the NAS reload timestamp is newer than the last update, accept the Access-Request and Redis DEL the session. Otherwise, run radcheck (or equivalent Python logic) to verify if the user is still actually logged in: If yes → reject the Access-Request. If no → accept the Access-Request and Redis DEL the stale session.
That should work.
By the way, for FreeRADIUS v4.0, what is the recommended method to implement Simultaneous-Use, given that the checksimul functionality has been removed from all modules?
The unlang in v4 has enough features that you can write the simultaneous-use checks directly in unlang. We do need to add examples of that, though. I'll add it to the list of things to do in v4. Alan DeKok.
participants (2)
-
Alan DeKok -
Nitzan Tzelniker