Configure FreeRADIUS attributes (AVPs)
This how-to describes the necessary configuration changes for the LinOTP Smart Virtual Appliance (SVA) to add RADIUS attribute value pairs (AVPs) to authentication responses depending on group memberships.
Configure FreeRADIUS
The SVA generates some system configuration settings of the operating system via a set of meta files. This also applies for the most relevant parts of the FreeRADIUS AVPs configuration.
If you don't use the LinOTP SVA you have to apply the changes directly to the FreeRADIUS configuration files in /etc/freeradius.
Log in to the SVA and activate the root shell:
unsupported
Change the mako files
Navigate to the Appliance configuration directory:
cd /etc/lseappliance/config-templates
Backup the original meta configuration file /etc/lseappliance/config-templates/etc-freeradius-modules-ldap.mako and apply the necessary changes:
cp -a etc-freeradius-modules-ldap.mako etc-freeradius-modules-ldap.mako.backup nano etc-freeradius-modules-ldap.mako
Configure the connection details to the AD/LDAP and what should be used as group filter.
New file content:
## <% ldap = config.get('radius', {}).get('ldap', {}) %> ldap { # # Note that this needs to match the name in the LDAP # server certificate, if you're using ldaps. # server = "${ldap.get('server', '')}" # identity = "${ldap.get('binddn', '')}" # password = "${ldap.get('password', '')}" # basedn = "${ldap.get('basedn', '')}" # filter = "(${ldap.get('loginattr', # '')}=%{%{Stripped-User-Name}:-%{User-Name}})" #base_filter = "(objectclass=radiusprofile)" ################# ### IP or FQDN of your AD/LDAP server (if you use ldaps mind the FQDN needs to ### match the server certificate server = "192.168.122.207" ### the credentials to acces AD/LDAP identity = "cn=query,dc=example,dc=net" password = "PASSWORD" ### The part of the DIT you want to connect to basedn = "dc=example,dc=net" ### The string used as sAMAccountName for LDAP query. If the variable ### %{Stripped-User-Name} is defined it is used. Otherwise fall back to ### %{User-Name} taken from the authentication request. See section ### "additional changes" in this guide for how to generate the stripped ### user name filter = "(sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}})" ################ # How many connections to keep open to the LDAP server. # This saves time over opening a new LDAP socket for # every authentication request. ldap_connections_number = 5 # seconds to wait for LDAP query to finish. default: 20 timeout = 4 # seconds LDAP server has to process the query (server-side # time limit). default: 20 # # LDAP_OPT_TIMELIMIT is set to this value. timelimit = 3 # # seconds to wait for response of the server. (network # failures) default: 10 # # LDAP_OPT_NETWORK_TIMEOUT is set to this value. net_timeout = 1 # Mapping of RADIUS dictionary attributes to LDAP # directory attributes. <%text> dictionary_mapping = ${confdir}/ldap.attrmap </%text> groupname_attribute = cn # groupmembership_filter = # "(|(&(objectClass=groupOfNames)(member=%{control:Ldap-UserDn}))(&(objectClass=groupOfUniqueNames)(uniquemember=%{control:Ldap-UserDn})))" ########### ## The LDAP search query for group membership groupmembership_filter = "(&(objectClass=group)(member=%{control:Ldap-UserDn}))" ########### groupmembership_attribute = radiusGroupName }
Backup the meta configuration file etc-freeradius-sites-available-linotp.mako and apply the necessary changes:
cp -a etc-freeradius-sites-available-linotp.mako etc-freeradius-sites-available-linotp.mako.backup nano etc-freeradius-sites-available-linotp.mako
In this file the checks for group memberships are configured and the actions triggered (setting AVPs) depending on a successful validation.
Only the section "post-auth" needs to be changed:
post-auth { ######## ### Add the ldap module to be processed ldap ####### exec Post-Auth-Type REJECT { attr_filter.access_reject } ########## ### Test for membership of group "networkers" and set AVP "Cisco-AVPair" if (LDAP-Group == "networkers") { update reply { Cisco-AVPair = "networktobeconfigured" } } else { update reply { Cisco-AVPair = "groupnotfound" } } ### Test for membership and set attribute Class if (LDAP-Group == "administrators") { update reply { Class += "admin" } } else { update reply { Class += "user" } } ### Test for membership to group "vpn" and set one more attribute Class if (LDAP-Group == "vpn" ) { update reply { Class += "vpn" } } else { update reply { Class += "local" } } ########### }
Regenerate the FreeRADIUS configuration files based on the modifications done before:
appliance_configure.py -c generate_config -o radius
This will create a new versions of /etc/freeradius/modules/ldap and /etc/freeradius/sites-enabled/linotp
Additional changes
In some cases it might be necessary to modify more FreeRADIUS configuration files directly. In this example the realm/domain part is stripped from the user name. This can be useful if the sAMAccountName contains the name only - a query for group membership with name+domain will fail because a user of this name can not be found.
Preprocessing changes can be configured in /etc/freeradius/hints. The following lines will define a variable %{Stripped-User-Name} as it is used above in etc-freeradius-modules-ldap.mako.
DEFAULT User-Name =~ "(.+)@([^@]+)$" Stripped-User-Name := "%{1}
Restart FreeRADIUS
To apply the changes FreeRADIUS needs to be restarted:
service freeradius restart
Troubleshooting
To verify if FreeRADIUS responses with the correct AVPs or to debug problems FreeRADIUS can be started interactively from unsupported mode.
- First stop the running service:
service freeradius stop
- Start FreeRADIUS in debug mode:
freeradius -X
- Trigger the login of a user (or use e.g. radtest for a simulation)
There should be lines of membership checks and a response containing the desired AVPs:
++? if (LDAP-Group == "networkers") ... rlm_ldap::ldap_groupcmp: User found in group networkers [ldap] ldap_release_conn: Release Id: 0 ? Evaluating (LDAP-Group == "networkers") -> TRUE ... ++? if (LDAP-Group == "administrators") ... rlm_ldap::ldap_groupcmp: ldap_get_values() failed [ldap] ldap_release_conn: Release Id: 0 ? Evaluating (LDAP-Group == "administrators") -> FALSE ... ++? if (LDAP-Group == "vpn") ... rlm_ldap::ldap_groupcmp: User found in group vpn [ldap] ldap_release_conn: Release Id: 0 ? Evaluating (LDAP-Group == "vpn" ) -> TRUE ... Sending Access-Accept of id 144 to 192.168.122.1 port 34554 Cisco-AVPair = "networktobeconfigured" Class = 0x75736572 Class = 0x76706e
If radtest is used on client side it should show something like this:
rad_recv: Access-Accept packet from host 192.168.122.224 port 1812, id=144, length=60 Cisco-AVPair = "networktobeconfigured" Class = 0x75736572 Class = 0x76706e