feat(freeradius): implement node pinning and dynamic IP allocation

- add node pinning logic in authorize (reject if wrong node)
- optimize SQL queries using control variables
- assign Pool-Name dynamically for engineers
- integrate sqlippool for dynamic IP allocation (engineers only)
- add Session-Timeout aligned with lease_duration
- fix duplicate sqlippool execution in post-auth/accounting
- configure sqlippool with NAS-IP scoping for multi-node isolation

Ensures correct routing, tenant isolation, and scalable IP management.
This commit is contained in:
2026-04-20 16:15:55 +02:00
parent fca66dfa52
commit 69a4e3a3b8
2 changed files with 45 additions and 15 deletions
+2 -2
View File
@@ -30,7 +30,7 @@ sqlippool {
# That way the NAS will automatically kick the user offline when the
# lease expires.
#
lease_duration = 86400
lease_duration = 18000
#
# Timeout between each consecutive 'allocate_clear' queries (default: 1s)
@@ -76,7 +76,7 @@ sqlippool {
# pool_key = "%{NAS-Port}"
# pool_key = "%{Calling-Station-Id}"
pool_key = "%{User-Name}"
nas_ip_address = "%{NAS-IP-Address}"
################################################################
#
# WARNING: MySQL (MyISAM) has certain limitations that means it can
+34 -4
View File
@@ -418,20 +418,31 @@ authorize {
#
# See "Authorization Queries" in mods-available/sql
sql
### Node pinning + client type (optimized)
update control {
Tmp-String-0 := "%{sql:SELECT node_ip FROM node_assignments WHERE username='%{User-Name}'}"
Tmp-String-1 := "%{sql:SELECT client_type FROM node_assignments WHERE username='%{User-Name}'}"
}
# No assignment → reject
if (&control:Tmp-String-0 == "") {
reject
}
if (&control:Tmp-String-0 != "%{NAS-IP-Address}") {
# Wrong node → reject (string compare to avoid type mismatch)
if ("%{control:Tmp-String-0}" != "%{NAS-IP-Address}") {
update reply {
Reply-Message := "Wrong node"
Reply-Message := "Wrong node (%{NAS-IP-Address}) expected %{control:Tmp-String-0}"
}
reject
}
# Engineers → dynamic pool
if (&control:Tmp-String-1 == "engineer") {
update control {
Pool-Name := "engineers-%{sql:SELECT SUBSTRING_INDEX('%{User-Name}','@',-1)}"
}
}
# If you are using /etc/smbpasswd, and are also doing mschap authentication, the un-comment this line, and configure the 'smbpasswd' module.
# smbpasswd
@@ -632,8 +643,17 @@ accounting {
# Return an address to the IP Pool when we see a stop record.
# Ensure that &control:Pool-Name is set to determine which pool of IPs are used.
sqlippool
# sqlippool
### rebuild Pool-Name
update control {
Pool-Name := "engineers-%{sql:SELECT SUBSTRING_INDEX('%{User-Name}','@',-1)}"
}
### apply only for engineers
if (&control:Pool-Name =~ /^engineers-/) {
sqlippool
}
# Log traffic to an SQL database.
# See "Accounting queries" in mods-available/sql
sql
@@ -765,8 +785,18 @@ post-auth {
#
# Ensure that &control:Pool-Name is set to determine which
# pool of IPs are used.
sqlippool
# sqlippool
# Engineers → dynamic IP
#
if (&control:Pool-Name =~ /^engineers-/) {
update reply {
Session-Timeout := 3600
}
sqlippool
}
# Create the CUI value and add the attribute to Access-Accept.
# Uncomment the line below if *returning* the CUI.