FR users & maintainers, I have a functional FreeRADIUS installation (3.0.20 - I know it's slightly old, it's from the Ubuntu repo - on Ubuntu 20.04LTS), being used for management access and authorization on varied network equipment at an ISP (not everything supports TACACS so we're using RADIUS). When I first setup this system, Alan and other members of this group gave me a bunch of help and I threw together a policy.d file (called "group_authz") that is querying for LDAP group membership, and then altering the replies back to the various NASes with specific attributes to grant read-only or read-write permissions on the devices. I was originally told by my boss that a universal read-only and read-write group would be sufficient, so it was pretty easy to check for membership in one of those two groups and then just send back specific attributes depending on %{client:nas_type}. Of course, now the goalposts have shifted. I've been told that we need "tiered" access. Some users (tier 2 NOC team) should get R/W to most of the network, but should *not* have any access at all to some of the more sensitive gear (core/datacenter network stuff). Others (tier 1 NOC team) should get only R/O to the same chunk of network, but not the sensitive stuff. We still need a "full R/W" (tier3/architects) and "full R/O" (probably won't get used much, but just in case) as well. I'm now trying to figure out the "best" / most elegant way to handle this new requirement. I'm thinking that it would be helpful to use group nesting to make things more simple on the LDAP side, but I don't know if LDAP-Group[*] will catch nested membership (user is a member of group X, which is a member of group Y - does LDAP-Group show group Y)? I'll also need to define the "sensitive" gear somehow (either via NAS name or IP address as defined in clients.conf) and return a reject for those cases... Below is the entirety of my "group_authz" file from policy.d/ so you know what I'm doing now. I am far from a developer, so I'm open to any and all ideas to improve this (in case I've done something stupid), as well as meet my new "tiered" requirements. Thanks all! Braden McGrath braden@big-geek.net group_authz { if (&LDAP-Group[*] == "cn=network-fulladmin,${modules.ldap.group.base_dn}") { switch "%{client:nas_type}" { case mikrotik { # For Mikrotik routers update reply { &Mikrotik-Group := "full" } return } case dell_os10 { # For Dell OS10 switches update reply { &Cisco-AVPair += 'shell:roles="sysadmin"' } return } case juniper { # For Juniper / JunOS devices update reply { &Juniper-Local-User-Name := "SUPER" } return } case cisco { update reply { &Cisco-AVPair += "shell:priv-lvl=15" # &Service-Type := "Administrative-User" } return } case awplus { # AlliedWare Plus - Allied Telesis switches (and others) # https://www.alliedtelesis.com/sites/default/files/documents/configuration-gu... # NOTE: AWPlus treats any Service-Type other than "Administrative-User" or "NAS-Prompt-User" as ACCESS-REJECT update reply { &Service-Type := "Administrative-User" } return } case { # default case - shouldn't ever match! update reply { &Reply-Message := "Could not find NAS_Type - check clients.conf?" } reject } } # debug_reply return } elsif (&LDAP-Group[*] == "cn=network-readonly,${modules.ldap.group.base_dn}") { switch "%{client:nas_type}" { case mikrotik { # Obviously, this group gets read access... update reply { &Mikrotik-Group := "read" } return } case dell_os10 { # On the Dells, this allows normal "op" actions but can't read/dump config update reply { &Cisco-AVPair += 'shell:roles="netoperator"' } return } case juniper { # true read-only for now, can use "OPR" if we want to allow clearing counters / etc update reply { &Juniper-Local-User-Name := "RO" } return } case cisco { update reply { # priv-lvl=1 is basic RO / not-enabled on Cisco, not "7" like Allied Telesis &Cisco-AVPair += "shell:priv-lvl=1" #&Service-Type := "NAS-Prompt-User" } return } case awplus { # this should be readonly/show commands for Allied Telesis # Definitely need both of these for priv7 level, drop the AVPair for just priv1 update reply { &Cisco-AVPair += "shell:priv-lvl=7" &Service-Type := "NAS-Prompt-User" } return } case { # default case, shouldn't ever match update reply { &Reply-Message := "Could not find NAS_Type - check clients.conf?" } reject } } return } else { update reply { &Reply-Message := "User not authorized - check directory/LDAP group membership" } reject } }