Fetching memberOf attribute
Hi Guys, I cannot get the ldap module to fetch a memberOf attribute from AD - it is not putting it as a filter parameter in the LDAP search packet. I need this list later to search through for port-authentication phase through the groups in session-state:LDAP-Group[*]'s to identify which ones are present and return a proper VSA. The LDAP configuration is as follows: ldap { server = 'abc.nomios.nl' identity = 'CN=ABC,OU=ABC,DC=nomios,DC=nl' password = '#######' base_dn = 'DC=nomios,DC=nl' user { base_dn = "${..base_dn}" filter = "(sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}})" scope = 'sub' access_attr = "memberOf" attribute { memberOf := 'session-state:LDAP-Group' } } # group { # base_dn = "${..base_dn}" # membership_attribute = 'memberOf' # } options { chase_referrals = no rebind = yes ldap_debug = 1 timeout = 10 timelimit = 3 net_timeout = 1 idle = 60 } tls { start_tls = no } update { session-state:LDAP-Group := "%{ldap:memberOf}" } } If I run manually ldap_search, then membership shows. Wireshark confirms that there is no attribute request in the ldap search packet present. Any help will be greatly appreciated! Best regards Matvey Teplov +31 62 705 12 73
On May 14, 2025, at 11:18 AM, Matvey Teplov via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
I cannot get the ldap module to fetch a memberOf attribute from AD - it is not putting it as a filter parameter in the LDAP search packet. I need this list later to search through for port-authentication phase through the groups in session-state:LDAP-Group[*]'s to identify which ones are present and return a proper VSA. The LDAP configuration is as follows:
Or, you can read the documentation which tells how to post to the list http://wiki.freeradius.org/list-help When you join the list, you get a message containing that URL, and a strongly worded comment saying you should read it.
If I run manually ldap_search, then membership shows. Wireshark confirms that there is no attribute request in the ldap search packet present.
If only there was some kind of "server debugging output" which tells you what the server is doing.
Any help will be greatly appreciated!
We did help. We wrote tons of documentation which tells you what to do. Please read it. Alan DeKok.
Good afternoon Everyone, @Alan DeKok<mailto:aland@deployingradius.com>, thank you for pointing me to the right direction. I cannot get the attributes to be: 1. filtered to the bare necessity - I don't need them all present in the reply and there are 39 of them. I just need a memberOf from the AD. 2. Having the memberOf attribute recognised - system comes back with: "ERROR: String passed does not look like an LDAP URL" Any help will be appreciated! Cheers Here is the configuration: mteplov@scorpio:~/temp$ cat ldap ldap { server = 'ms-ad.csec.ms.nomios.nl' identity = 'CN=Radius Proxy NomiosLabs,OU=Servce Accounts,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl' password = 'SECRET' base_dn = 'DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl' user { base_dn = "${..base_dn}" filter = "(sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}})" scope = 'sub' uid_attribute = "sAMAccountName" attribute { LDAP-Group := memberOf } } # attributes { # memberof = "session-state:LDAP-Group" # } #user_dn = "${.:instance}-LDAP-UserDn" # group { # base_dn = "${..base_dn}" # filter = '(objectClass=group)' # name_attribute = cn # # use magic AD search to get all groups # membership_filter = "(member:1.2.840.113556.1.4.1941:=%{control:${..user_dn}})" # # enable cache module # cacheable_dn = 'yes' # cache_attribute = "${..:instance}-LDAP-Group" # } options { chase_referrals = no rebind = yes ldap_debug = 2147483647 timeout = 10 timelimit = 3 net_timeout = 1 idle = 60 } tls { start_tls = no } } mteplov@scorpio:~/temp$ cat default server default { listen { type = auth ipaddr = * port = 1825 limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } pre-proxy { } post-proxy { attr_filter.post-proxy if (1) { update reply { Reply-Message += " Session-State LDAP-Group: %{session-state:LDAP-Group}" } } } authorize { preprocess auth_log chap # suffix if (&request:User-Name) { update session-state { Persisted-User-Name := "%{request:User-Name}" } } update control { Proxy-To-Realm := "radius_server" } ldap if (1) { update reply { Reply-Message := "DN: %{ldap:dn}" Reply-Message += ", memberOf: %{ldap:memberOf[*]}" Reply-Message += ", mail: %{ldap:mail}" Reply-Message += ", sAMAccountName: %{ldap:sAMAccountName}" } } } authenticate { } post-auth { # Debugging: Let's see what's in the ldap namespace if (1) { update reply { Reply-Message += " Session-State LDAP-Group: %{session-state:LDAP-Group}" } } update session-state { LDAP-Group := "%{ldap:memberOf}" } if (&session-state:LDAP-Group[*]) { update reply { Reply-Message := "Groups: %{session-state:LDAP-Group[*]}" } } else { update reply { Reply-Message := "No LDAP groups found" } } if (&session-state:LDAP-Group[*] == "CN=Radius_Admin_Group,OU=NomiosLabs,OU=Roles,OU=Nomios Services,OU=Nomios,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl") { update reply { Fortinet-Group-Name := "admin_group" Juniper-Local-User-Name := "admin_role" Reply-Message := "Authorized as Admin" } } elsif (&session-state:LDAP-Group[*] == "CN=Radius_ReadOnly_Group,OU=NomiosLabs,OU=Roles,OU=Nomios Services,OU=Nomios,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl") { update reply { Fortinet-Group-Name := "read_only_group" Juniper-Local-User-Name := "read_only_role" Reply-Message := "Authorized as Read-Only" } } elsif (&session-state:LDAP-Group[*] == "CN=Radius_ReadWrite_Group,OU=NomiosLabs,OU=Roles,OU=Nomios Services,OU=Nomios,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl") { update reply { Fortinet-Group-Name := "read_write_group" Juniper-Local-User-Name := "read_write_role" Reply-Message := "Authorized as Read-Write" } } else { update control { Auth-Type := Reject } update reply { Persisted-User-Name := "%{session-state:Persisted-User-Name}" Reply-Message := "Access denied: Unauthorized group." } } } } And here is the log: FreeRADIUS Version 3.0.20 Copyright (C) 1999-2019 The FreeRADIUS server project and contributors There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE You may redistribute copies of FreeRADIUS under the terms of the GNU General Public License For more information about these matters, see the file named COPYRIGHT Starting - reading configuration files ... including dictionary file /usr/share/freeradius/dictionary including dictionary file /usr/share/freeradius/dictionary.dhcp including dictionary file /usr/share/freeradius/dictionary.vqp including dictionary file /etc/freeradius/3.0/dictionary including configuration file /etc/freeradius/3.0/radiusd.conf including configuration file /etc/freeradius/3.0/proxy.conf including configuration file /etc/freeradius/3.0/clients.conf including files in directory /etc/freeradius/3.0/mods-enabled/ including configuration file /etc/freeradius/3.0/mods-enabled/chap including configuration file /etc/freeradius/3.0/mods-enabled/ldap including configuration file /etc/freeradius/3.0/mods-enabled/detail.log including configuration file /etc/freeradius/3.0/mods-enabled/pap including configuration file /etc/freeradius/3.0/mods-enabled/radutmp including configuration file /etc/freeradius/3.0/mods-enabled/ntlm_auth including configuration file /etc/freeradius/3.0/mods-enabled/attr_filter including configuration file /etc/freeradius/3.0/mods-enabled/detail including configuration file /etc/freeradius/3.0/mods-enabled/preprocess including configuration file /etc/freeradius/3.0/mods-enabled/dynamic_clients including configuration file /etc/freeradius/3.0/mods-enabled/linelog including configuration file /etc/freeradius/3.0/mods-enabled/logintime including files in directory /etc/freeradius/3.0/policy.d/ including configuration file /etc/freeradius/3.0/policy.d/control including configuration file /etc/freeradius/3.0/policy.d/ntlm_auth including configuration file /etc/freeradius/3.0/policy.d/abfab-tr including configuration file /etc/freeradius/3.0/policy.d/cui including configuration file /etc/freeradius/3.0/policy.d/dhcp including configuration file /etc/freeradius/3.0/policy.d/debug including configuration file /etc/freeradius/3.0/policy.d/eap including configuration file /etc/freeradius/3.0/policy.d/rfc7542 including configuration file /etc/freeradius/3.0/policy.d/accounting including configuration file /etc/freeradius/3.0/policy.d/canonicalization including configuration file /etc/freeradius/3.0/policy.d/filter including configuration file /etc/freeradius/3.0/policy.d/operator-name including configuration file /etc/freeradius/3.0/policy.d/moonshot-targeted-ids including files in directory /etc/freeradius/3.0/sites-enabled/ including configuration file /etc/freeradius/3.0/sites-enabled/default main { security { user = "freerad" group = "freerad" allow_core_dumps = no } name = "freeradius" prefix = "/usr" localstatedir = "/var" logdir = "/var/log/freeradius" run_dir = "/var/run/freeradius" } main { name = "freeradius" prefix = "/usr" localstatedir = "/var" sbindir = "/usr/sbin" logdir = "/var/log/freeradius" run_dir = "/var/run/freeradius" libdir = "/usr/lib/freeradius" radacctdir = "/var/log/freeradius/radacct" hostname_lookups = no max_request_time = 10 cleanup_delay = 5 max_requests = 16384 pidfile = "/var/run/freeradius/freeradius.pid" checkrad = "/usr/sbin/checkrad" debug_level = 0 proxy_requests = yes log { stripped_names = no auth = yes auth_badpass = yes auth_goodpass = no colourise = yes msg_denied = "You are already logged in - access denied" } resources { } security { max_attributes = 200 reject_delay = 1.000000 status_server = no require_message_authenticator = "auto" limit_proxy_state = "auto" } } radiusd: #### Loading Realms and Home Servers #### home_server radius_server1 { require_message_authenticator = "no" ipaddr = 100.127.1.24 port = 1823 type = "auth" secret = <<< secret >>> response_window = 30.000000 response_timeouts = 1 max_outstanding = 65536 zombie_period = 40 status_check = "none" ping_interval = 30 check_timeout = 4 num_answers_to_alive = 3 revive_interval = 300 limit { max_connections = 16 max_requests = 0 lifetime = 0 idle_timeout = 0 } coa { irt = 2 mrt = 16 mrc = 5 mrd = 30 } } Ignoring "response_window = 30.000000", forcing to "response_window = 10.000000" home_server radius_server2 { require_message_authenticator = "no" ipaddr = 100.127.1.102 port = 1823 type = "auth" secret = <<< secret >>> response_window = 30.000000 response_timeouts = 1 max_outstanding = 65536 zombie_period = 40 status_check = "none" ping_interval = 30 check_timeout = 4 num_answers_to_alive = 3 revive_interval = 300 limit { max_connections = 16 max_requests = 0 lifetime = 0 idle_timeout = 0 } coa { irt = 2 mrt = 16 mrc = 5 mrd = 30 } } Ignoring "response_window = 30.000000", forcing to "response_window = 10.000000" home_server_pool radius_pool { type = fail-over home_server = radius_server2 } realm radius_server { auth_pool = radius_pool } radiusd: #### Loading Clients #### client localhost { ipaddr = * require_message_authenticator = "yes" secret = <<< secret >>> shortname = "localhost" nas_type = "other" proto = "*" limit { max_connections = 1 lifetime = 0 idle_timeout = 30 } } client NMS_APPS_1 { ipaddr = 172.31.50.0/24 secret = <<< secret >>> proto = "*" limit { max_connections = 10 lifetime = 300 idle_timeout = 300 } } client NMS_APPS_2 { ipaddr = 172.31.60.0/24 secret = <<< secret >>> proto = "*" limit { max_connections = 10 lifetime = 300 idle_timeout = 300 } } Debug state unknown (cap_sys_ptrace capability not set) systemd watchdog is disabled radiusd: #### Instantiating modules #### modules { # Loaded module rlm_chap # Loading module "chap" from file /etc/freeradius/3.0/mods-enabled/chap # Loaded module rlm_ldap # Loading module "ldap" from file /etc/freeradius/3.0/mods-enabled/ldap ldap { server = "ms-ad.csec.ms.nomios.nl" identity = "CN=Radius Proxy NomiosLabs,OU=Servce Accounts,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl" password = <<< secret >>> sasl { } user { scope = "sub" access_positive = yes sasl { } } group { scope = "sub" name_attribute = "cn" cacheable_name = no cacheable_dn = no allow_dangling_group_ref = no } client { scope = "sub" base_dn = "" } profile { } options { ldap_debug = 2147483647 chase_referrals = no rebind = yes net_timeout = 1 res_timeout = 20 srv_timelimit = 20 idle = 60 probes = 3 interval = 30 } tls { start_tls = no } } Creating attribute LDAP-Group # Loaded module rlm_detail # Loading module "auth_log" from file /etc/freeradius/3.0/mods-enabled/detail.log detail auth_log { filename = "/var/log/freeradius/radacct/%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}}/auth-detail-%Y%m%d" header = "%t" permissions = 384 locking = no escape_filenames = no log_packet_header = no } # Loading module "reply_log" from file /etc/freeradius/3.0/mods-enabled/detail.log detail reply_log { filename = "/var/log/freeradius/radacct/%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}}/reply-detail-%Y%m%d" header = "%t" permissions = 384 locking = no escape_filenames = no log_packet_header = no } # Loading module "pre_proxy_log" from file /etc/freeradius/3.0/mods-enabled/detail.log detail pre_proxy_log { filename = "/var/log/freeradius/radacct/%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}}/pre-proxy-detail-%Y%m%d" header = "%t" permissions = 384 locking = no escape_filenames = no log_packet_header = no } # Loading module "post_proxy_log" from file /etc/freeradius/3.0/mods-enabled/detail.log detail post_proxy_log { filename = "/var/log/freeradius/radacct/%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}}/post-proxy-detail-%Y%m%d" header = "%t" permissions = 384 locking = no escape_filenames = no log_packet_header = no } # Loaded module rlm_pap # Loading module "pap" from file /etc/freeradius/3.0/mods-enabled/pap pap { normalise = yes } # Loaded module rlm_radutmp # Loading module "radutmp" from file /etc/freeradius/3.0/mods-enabled/radutmp radutmp { filename = "/var/log/freeradius/radutmp" username = "%{User-Name}" case_sensitive = yes check_with_nas = yes permissions = 384 caller_id = yes } # Loaded module rlm_exec # Loading module "ntlm_auth" from file /etc/freeradius/3.0/mods-enabled/ntlm_auth exec ntlm_auth { wait = yes program = "/usr/bin/ntlm_auth --request-nt-key --username=%{User-Name} --password=%{User-Password}" shell_escape = yes } # Loaded module rlm_attr_filter # Loading module "attr_filter.post-proxy" from file /etc/freeradius/3.0/mods-enabled/attr_filter attr_filter attr_filter.post-proxy { filename = "/etc/freeradius/3.0/mods-config/attr_filter/post-proxy" key = "%{Realm}" relaxed = no } # Loading module "attr_filter.pre-proxy" from file /etc/freeradius/3.0/mods-enabled/attr_filter attr_filter attr_filter.pre-proxy { filename = "/etc/freeradius/3.0/mods-config/attr_filter/pre-proxy" key = "%{Realm}" relaxed = no } # Loading module "attr_filter.access_reject" from file /etc/freeradius/3.0/mods-enabled/attr_filter attr_filter attr_filter.access_reject { filename = "/etc/freeradius/3.0/mods-config/attr_filter/access_reject" key = "%{User-Name}" relaxed = no } # Loading module "attr_filter.access_challenge" from file /etc/freeradius/3.0/mods-enabled/attr_filter attr_filter attr_filter.access_challenge { filename = "/etc/freeradius/3.0/mods-config/attr_filter/access_challenge" key = "%{User-Name}" relaxed = no } # Loading module "attr_filter.accounting_response" from file /etc/freeradius/3.0/mods-enabled/attr_filter attr_filter attr_filter.accounting_response { filename = "/etc/freeradius/3.0/mods-config/attr_filter/accounting_response" key = "%{User-Name}" relaxed = no } # Loading module "detail" from file /etc/freeradius/3.0/mods-enabled/detail detail { filename = "/var/log/freeradius/radacct/%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}}/detail-%Y%m%d" header = "%t" permissions = 384 locking = no escape_filenames = no log_packet_header = no } # Loaded module rlm_preprocess # Loading module "preprocess" from file /etc/freeradius/3.0/mods-enabled/preprocess preprocess { with_ascend_hack = no ascend_channels_per_line = 23 with_ntdomain_hack = no with_specialix_jetstream_hack = no with_cisco_vsa_hack = no with_alvarion_vsa_hack = no } # Loaded module rlm_dynamic_clients # Loading module "dynamic_clients" from file /etc/freeradius/3.0/mods-enabled/dynamic_clients # Loaded module rlm_linelog # Loading module "linelog" from file /etc/freeradius/3.0/mods-enabled/linelog linelog { filename = "/var/log/freeradius/linelog" escape_filenames = no syslog_severity = "info" permissions = 384 format = "This is a log message for %{User-Name}" reference = "messages.%{%{reply:Packet-Type}:-default}" } # Loading module "log_accounting" from file /etc/freeradius/3.0/mods-enabled/linelog linelog log_accounting { filename = "/var/log/freeradius/linelog-accounting" escape_filenames = no syslog_severity = "info" permissions = 384 format = "" reference = "Accounting-Request.%{%{Acct-Status-Type}:-unknown}" } # Loaded module rlm_logintime # Loading module "logintime" from file /etc/freeradius/3.0/mods-enabled/logintime logintime { minimum_timeout = 60 } instantiate { } # Instantiating module "ldap" from file /etc/freeradius/3.0/mods-enabled/ldap rlm_ldap: libldap vendor: OpenLDAP, version: 20449 rlm_ldap (ldap): Couldn't find configuration for accounting, will return NOOP for calls from this section rlm_ldap (ldap): Couldn't find configuration for post-auth, will return NOOP for calls from this section rlm_ldap (ldap): Initialising connection pool pool { start = 5 min = 5 max = 10 spare = 3 uses = 0 lifetime = 0 cleanup_interval = 30 idle_timeout = 60 retry_delay = 1 spread = no } rlm_ldap (ldap): Opening additional connection (0), 1 of 10 pending slots used rlm_ldap (ldap): Connecting to ldap://ms-ad.csec.ms.nomios.nl:389 rlm_ldap (ldap): Waiting for bind result... rlm_ldap (ldap): Bind successful rlm_ldap (ldap): Opening additional connection (1), 1 of 9 pending slots used rlm_ldap (ldap): Connecting to ldap://ms-ad.csec.ms.nomios.nl:389 rlm_ldap (ldap): Waiting for bind result... rlm_ldap (ldap): Bind successful rlm_ldap (ldap): Opening additional connection (2), 1 of 8 pending slots used rlm_ldap (ldap): Connecting to ldap://ms-ad.csec.ms.nomios.nl:389 rlm_ldap (ldap): Waiting for bind result... rlm_ldap (ldap): Bind successful rlm_ldap (ldap): Opening additional connection (3), 1 of 7 pending slots used rlm_ldap (ldap): Connecting to ldap://ms-ad.csec.ms.nomios.nl:389 rlm_ldap (ldap): Waiting for bind result... rlm_ldap (ldap): Bind successful rlm_ldap (ldap): Opening additional connection (4), 1 of 6 pending slots used rlm_ldap (ldap): Connecting to ldap://ms-ad.csec.ms.nomios.nl:389 rlm_ldap (ldap): Waiting for bind result... rlm_ldap (ldap): Bind successful # Instantiating module "auth_log" from file /etc/freeradius/3.0/mods-enabled/detail.log rlm_detail (auth_log): 'User-Password' suppressed, will not appear in detail output # Instantiating module "reply_log" from file /etc/freeradius/3.0/mods-enabled/detail.log # Instantiating module "pre_proxy_log" from file /etc/freeradius/3.0/mods-enabled/detail.log rlm_detail (pre_proxy_log): 'User-Password' suppressed, will not appear in detail output # Instantiating module "post_proxy_log" from file /etc/freeradius/3.0/mods-enabled/detail.log # Instantiating module "pap" from file /etc/freeradius/3.0/mods-enabled/pap # Instantiating module "attr_filter.post-proxy" from file /etc/freeradius/3.0/mods-enabled/attr_filter reading pairlist file /etc/freeradius/3.0/mods-config/attr_filter/post-proxy # Instantiating module "attr_filter.pre-proxy" from file /etc/freeradius/3.0/mods-enabled/attr_filter reading pairlist file /etc/freeradius/3.0/mods-config/attr_filter/pre-proxy # Instantiating module "attr_filter.access_reject" from file /etc/freeradius/3.0/mods-enabled/attr_filter reading pairlist file /etc/freeradius/3.0/mods-config/attr_filter/access_reject # Instantiating module "attr_filter.access_challenge" from file /etc/freeradius/3.0/mods-enabled/attr_filter reading pairlist file /etc/freeradius/3.0/mods-config/attr_filter/access_challenge # Instantiating module "attr_filter.accounting_response" from file /etc/freeradius/3.0/mods-enabled/attr_filter reading pairlist file /etc/freeradius/3.0/mods-config/attr_filter/accounting_response # Instantiating module "detail" from file /etc/freeradius/3.0/mods-enabled/detail # Instantiating module "preprocess" from file /etc/freeradius/3.0/mods-enabled/preprocess # Instantiating module "linelog" from file /etc/freeradius/3.0/mods-enabled/linelog # Instantiating module "log_accounting" from file /etc/freeradius/3.0/mods-enabled/linelog # Instantiating module "logintime" from file /etc/freeradius/3.0/mods-enabled/logintime } # modules radiusd: #### Loading Virtual Servers #### server { # from file /etc/freeradius/3.0/radiusd.conf } # server server default { # from file /etc/freeradius/3.0/sites-enabled/default # Loading authorize {...} # Loading post-proxy {...} # Loading post-auth {...} } # server default radiusd: #### Opening IP addresses and Ports #### listen { type = "auth" ipaddr = * port = 1825 limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } Listening on auth address * port 1825 bound to server default Listening on proxy address * port 51834 Ready to process requests (0) Received Access-Request Id 38 from 127.0.0.1:47469 to 127.0.0.1:1825 length 101 (0) Message-Authenticator = 0xe1c8fe585dddd97d45029bb9dcc5bd5d (0) User-Name = "generic-user" (0) User-Password = "ommited" (0) NAS-IP-Address = 172.17.0.6 (0) NAS-Port = 1 (0) # Executing section authorize from file /etc/freeradius/3.0/sites-enabled/default (0) authorize { (0) [preprocess] = ok (0) auth_log: EXPAND /var/log/freeradius/radacct/%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}}/auth-detail-%Y%m%d (0) auth_log: --> /var/log/freeradius/radacct/127.0.0.1/auth-detail-20250515 (0) auth_log: /var/log/freeradius/radacct/%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}}/auth-detail-%Y%m%d expands to /var/log/freeradius/radacct/127.0.0.1/auth-detail-20250515 (0) auth_log: EXPAND %t (0) auth_log: --> Thu May 15 12:01:36 2025 (0) [auth_log] = ok (0) [chap] = noop (0) if (&request:User-Name) { (0) if (&request:User-Name) -> TRUE (0) if (&request:User-Name) { (0) update session-state { (0) EXPAND %{request:User-Name} (0) --> generic-user (0) Persisted-User-Name := generic-user (0) } # update session-state = noop (0) } # if (&request:User-Name) = noop (0) update control { (0) Proxy-To-Realm := "radius_server" (0) } # update control = noop rlm_ldap (ldap): Reserved connection (0) (0) ldap: EXPAND (sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}}) (0) ldap: --> (sAMAccountName=generic-user) (0) ldap: Performing search in "DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl" with filter "(sAMAccountName=generic-user)", scope "sub" ldap_search_ext put_filter: "(sAMAccountName=generic-user)" put_filter: simple put_simple_filter: "sAMAccountName=generic-user" ldap_build_search_req ATTRS:€û ldap_send_initial_request ldap_send_server_request (0) ldap: Waiting for search result... ldap_result ld 0x55d84d24efc0 msgid 2 wait4msg ld 0x55d84d24efc0 msgid 2 (timeout 20000000 usec) wait4msg continue ld 0x55d84d24efc0 msgid 2 all 1 ** ld 0x55d84d24efc0 Connections: * host: ms-ad.csec.ms.nomios.nl port: 389 (default) refcnt: 2 status: Connected last used: Thu May 15 12:01:36 2025 ** ld 0x55d84d24efc0 Outstanding Requests: * msgid 2, origid 2, status InProgress outstanding referrals 0, parent count 0 ld 0x55d84d24efc0 request count 1 (abandoned 0) ** ld 0x55d84d24efc0 Response Queue: Empty ld 0x55d84d24efc0 response count 0 ldap_chkResponseList ld 0x55d84d24efc0 msgid 2 all 1 ldap_chkResponseList returns ld 0x55d84d24efc0 NULL ldap_int_select read1msg: ld 0x55d84d24efc0 msgid 2 all 1 read1msg: ld 0x55d84d24efc0 msgid 2 message type search-entry wait4msg ld 0x55d84d24efc0 19 s 989759 us to go wait4msg continue ld 0x55d84d24efc0 msgid 2 all 1 ** ld 0x55d84d24efc0 Connections: * host: ms-ad.csec.ms.nomios.nl port: 389 (default) refcnt: 2 status: Connected last used: Thu May 15 12:01:36 2025 ** ld 0x55d84d24efc0 Outstanding Requests: * msgid 2, origid 2, status InProgress outstanding referrals 0, parent count 0 ld 0x55d84d24efc0 request count 1 (abandoned 0) ** ld 0x55d84d24efc0 Response Queue: * msgid 2, type 100 ld 0x55d84d24efc0 response count 1 ldap_chkResponseList ld 0x55d84d24efc0 msgid 2 all 1 ldap_chkResponseList returns ld 0x55d84d24efc0 NULL ldap_int_select read1msg: ld 0x55d84d24efc0 msgid 2 all 1 read1msg: ld 0x55d84d24efc0 msgid 2 message type search-reference adding response ld 0x55d84d24efc0 msgid 2 type 115: wait4msg ld 0x55d84d24efc0 19 s 989704 us to go wait4msg continue ld 0x55d84d24efc0 msgid 2 all 1 ** ld 0x55d84d24efc0 Connections: * host: ms-ad.csec.ms.nomios.nl port: 389 (default) refcnt: 2 status: Connected last used: Thu May 15 12:01:36 2025 ** ld 0x55d84d24efc0 Outstanding Requests: * msgid 2, origid 2, status InProgress outstanding referrals 0, parent count 0 ld 0x55d84d24efc0 request count 1 (abandoned 0) ** ld 0x55d84d24efc0 Response Queue: * msgid 2, type 100 chained responses: * msgid 2, type 115 ld 0x55d84d24efc0 response count 1 ldap_chkResponseList ld 0x55d84d24efc0 msgid 2 all 1 ldap_chkResponseList returns ld 0x55d84d24efc0 NULL ldap_int_select read1msg: ld 0x55d84d24efc0 msgid 2 all 1 read1msg: ld 0x55d84d24efc0 msgid 2 message type search-reference adding response ld 0x55d84d24efc0 msgid 2 type 115: wait4msg ld 0x55d84d24efc0 19 s 989648 us to go wait4msg continue ld 0x55d84d24efc0 msgid 2 all 1 ** ld 0x55d84d24efc0 Connections: * host: ms-ad.csec.ms.nomios.nl port: 389 (default) refcnt: 2 status: Connected last used: Thu May 15 12:01:36 2025 ** ld 0x55d84d24efc0 Outstanding Requests: * msgid 2, origid 2, status InProgress outstanding referrals 0, parent count 0 ld 0x55d84d24efc0 request count 1 (abandoned 0) ** ld 0x55d84d24efc0 Response Queue: * msgid 2, type 100 chained responses: * msgid 2, type 115 * msgid 2, type 115 ld 0x55d84d24efc0 response count 1 ldap_chkResponseList ld 0x55d84d24efc0 msgid 2 all 1 ldap_chkResponseList returns ld 0x55d84d24efc0 NULL ldap_int_select read1msg: ld 0x55d84d24efc0 msgid 2 all 1 read1msg: ld 0x55d84d24efc0 msgid 2 message type search-reference adding response ld 0x55d84d24efc0 msgid 2 type 115: wait4msg ld 0x55d84d24efc0 19 s 989602 us to go wait4msg continue ld 0x55d84d24efc0 msgid 2 all 1 ** ld 0x55d84d24efc0 Connections: * host: ms-ad.csec.ms.nomios.nl port: 389 (default) refcnt: 2 status: Connected last used: Thu May 15 12:01:36 2025 ** ld 0x55d84d24efc0 Outstanding Requests: * msgid 2, origid 2, status InProgress outstanding referrals 0, parent count 0 ld 0x55d84d24efc0 request count 1 (abandoned 0) ** ld 0x55d84d24efc0 Response Queue: * msgid 2, type 100 chained responses: * msgid 2, type 115 * msgid 2, type 115 * msgid 2, type 115 ld 0x55d84d24efc0 response count 1 ldap_chkResponseList ld 0x55d84d24efc0 msgid 2 all 1 ldap_chkResponseList returns ld 0x55d84d24efc0 NULL ldap_int_select read1msg: ld 0x55d84d24efc0 msgid 2 all 1 read1msg: ld 0x55d84d24efc0 msgid 2 message type search-result read1msg: ld 0x55d84d24efc0 0 new referrals read1msg: mark request completed, ld 0x55d84d24efc0 msgid 2 request done: ld 0x55d84d24efc0 msgid 2 res_errno: 0, res_error: <>, res_matched: <> ldap_free_request (origid 2, msgid 2) adding response ld 0x55d84d24efc0 msgid 2 type 101: ldap_parse_result ldap_get_dn (0) ldap: User object found at DN "CN=Generic Copy MS User,OU=Users,OU=nomios.nl,OU=Nomios,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl" ldap_msgfree rlm_ldap (ldap): Released connection (0) (0) [ldap] = ok (0) if (1) { (0) if (1) -> TRUE (0) if (1) { (0) update reply { (0) ERROR: String passed does not look like an LDAP URL (0) EXPAND DN: %{ldap:dn} (0) --> DN: (0) Reply-Message := DN: (0) ERROR: String passed does not look like an LDAP URL (0) EXPAND , memberOf: %{ldap:memberOf[*]} (0) --> , memberOf: (0) Reply-Message += , memberOf: (0) ERROR: String passed does not look like an LDAP URL (0) EXPAND , mail: %{ldap:mail} (0) --> , mail: (0) Reply-Message += , mail: (0) ERROR: String passed does not look like an LDAP URL (0) EXPAND , sAMAccountName: %{ldap:sAMAccountName} (0) --> , sAMAccountName: (0) Reply-Message += , sAMAccountName: (0) } # update reply = noop (0) } # if (1) = noop (0) } # authorize = ok (0) Starting proxy to home server 100.127.1.102 port 1823 (0) server default { (0) } (0) Proxying request to home server 100.127.1.102 port 1823 timeout 10.000000 (0) Sent Access-Request Id 198 from 0.0.0.0:51834 to 100.127.1.102:1823 length 111 (0) Message-Authenticator = 0xe1c8fe585dddd97d45029bb9dcc5bd5d (0) User-Name = "generic-user" (0) User-Password = "ommited" (0) NAS-IP-Address = 172.17.0.6 (0) NAS-Port = 1 (0) Event-Timestamp = "May 15 2025 12:01:36 UTC" (0) Proxy-State = 0x3338 Waking up in 0.3 seconds. (0) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! (0) BlastRADIUS check: Received packet without Message-Authenticator from home_server radius_server2 (0) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! (0) The packet does not contain Message-Authenticator, which is a security issue (0) UPGRADE THE HOME SERVER AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK. (0) Once the home server is upgraded, set "require_message_authenticator = true" for home_server radius_server2 (0) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! (0) Marking home server 100.127.1.102 port 1823 alive (0) Clearing existing &reply: attributes (0) Received Access-Accept Id 198 from 100.127.1.102:1823 to 172.17.0.6:51834 length 50 (0) Reply-Message = "Recovered as generic-user" (0) server default { (0) # Executing section post-proxy from file /etc/freeradius/3.0/sites-enabled/default (0) post-proxy { (0) attr_filter.post-proxy: EXPAND %{Realm} (0) attr_filter.post-proxy: --> radius_server (0) attr_filter.post-proxy: Matched entry DEFAULT at line 1 (0) [attr_filter.post-proxy] = updated (0) if (1) { (0) if (1) -> TRUE (0) if (1) { (0) update reply { (0) EXPAND Session-State LDAP-Group: %{session-state:LDAP-Group} (0) --> Session-State LDAP-Group: (0) Reply-Message += Session-State LDAP-Group: (0) } # update reply = noop (0) } # if (1) = noop (0) } # post-proxy = updated (0) } (0) Found Auth-Type = Accept (0) Auth-Type = Accept, accepting the user (0) # Executing section post-auth from file /etc/freeradius/3.0/sites-enabled/default (0) post-auth { (0) if (1) { (0) if (1) -> TRUE (0) if (1) { (0) update reply { (0) EXPAND Session-State LDAP-Group: %{session-state:LDAP-Group} (0) --> Session-State LDAP-Group: (0) Reply-Message += Session-State LDAP-Group: (0) } # update reply = noop (0) } # if (1) = noop (0) update session-state { (0) ERROR: String passed does not look like an LDAP URL (0) EXPAND %{ldap:memberOf} (0) --> (0) LDAP-Group := (0) } # update session-state = noop (0) if (&session-state:LDAP-Group[*]) { (0) if (&session-state:LDAP-Group[*]) -> TRUE (0) if (&session-state:LDAP-Group[*]) { (0) update reply { (0) EXPAND Groups: %{session-state:LDAP-Group[*]} (0) --> Groups: (0) Reply-Message := Groups: (0) } # update reply = noop (0) } # if (&session-state:LDAP-Group[*]) = noop (0) ... skipping else: Preceding "if" was taken (0) if (&session-state:LDAP-Group[*] == "CN=Radius_Admin_Group,OU=NomiosLabs,OU=Roles,OU=Nomios Services,OU=Nomios,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl") { (0) if (&session-state:LDAP-Group[*] == "CN=Radius_Admin_Group,OU=NomiosLabs,OU=Roles,OU=Nomios Services,OU=Nomios,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl") -> FALSE (0) elsif (&session-state:LDAP-Group[*] == "CN=Radius_ReadOnly_Group,OU=NomiosLabs,OU=Roles,OU=Nomios Services,OU=Nomios,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl") { (0) elsif (&session-state:LDAP-Group[*] == "CN=Radius_ReadOnly_Group,OU=NomiosLabs,OU=Roles,OU=Nomios Services,OU=Nomios,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl") -> FALSE (0) elsif (&session-state:LDAP-Group[*] == "CN=Radius_ReadWrite_Group,OU=NomiosLabs,OU=Roles,OU=Nomios Services,OU=Nomios,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl") { (0) elsif (&session-state:LDAP-Group[*] == "CN=Radius_ReadWrite_Group,OU=NomiosLabs,OU=Roles,OU=Nomios Services,OU=Nomios,DC=ms-ad,DC=csec,DC=ms,DC=nomios,DC=nl") -> FALSE (0) else { (0) update control { (0) Auth-Type := Reject (0) } # update control = noop (0) update reply { (0) EXPAND %{session-state:Persisted-User-Name} (0) --> generic-user (0) Persisted-User-Name := generic-user (0) Reply-Message := "Access denied: Unauthorized group." (0) } # update reply = noop (0) } # else = noop (0) } # post-auth = noop (0) Login OK: [generic-user] (from client localhost port 1) (0) Sent Access-Accept Id 38 from 127.0.0.1:1825 to 127.0.0.1:47469 length 0 (0) Reply-Message := "Access denied: Unauthorized group." (0) Reply-Message = "Recovered as generic-user" (0) Reply-Message += " Session-State LDAP-Group: " (0) Persisted-User-Name := "generic-user" (0) Finished request Waking up in 4.9 seconds. (0) Cleaning up request packet ID 38 with timestamp +6 Ready to process requests ________________________________ From: Freeradius-Users <freeradius-users-bounces+matvey.teplov=nomios.nl@lists.freeradius.org> on behalf of Matvey Teplov via Freeradius-Users <freeradius-users@lists.freeradius.org> Sent: 14 May 2025 17:18 To: freeradius-users@lists.freeradius.org <freeradius-users@lists.freeradius.org> Cc: Matvey Teplov <matvey.teplov@nomios.nl> Subject: [EXTERNAL] Fetching memberOf attribute Hi Guys, I cannot get the ldap module to fetch a memberOf attribute from AD - it is not putting it as a filter parameter in the LDAP search packet. I need this list later to search through for port-authentication phase through the groups in session-state:LDAP-Group[*]'s to identify which ones are present and return a proper VSA. The LDAP configuration is as follows: ldap { server = 'abc.nomios.nl' identity = 'CN=ABC,OU=ABC,DC=nomios,DC=nl' password = '#######' base_dn = 'DC=nomios,DC=nl' user { base_dn = "${..base_dn}" filter = "(sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}})" scope = 'sub' access_attr = "memberOf" attribute { memberOf := 'session-state:LDAP-Group' } } # group { # base_dn = "${..base_dn}" # membership_attribute = 'memberOf' # } options { chase_referrals = no rebind = yes ldap_debug = 1 timeout = 10 timelimit = 3 net_timeout = 1 idle = 60 } tls { start_tls = no } update { session-state:LDAP-Group := "%{ldap:memberOf}" } } If I run manually ldap_search, then membership shows. Wireshark confirms that there is no attribute request in the ldap search packet present. Any help will be greatly appreciated! Best regards Matvey Teplov +31 62 705 12 73 - List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html
On May 15, 2025, at 8:26 AM, Matvey Teplov <matvey.teplov@nomios.nl> wrote:
I cannot get the attributes to be: • filtered to the bare necessity - I don't need them all present in the reply and there are 39 of them. I just need a memberOf from the AD.
You can filter the reply attributes to keep only the ones you want. See the attr_filter module.
• Having the memberOf attribute recognised - system comes back with: "ERROR: String passed does not look like an LDAP URL"
i.e. it's a malformed LDAP URL. You need to fix it so the it's the correct syntax. You can't just invent syntax and expect it to work.
Any help will be appreciated!
Cheers
Here is the configuration:
Why? You were already told to read the documentation, which says DON'T POST THE CONFIGURATION FILES. It's easier to solve problems if you read the documentation.
(0) ERROR: String passed does not look like an LDAP URL (0) EXPAND %{ldap:memberOf}
That's not a valid LDAP URL, or a valid LDAP query. You're inventing your own things, and then wondering why they don't work. The argument to the function is an LDAP URL. The LDAP function is intended to do an LDAP query, and get a result.
If I run manually ldap_search, then membership shows. Wireshark confirms that there is no attribute request in the ldap search packet present.
Yes, you've said that before. You don't need to say it again. Alan DeKok.
Matvey Teplov via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
Hi Guys,
I cannot get the ldap module to fetch a memberOf attribute from AD - it is not putting it as a filter parameter in the LDAP search packet. I need this list later to search through for port-authentication phase through the groups in session-state:LDAP-Group[*]'s to identify which ones are present and return a proper VSA. The LDAP configuration is as follows:
We use this, because it searches the group membership hierarchy so nested groups work: user { ... filter = "(&(objectClass=user)(sAMAccountName=%{Stripped-User-Name}))" scope = 'sub' } group { ... scope = 'sub' filter = '(objectCategory=group)' membership_filter = "(member:1.2.840.113556.1.4.1941:=%{&control:Ldap-UserDN})" ... } You can thank Microsoft for the ugly OID, as far as I know it has no textual alias. then: if (&ldap_ad-LDAP-Group[*] == "whatever") ...that statement will be true if any one of the users groups, including groups inherited from other groups, is "whatever", no need for a for loop unless you need to do something fancy. Do note that if you have a lot of groups in the OU the search will not be especially efficient, it will even rummage through your printers and whatnot. We made a new OU for the top level RADIUS groups and then could put groups from our other OUs inside those groups to reduce the scope of the search to just users.
Hi Brian, How do you define ldap_ad-LDAP-Group? Best regards Matvey Teplov +31 62 705 12 73 ________________________________ From: Brian Julin <BJulin@clarku.edu> Sent: 15 May 2025 15:02 To: freeradius-users@lists.freeradius.org <freeradius-users@lists.freeradius.org> Cc: Matvey Teplov <matvey.teplov@nomios.nl> Subject: Re: [EXT] Fetching memberOf attribute [You don't often get email from bjulin@clarku.edu. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] Matvey Teplov via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
Hi Guys,
I cannot get the ldap module to fetch a memberOf attribute from AD - it is not putting it as a filter parameter in the LDAP search packet. I need this list later to search through for port-authentication phase through the groups in session-state:LDAP-Group[*]'s to identify which ones are present and return a proper VSA. The LDAP configuration is as follows:
We use this, because it searches the group membership hierarchy so nested groups work: user { ... filter = "(&(objectClass=user)(sAMAccountName=%{Stripped-User-Name}))" scope = 'sub' } group { ... scope = 'sub' filter = '(objectCategory=group)' membership_filter = "(member:1.2.840.113556.1.4.1941:=%{&control:Ldap-UserDN})" ... } You can thank Microsoft for the ugly OID, as far as I know it has no textual alias. then: if (&ldap_ad-LDAP-Group[*] == "whatever") ...that statement will be true if any one of the users groups, including groups inherited from other groups, is "whatever", no need for a for loop unless you need to do something fancy. Do note that if you have a lot of groups in the OU the search will not be especially efficient, it will even rummage through your printers and whatnot. We made a new OU for the top level RADIUS groups and then could put groups from our other OUs inside those groups to reduce the scope of the search to just users.
Matvey Teplov <matvey.teplov@nomios.nl> wrote:
How do you define ldap_ad-LDAP-Group?
Sorry, "ldap_ad" is just the name we gave our ldap module when we instantiated it. Use whatever you named your module instance in place of that. e.g. we did: ldap ldap_ad { ... } IIRC if you don't give you instance a name it's just "ldap"
Hi Brian, Thank you for the pushing to the right direction - I reshaped the ldap group section including filters and it worked like a charm. For the record in mods-available/ldap: ldap { server = 'abc.abc' identity = 'CN=ServiceAccount,DC=Users,DC=abc,DC=abc' password = '123456789' base_dn = 'DC=abc,DC=abc' user { base_dn = "${..base_dn}" filter = "(&(objectClass=user)(sAMAccountName=%{&User-Name}))" scope = 'sub' uid_attribute = "sAMAccountName" attribute { memberOf := "session-state:LDAP-Group" } } group { base_dn = "${..base_dn}" filter = '(objectClass=group)' name_attribute = cn # use magic AD search to get all groups membership_filter = "(member:1.2.840.113556.1.4.1941:=%{control:Ldap-UserDN})" membership_attribute = 'memberOf' # enable cache module cacheable_dn = 'yes' cache_attribute = "${..:instance}-LDAP-Group" } options { chase_referrals = no rebind = yes ldap_debug = 2147483647 timeout = 10 timelimit = 3 net_timeout = 1 idle = 60 } tls { start_tls = no } } The LDAP queries are happening in the authorize section after the proxy call (sites-available/default) and will yield a list of groups in the control:ldap-LDAP-Group attribute: authorize { preprocess auth_log chap # suffix if (&request:User-Name) { update session-state { Persisted-User-Name := "%{request:User-Name}" } } update control { Proxy-To-Realm := "radius_server" } ldap } So, when you will be getting it in the post-auth section, you can go over it with the foreach loop: foreach &control:ldap-LDAP-Group { if ("%{Foreach-Variable-0}" == "CN=Radius_ReadOnly_Group,DC=Groups,DC=abc,DC=abc") { update reply { Reply-Message := "Authorized as RO user" } update control { Auth-Type := Accept } } if ("%{Foreach-Variable-0}" == "CN=Radius_ReadWrite_Group,DC=Groups,DC=abc,DC=abc") { update reply { Reply-Message := "Authorized as RW user" } update control { Auth-Type := Accept } } if ("%{Foreach-Variable-0}" == "CN=Radius_Admin_Group,DC=Groups,DC=abc,DC=abc") { update reply { Reply-Message := "Authorized as Admin" } update control { Auth-Type := Accept } } } The problem I still have is how to send the Reject back if the loop conditions are not met, because before I go into it, I set Reject explicitly: update { reply:Reply-Message := "Unauthorized - No group found" control:Auth-Type := "Reject" } but it is still returning accept: (0) # Executing section post-auth from file /etc/freeradius/3.0/sites-enabled/default (0) post-auth { (0) update { (0) reply:Reply-Message := "Unauthorized - No group found" (0) control:Auth-Type := Reject (0) } # update = noop (0) foreach &control:ldap-LDAP-Group { (0) } # foreach &control:ldap-LDAP-Group = noop (0) } # post-auth = noop (0) Login OK: [generic-ms-user] (from client localhost port 1) (0) Sent Access-Accept Id 116 from 127.0.0.1:1825 to 127.0.0.1:50310 length 0 (0) Reply-Message := "Unauthorized - No group found" (0) Finished request The strange thing is that the message is being set, but the update still says "noop". Does anyone have a clue? Best regards Matvey Teplov +31 62 705 12 73 ________________________________ From: Freeradius-Users <freeradius-users-bounces+matvey.teplov=nomios.nl@lists.freeradius.org> on behalf of Matvey Teplov via Freeradius-Users <freeradius-users@lists.freeradius.org> Sent: 15 May 2025 15:23 To: Brian Julin <BJulin@clarku.edu>; freeradius-users@lists.freeradius.org <freeradius-users@lists.freeradius.org> Cc: Matvey Teplov <matvey.teplov@nomios.nl> Subject: Re: [EXT] Fetching memberOf attribute Hi Brian, How do you define ldap_ad-LDAP-Group? Best regards Matvey Teplov +31 62 705 12 73 ________________________________ From: Brian Julin <BJulin@clarku.edu> Sent: 15 May 2025 15:02 To: freeradius-users@lists.freeradius.org <freeradius-users@lists.freeradius.org> Cc: Matvey Teplov <matvey.teplov@nomios.nl> Subject: Re: [EXT] Fetching memberOf attribute [You don't often get email from bjulin@clarku.edu. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] Matvey Teplov via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
Hi Guys,
I cannot get the ldap module to fetch a memberOf attribute from AD - it is not putting it as a filter parameter in the LDAP search packet. I need this list later to search through for port-authentication phase through the groups in session-state:LDAP-Group[*]'s to identify which ones are present and return a proper VSA. The LDAP configuration is as follows:
We use this, because it searches the group membership hierarchy so nested groups work: user { ... filter = "(&(objectClass=user)(sAMAccountName=%{Stripped-User-Name}))" scope = 'sub' } group { ... scope = 'sub' filter = '(objectCategory=group)' membership_filter = "(member:1.2.840.113556.1.4.1941:=%{&control:Ldap-UserDN})" ... } You can thank Microsoft for the ugly OID, as far as I know it has no textual alias. then: if (&ldap_ad-LDAP-Group[*] == "whatever") ...that statement will be true if any one of the users groups, including groups inherited from other groups, is "whatever", no need for a for loop unless you need to do something fancy. Do note that if you have a lot of groups in the OU the search will not be especially efficient, it will even rummage through your printers and whatnot. We made a new OU for the top level RADIUS groups and then could put groups from our other OUs inside those groups to reduce the scope of the search to just users. - List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html
Matvey Teplov <matvey.teplov@nomios.nl> wrote:
The LDAP queries are happening in the authorize section after the proxy call (sites-available/default) and will yield a list of groups in the control:ldap-LDAP-Group attribute: So, when you will be getting it in the post-auth section, you can go over it with the foreach loop:
foreach &control:ldap-LDAP-Group { if ("%{Foreach-Variable-0}" == "CN=Radius_ReadOnly_Group,DC=Groups,DC=abc,DC=abc") {
You should not have to do a for loop, I do not think. With a splatted array attribute, "==" is magic and just checks if any one element is equal, just splat the attribute with an index of [*] if (&control:ldap-LDAP-Group[*] == "whatever") { }
The problem I still have is how to send the Reject back if the loop conditions are not met, because before I go into it, I set Reject explicitly:
Off the top of my head, try using the "reject" keyword instead of manipulating Auth-Type, after you have determined no group has been found. something like: if (reply:Reply-Message == "Unauthorized - No Group found") { reject } ...or if your list is short, just in the last else in a bunch of nested if/else clauses.
Hi Brian, Picking this outstanding action. I tried "reject" before, and it is a problem. The startup comes with: /etc/freeradius/3.0/sites-enabled/default[85]: Failed to find "reject" as a module or policy. /etc/freeradius/3.0/sites-enabled/default[85]: Please verify that the configuration exists in /etc/freeradius/3.0/mods-enabled/reject. /etc/freeradius/3.0/sites-enabled/default[85]: Failed to parse "reject" entry. Also, the simple '==' doesn't work either, and that's why the loop is there. It is coming back with during authentication: (0) if (&control:ldap-LDAP-Group[*] == "CN=Radius_ReadOnly_Group,DC=Groups,DC=abc,DC=abc") { (0) ERROR: Failed retrieving values required to evaluate condition Configured condition is: if (&control:ldap-LDAP-Group[*] == "CN=Radius_ReadOnly_Group,DC=Groups,DC=abc,DC=abc") { update reply { Fortinet-Group-Name := "ro_group" Juniper-Local-User-Name := "ro_role" Reply-Message := "Authorized as RO user" } update control { Auth-Type := Accept } } Best regards Matvey Teplov ________________________________ From: Brian Julin <BJulin@clarku.edu> Sent: 22 May 2025 19:03 To: Matvey Teplov <matvey.teplov@nomios.nl>; freeradius-users@lists.freeradius.org <freeradius-users@lists.freeradius.org> Subject: Re: [EXT] Fetching memberOf attribute Matvey Teplov <matvey.teplov@nomios.nl> wrote:
The LDAP queries are happening in the authorize section after the proxy call (sites-available/default) and will yield a list of groups in the control:ldap-LDAP-Group attribute: So, when you will be getting it in the post-auth section, you can go over it with the foreach loop:
foreach &control:ldap-LDAP-Group { if ("%{Foreach-Variable-0}" == "CN=Radius_ReadOnly_Group,DC=Groups,DC=abc,DC=abc") {
You should not have to do a for loop, I do not think. With a splatted array attribute, "==" is magic and just checks if any one element is equal, just splat the attribute with an index of [*] if (&control:ldap-LDAP-Group[*] == "whatever") { }
The problem I still have is how to send the Reject back if the loop conditions are not met, because before I go into it, I set Reject explicitly:
Off the top of my head, try using the "reject" keyword instead of manipulating Auth-Type, after you have determined no group has been found. something like: if (reply:Reply-Message == "Unauthorized - No Group found") { reject } ...or if your list is short, just in the last else in a bunch of nested if/else clauses.
On Jun 2, 2025, at 8:34 AM, Matvey Teplov via Freeradius-Users <freeradius-users@lists.freeradius.org> wrote:
Picking this outstanding action. I tried "reject" before, and it is a problem. The startup comes with:
/etc/freeradius/3.0/sites-enabled/default[85]: Failed to find "reject" as a module or policy. /etc/freeradius/3.0/sites-enabled/default[85]: Please verify that the configuration exists in /etc/freeradius/3.0/mods-enabled/reject. /etc/freeradius/3.0/sites-enabled/default[85]: Failed to parse "reject" entry.
Because you edited the default configuration and broke it. Don't do that. The "reject" module is defined in mods-available/always. You've either edited it to remove the "reject" entry, or you've disabled it by removing mods-enabled/always,
Also, the simple '==' doesn't work either, and that's why the loop is there. It is coming back with during authentication: (0) if (&control:ldap-LDAP-Group[*] == "CN=Radius_ReadOnly_Group,DC=Groups,DC=abc,DC=abc") { (0) ERROR: Failed retrieving values required to evaluate condition
The LDAP-Group attribute is documented in the Wiki: https://wiki.freeradius.org/modules/Rlm_ldap Follow those examples and it will work. Alan DeKok.
Matvey Teplov <matvey.teplov@nomios.nl> wrote:
Picking this outstanding action. I tried "reject" before, and it is a problem. The startup comes with: /etc/freeradius/3.0/sites-enabled/default[85]: Failed to find "reject" as a module or policy.
We have these defined in our config. Maybe this is an obsolete way to reject things I do not know at this point: modules { always fail { rcode = fail } always reject { rcode = reject } always handled { rcode = handled } always ok { rcode = ok simulcount = 0 mpp = no } }
Also, the simple '==' doesn't work either, and that's why the loop is there. It is coming back with during authentication: (0) if (&control:ldap-LDAP-Group[*] == "CN=Radius_ReadOnly_Group,DC=Groups,DC=abc,DC=abc") { (0) ERROR: Failed retrieving values required to evaluate condition
That's strange. Either something with the version you are running, or maybe that statement is getting run over more than once, and sometimes the attribute is empty. It can be difficult to run statements at the correct phase with all the encapsulation layers involved in EAP-PEAP. The splat statement definitely works for us, but we do a lot of explicit cache handling. Specifically we've defined this: cache { key = "%{User-Name}" # Just enough time to get us from autz to auth ttl = 15 add_stats = "no" update { control:ldap-LDAP-Group := &control:ldap-LDAP-Group[*] } } ...and we always call "cache" before accessing the attribute. We access it once in our Autz-Type for wifi, to deny password guessing attempts for certain accounts, and then in our Auth-Type for WiFi to do the actual group memberships. ...and then we have the EAP session resumption running another layer of caching on top of that so it's really quite an astounding pile of duct tape and bubblgum. But that's EAP-PEAP for you.
Guys, Thank you very much! Indeed, the mods-available/always was missing in the installation. I had to re-adjust the configuration to: post-auth { if (&control:ldap-LDAP-Group[*] == "CN=Radius_ReadOnly_Group,DC=Groups,DC=abc,DC=abc") { update reply { Fortinet-Group-Name := "ro_group" Juniper-Local-User-Name := "ro_role" Reply-Message := "Authorized as RO user" } update control { Auth-Type := Accept } accept } if (&control:ldap-LDAP-Group[*] == "CN=Radius_ReadWrite_Group,DC=Groups,DC=abc,DC=abc") { update reply { Fortinet-Group-Name := "rw_group" Juniper-Local-User-Name := "rw_role" Reply-Message := "Authorized as RW user" } update control { Auth-Type := Accept } accept } if (&control:ldap-LDAP-Group[*] == "CN=Radius_Admin_Group,DC=Groups,DC=abc,DC=abc") { update reply { Fortinet-Group-Name := "admin_group" Juniper-Local-User-Name := "admin_role" Reply-Message := "Authorized as Admin" } update control { Auth-Type := Accept } accept } update { reply:Reply-Message := "Unauthorized - No group found" control:Auth-Type := Reject } reject } } I just wanted to let you know that everything is now working as expected. Best regards Matvey Teplov +31 62 705 12 73 ________________________________ From: Brian Julin <BJulin@clarku.edu> Sent: 03 June 2025 16:14 To: Matvey Teplov <matvey.teplov@nomios.nl>; freeradius-users@lists.freeradius.org <freeradius-users@lists.freeradius.org> Subject: Re: [EXT] Fetching memberOf attribute Matvey Teplov <matvey.teplov@nomios.nl> wrote:
Picking this outstanding action. I tried "reject" before, and it is a problem. The startup comes with: /etc/freeradius/3.0/sites-enabled/default[85]: Failed to find "reject" as a module or policy.
We have these defined in our config. Maybe this is an obsolete way to reject things I do not know at this point: modules { always fail { rcode = fail } always reject { rcode = reject } always handled { rcode = handled } always ok { rcode = ok simulcount = 0 mpp = no } }
Also, the simple '==' doesn't work either, and that's why the loop is there. It is coming back with during authentication: (0) if (&control:ldap-LDAP-Group[*] == "CN=Radius_ReadOnly_Group,DC=Groups,DC=abc,DC=abc") { (0) ERROR: Failed retrieving values required to evaluate condition
That's strange. Either something with the version you are running, or maybe that statement is getting run over more than once, and sometimes the attribute is empty. It can be difficult to run statements at the correct phase with all the encapsulation layers involved in EAP-PEAP. The splat statement definitely works for us, but we do a lot of explicit cache handling. Specifically we've defined this: cache { key = "%{User-Name}" # Just enough time to get us from autz to auth ttl = 15 add_stats = "no" update { control:ldap-LDAP-Group := &control:ldap-LDAP-Group[*] } } ...and we always call "cache" before accessing the attribute. We access it once in our Autz-Type for wifi, to deny password guessing attempts for certain accounts, and then in our Auth-Type for WiFi to do the actual group memberships. ...and then we have the EAP session resumption running another layer of caching on top of that so it's really quite an astounding pile of duct tape and bubblgum. But that's EAP-PEAP for you.
participants (3)
-
Alan DeKok -
Brian Julin -
Matvey Teplov