Hi all, I have a question regarding multiple ldap instances in freeradius and searching users in LDAP-Groups. First of all, what do I try to achieve: Freeradius has to authenticate and authorize our admin users for our customers network devices. It has to perform user authentication with a simple ldap bind authentication and look up the group membership of the user. If the password of the user is correct and the user is member of a specific group, access shall be granted. I have already achieved a working configuration for the tasks above. I recently got a new task to allow also our customers access to their network devices with read-only access. The customer users are already configured in another ldap server. So I would like to configure two named ldap instances in rlm_ldap. If I do so, the authentication part for the admin user still works, but freeradius will not search the LDAP for user membership anymore and reject access. Please find below the config and debug output of the working configuration with only one ldap server And the modified config with two named ldap instances. Any help appreciated. If further debug output is needed, please let me know. Best regards, Florian #################### ## ## Sinlge LDAP Server configuration working ## LDAP search is performed and user is found in the configured group ## #################### Single LDAP Server configured: :/etc/freeradius/3.0# grep -v '#' mods-enabled/ldap | grep -v -e '^$' ldap { server = 'IP' identity = 'CN=freerad,OU=Service-Accounts,OU=User,OU=mycompany,DC=mycompany,DC=de' password = removed base_dn = 'OU=mycompany,DC=mycompany,DC=de' sasl { } update { control:Password-With-Header += 'userPassword' control: += 'radiusControlAttribute' request: += 'radiusRequestAttribute' reply: += 'radiusReplyAttribute' } user { base_dn = "${..base_dn}" filter = "(sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}})" sasl { } } group { base_dn = "${..base_dn}" filter = '(objectClass=posixGroup)' scope = 'sub' name_attribute = cn membership_filter = "(|(&(objectClass=group)(member=%{control:Ldap-UserDn})))" membership_attribute = 'memberOf' } profile { } client { base_dn = "${..base_dn}" filter = '(objectClass=radiusClient)' template { } attribute { ipaddr = 'radiusClientIdentifier' secret = 'radiusClientSecret' } } accounting { reference = "%{tolower:type.%{Acct-Status-Type}}" type { start { update { description := "Online at %S" } } interim-update { update { description := "Last seen at %S" } } stop { update { description := "Offline at %S" } } } } post-auth { update { description := "Authenticated at %S" } } options { chase_referrals = yes rebind = yes res_timeout = 10 srv_timelimit = 3 net_timeout = 1 idle = 60 probes = 3 interval = 3 ldap_debug = 0x0028 } tls { } pool { start = ${thread[pool].start_servers} min = ${thread[pool].min_spare_servers} max = ${thread[pool].max_servers} spare = ${thread[pool].max_spare_servers} uses = 0 retry_delay = 30 lifetime = 0 idle_timeout = 60 } } :/etc/freeradius/3.0# grep -v '#' sites-enabled/default | grep -v -e '^$' server default { listen { type = auth ipaddr = * port = 0 limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } listen { ipaddr = * port = 0 type = acct limit { } } listen { type = auth port = 0 limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } listen { ipv6addr = :: port = 0 type = acct limit { } } authorize { preprocess eap { ok = return } ldap if ((ok || updated) && User-Password) { update { control:Auth-Type := ldap } } } authenticate { digest Auth-Type LDAP { ldap if (LDAP-Group == "mycompany_Technik_Sec"){ if (Huntgroup-Name == "fortigate") { update reply { Fortinet-Group-Name = "mycompany_Operations" Fortinet-Access-Profile = "super_admin" } } } else { reject } } eap } preacct { preprocess acct_unique suffix files } accounting { detail unix -sql exec attr_filter.accounting_response } session { } post-auth { update { &reply: += &session-state: } -sql exec remove_reply_message_if_eap Post-Auth-Type REJECT { -sql attr_filter.access_reject eap remove_reply_message_if_eap } Post-Auth-Type Challenge { } } pre-proxy { } post-proxy { eap } } Debug output of auth request tested via radtest -t pap user.name pw-removed localhost 0 testing123 (0) Received Access-Request Id 178 from 127.0.0.1:52807 to 127.0.0.1:1812 length 81 (0) User-Name = "user.name" (0) User-Password = "pw-removed" (0) NAS-IP-Address = 127.0.1.1 (0) NAS-Port = 0 (0) Message-Authenticator = 0x87ca46d1ef7da1eb772aef641e0d9e02 (0) # Executing section authorize from file /etc/freeradius/3.0/sites-enabled/default (0) authorize { (0) [preprocess] = ok (0) eap: No EAP-Message, not doing EAP (0) [eap] = noop rlm_ldap (ldap): Reserved connection (0) (0) ldap: EXPAND (sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}}) (0) ldap: --> (sAMAccountName=user.name) (0) ldap: Performing search in "OU=mycompany,DC=mycompany,DC=mycompany,DC=de" with filter "(sAMAccountName=user.name)", scope "sub" (0) ldap: Waiting for search result... (0) ldap: User object found at DN "CN=User Name,OU=Windows 10 User,OU=User,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" (0) ldap: Processing user attributes (0) ldap: WARNING: No "known good" password added. Ensure the admin user has permission to read the password attribute (0) ldap: WARNING: PAP authentication will *NOT* work with Active Directory (if that is what you were trying to configure) rlm_ldap (ldap): Released connection (0) Need 5 more connections to reach 10 spares rlm_ldap (ldap): Opening additional connection (5), 1 of 27 pending slots used rlm_ldap (ldap): Connecting to ldap://IP:389 rlm_ldap (ldap): Waiting for bind result... rlm_ldap (ldap): Bind successful (0) [ldap] = ok (0) if ((ok || updated) && User-Password) { (0) if ((ok || updated) && User-Password) -> TRUE (0) if ((ok || updated) && User-Password) { (0) update { (0) control:Auth-Type := LDAP (0) } # update = noop (0) } # if ((ok || updated) && User-Password) = noop (0) } # authorize = ok (0) Found Auth-Type = LDAP (0) # Executing group from file /etc/freeradius/3.0/sites-enabled/default (0) Auth-Type LDAP { rlm_ldap (ldap): Reserved connection (1) (0) ldap: Login attempt by "user.name" (0) ldap: Using user DN from request "CN=User Name,OU=Windows 10 User,OU=User,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" (0) ldap: Waiting for bind result... (0) ldap: Bind successful (0) ldap: Bind as user "CN=User Name,OU=Windows 10 User,OU=User,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" was successful rlm_ldap (ldap): Released connection (1) (0) [ldap] = ok (0) if (LDAP-Group == "mycompany_Technik_Sec"){ (0) Searching for user in group "mycompany_Technik_Sec" rlm_ldap (ldap): Reserved connection (2) (0) Using user DN from request "CN=User Name,OU=Windows 10 User,OU=User,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" (0) Checking for user in group objects (0) EXPAND (&(cn=mycompany_Technik_Sec)(objectClass=posixGroup)(|(&(objectClass=group)(member=%{control:Ldap-UserDn})))) (0) --> (&(cn=mycompany_Technik_Sec)(objectClass=posixGroup)(|(&(objectClass=group)(member=CN\3dUser Name\2cOU\3dWindows 10 User\2cOU\3dUser\2cOU\3dmycompany\2cDC\3dmycompany\2cDC\3dmycompany\2cDC\3dde)))) (0) Performing search in "OU=mycompany,DC=mycompany,DC=mycompany,DC=de" with filter "(&(cn=mycompany_Technik_Sec)(objectClass=posixGroup)(|(&(objectClass=group)(member=CN\3dUser Name\2cOU\3dWindows 10 User\2cOU\3dUser\2cOU\3dmycompany\2cDC\3dmycompany\2cDC\3dmycompany\2cDC\3dde))))", scope "sub" (0) Waiting for search result... (0) Search returned no results (0) Checking user object's memberOf attributes (0) Performing unfiltered search in "CN=User Name,OU=Windows 10 User,OU=User,OU=mycompany,DC=mycompany,DC=mycompany,DC=de", scope "base" (0) Waiting for search result... ber_get_next failed. ber_get_next failed. (0) Processing memberOf value "CN=mycompany_pdns_operator,OU=PDNS,OU=Sicherheitsgruppen,OU=Gruppen,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" as a DN (0) Resolving group DN "CN=mycompany_pdns_operator,OU=PDNS,OU=Sicherheitsgruppen,OU=Gruppen,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" to group name ... (0) Processing memberOf value "CN=mycompany_Technik_Sec,OU=Sicherheitsgruppen,OU=Gruppen,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" as a DN (0) Resolving group DN "CN=mycompany_Technik_Sec,OU=Sicherheitsgruppen,OU=Gruppen,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" to group name (0) Performing unfiltered search in "CN=mycompany_Technik_Sec,OU=Sicherheitsgruppen,OU=Gruppen,OU=mycompany,DC=mycompany,DC=mycompany,DC=de", scope "base" (0) Waiting for search result... (0) Group DN "CN=mycompany_Technik_Sec,OU=Sicherheitsgruppen,OU=Gruppen,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" resolves to name "mycompany_Technik_Sec" (0) User found in group "mycompany_Technik_Sec". Comparison between membership: name (resolved from DN "CN=mycompany_Technik_Sec,OU=Sicherheitsgruppen,OU=Gruppen,OU=mycompany,DC=mycompany,DC=mycompany,DC=de"), check: name rlm_ldap (ldap): Released connection (2) (0) if (LDAP-Group == "mycompany_Technik_Sec") -> TRUE (0) if (LDAP-Group == "mycompany_Technik_Sec") { (0) if (Huntgroup-Name == "fortigate") { (0) ERROR: Failed retrieving values required to evaluate condition (0) } # if (LDAP-Group == "mycompany_Technik_Sec") = ok (0) ... skipping else: Preceding "if" was taken (0) } # Auth-Type LDAP = ok (0) # Executing section post-auth from file /etc/freeradius/3.0/sites-enabled/default (0) post-auth { (0) update { (0) No attributes updated (0) } # update = noop (0) [exec] = noop (0) policy remove_reply_message_if_eap { (0) if (&reply:EAP-Message && &reply:Reply-Message) { (0) if (&reply:EAP-Message && &reply:Reply-Message) -> FALSE (0) else { (0) [noop] = noop (0) } # else = noop (0) } # policy remove_reply_message_if_eap = noop (0) } # post-auth = noop (0) Sent Access-Accept Id 178 from 127.0.0.1:1812 to 127.0.0.1:52807 length 0 (0) Finished request Waking up in 4.9 seconds. (0) Cleaning up request packet ID 178 with timestamp +15 Ready to process requests #################### ## ## Multiple LDAP Server configuration not working ## Looks like freeradius is not searching the ldap ## #################### :/etc/freeradius/3.0# grep -v '#' mods-enabled/ldap | grep -v -e '^$' ldap mycompany { server = 'IP' identity = 'CN=freerad,OU=Service-Accounts,OU=User,OU=mycompany,DC=mycompany,DC=de' password = removed base_dn = 'OU=mycompany,DC=mycompany,DC=de' sasl { } update { control:Password-With-Header += 'userPassword' control: += 'radiusControlAttribute' request: += 'radiusRequestAttribute' reply: += 'radiusReplyAttribute' } user { base_dn = "${..base_dn}" filter = "(sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}})" sasl { } } group { base_dn = "${..base_dn}" filter = '(objectClass=posixGroup)' scope = 'sub' name_attribute = cn membership_filter = "(|(&(objectClass=group)(member=%{control:Ldap-UserDn})))" membership_attribute = 'memberOf' } profile { } client { base_dn = "${..base_dn}" filter = '(objectClass=radiusClient)' template { } attribute { ipaddr = 'radiusClientIdentifier' secret = 'radiusClientSecret' } } accounting { reference = "%{tolower:type.%{Acct-Status-Type}}" type { start { update { description := "Online at %S" } } interim-update { update { description := "Last seen at %S" } } stop { update { description := "Offline at %S" } } } } post-auth { update { description := "Authenticated at %S" } } options { chase_referrals = yes rebind = yes res_timeout = 10 srv_timelimit = 3 net_timeout = 1 idle = 60 probes = 3 interval = 3 ldap_debug = 0x0028 } tls { } pool { start = ${thread[pool].start_servers} min = ${thread[pool].min_spare_servers} max = ${thread[pool].max_servers} spare = ${thread[pool].max_spare_servers} uses = 0 retry_delay = 30 lifetime = 0 idle_timeout = 60 } } ldap customers { server = 'IP' identity = 'CN=freerad,OU=Service-Accounts,OU=User,OU=mycompany,DC=mycompany,DC=de' password = removed base_dn = 'OU=mycompany,DC=mycompany,DC=mycompany,DC=de' sasl { } update { control:Password-With-Header += 'userPassword' control: += 'radiusControlAttribute' request: += 'radiusRequestAttribute' reply: += 'radiusReplyAttribute' } user { base_dn = "${..base_dn}" filter = "(sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}})" sasl { } } group { base_dn = "${..base_dn}" filter = '(objectClass=posixGroup)' scope = 'sub' name_attribute = cn membership_filter = "(|(&(objectClass=group)(member=%{control:Ldap-UserDn})))" membership_attribute = 'memberOf' } profile { } client { base_dn = "${..base_dn}" filter = '(objectClass=radiusClient)' template { } attribute { ipaddr = 'radiusClientIdentifier' secret = 'radiusClientSecret' } } accounting { reference = "%{tolower:type.%{Acct-Status-Type}}" type { start { update { description := "Online at %S" } } interim-update { update { description := "Last seen at %S" } } stop { update { description := "Offline at %S" } } } } post-auth { update { description := "Authenticated at %S" } } options { chase_referrals = yes rebind = yes res_timeout = 10 srv_timelimit = 3 net_timeout = 1 idle = 60 probes = 3 interval = 3 ldap_debug = 0x0028 } tls { } pool { start = ${thread[pool].start_servers} min = ${thread[pool].min_spare_servers} max = ${thread[pool].max_servers} spare = ${thread[pool].max_spare_servers} uses = 0 retry_delay = 30 lifetime = 0 idle_timeout = 60 } } :/etc/freeradius/3.0# grep -v '#' sites-enabled/default | grep -v -e '^$' server default { listen { type = auth ipaddr = * port = 0 limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } listen { ipaddr = * port = 0 type = acct limit { } } listen { type = auth port = 0 limit { max_connections = 16 lifetime = 0 idle_timeout = 30 } } listen { ipv6addr = :: port = 0 type = acct limit { } } authorize { filter_username preprocess eap { ok = return } mycompany if ((ok || updated) && User-Password) { update { control:Auth-Type := ldap } } customers if ((ok || updated) && User-Password) { update { control:Auth-Type := ldap } } } authenticate { digest Auth-Type LDAP { mycompany if (mycompany-LDAP-Group == "mycompany_Technik_Sec"){ if (Huntgroup-Name == "fortigate") { update reply { Fortinet-Group-Name = "mycompany_Operations" Fortinet-Access-Profile = "super_admin" } } } else { reject } customers } eap } preacct { preprocess acct_unique suffix files } accounting { detail unix -sql exec attr_filter.accounting_response } session { } post-auth { update { &reply: += &session-state: } -sql exec remove_reply_message_if_eap Post-Auth-Type REJECT { -sql attr_filter.access_reject eap remove_reply_message_if_eap } Post-Auth-Type Challenge { } } pre-proxy { } post-proxy { eap } } Debug output of auth request tested via radtest -t pap user.name pw-removed localhost 0 testing123 (0) Received Access-Request Id 85 from 127.0.0.1:42556 to 127.0.0.1:1812 length 81 (0) User-Name = "user.name" (0) User-Password = "pw-removed" (0) NAS-IP-Address = 127.0.1.1 (0) NAS-Port = 0 (0) Message-Authenticator = 0xd04d42343938af1eb2a54bbed931ccd6 (0) # Executing section authorize from file /etc/freeradius/3.0/sites-enabled/default (0) authorize { (0) policy filter_username { (0) if (&User-Name) { (0) if (&User-Name) -> TRUE (0) if (&User-Name) { (0) if (&User-Name =~ / /) { (0) if (&User-Name =~ / /) -> FALSE (0) if (&User-Name =~ /@[^@]*@/ ) { (0) if (&User-Name =~ /@[^@]*@/ ) -> FALSE (0) if (&User-Name =~ /\.\./ ) { (0) if (&User-Name =~ /\.\./ ) -> FALSE (0) if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/)) { (0) if ((&User-Name =~ /@/) && (&User-Name !~ /@(.+)\.(.+)$/)) -> FALSE (0) if (&User-Name =~ /\.$/) { (0) if (&User-Name =~ /\.$/) -> FALSE (0) if (&User-Name =~ /@\./) { (0) if (&User-Name =~ /@\./) -> FALSE (0) } # if (&User-Name) = notfound (0) } # policy filter_username = notfound (0) [preprocess] = ok (0) eap: No EAP-Message, not doing EAP (0) [eap] = noop rlm_ldap (mycompany): Reserved connection (0) (0) mycompany: EXPAND (sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}}) (0) mycompany: --> (sAMAccountName=user.name) (0) mycompany: Performing search in "OU=mycompany,DC=mycompany,DC=mycompany,DC=de" with filter "(sAMAccountName=user.name)", scope "sub" (0) mycompany: Waiting for search result... (0) mycompany: User object found at DN "CN=User Name,OU=Windows 10 User,OU=User,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" (0) mycompany: Processing user attributes (0) mycompany: WARNING: No "known good" password added. Ensure the admin user has permission to read the password attribute (0) mycompany: WARNING: PAP authentication will *NOT* work with Active Directory (if that is what you were trying to configure) rlm_ldap (mycompany): Released connection (0) Need 5 more connections to reach 10 spares rlm_ldap (mycompany): Opening additional connection (5), 1 of 27 pending slots used rlm_ldap (mycompany): Connecting to ldap://IP:389 rlm_ldap (mycompany): Waiting for bind result... rlm_ldap (mycompany): Bind successful (0) [mycompany] = ok (0) if ((ok || updated) && User-Password) { (0) if ((ok || updated) && User-Password) -> TRUE (0) if ((ok || updated) && User-Password) { (0) update { (0) control:Auth-Type := LDAP (0) } # update = noop (0) } # if ((ok || updated) && User-Password) = noop rlm_ldap (customers): Reserved connection (0) (0) customers: EXPAND (sAMAccountName=%{%{Stripped-User-Name}:-%{User-Name}}) (0) customers: --> (sAMAccountName=user.name) (0) customers: Performing search in "OU=mycompany,DC=mycompany,DC=mycompany,DC=de" with filter "(sAMAccountName=user.name)", scope "sub" (0) customers: Waiting for search result... (0) customers: User object found at DN "CN=User Name,OU=Windows 10 User,OU=User,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" (0) customers: Processing user attributes (0) customers: WARNING: No "known good" password added. Ensure the admin user has permission to read the password attribute (0) customers: WARNING: PAP authentication will *NOT* work with Active Directory (if that is what you were trying to configure) rlm_ldap (customers): Released connection (0) Need 5 more connections to reach 10 spares rlm_ldap (customers): Opening additional connection (5), 1 of 27 pending slots used rlm_ldap (customers): Connecting to ldap://IP:389 rlm_ldap (customers): Waiting for bind result... rlm_ldap (customers): Bind successful (0) [customers] = ok (0) if ((ok || updated) && User-Password) { (0) if ((ok || updated) && User-Password) -> TRUE (0) if ((ok || updated) && User-Password) { (0) update { (0) control:Auth-Type := LDAP (0) } # update = noop (0) } # if ((ok || updated) && User-Password) = noop (0) } # authorize = ok (0) Found Auth-Type = LDAP (0) # Executing group from file /etc/freeradius/3.0/sites-enabled/default (0) Auth-Type LDAP { rlm_ldap (mycompany): Reserved connection (1) (0) mycompany: Login attempt by "user.name" (0) mycompany: Using user DN from request "CN=User Name,OU=Windows 10 User,OU=User,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" (0) mycompany: Waiting for bind result... (0) mycompany: Bind successful (0) mycompany: Bind as user "CN=User Name,OU=Windows 10 User,OU=User,OU=mycompany,DC=mycompany,DC=mycompany,DC=de" was successful rlm_ldap (mycompany): Released connection (1) (0) [mycompany] = ok (0) if (mycompany-LDAP-Group == "mycompany_Technik_Sec"){ (0) if (mycompany-LDAP-Group == "mycompany_Technik_Sec") -> FALSE (0) else { (0) [reject] = reject (0) } # else = reject (0) } # Auth-Type LDAP = reject (0) Failed to authenticate the user (0) Using Post-Auth-Type Reject (0) # Executing group from file /etc/freeradius/3.0/sites-enabled/default (0) Post-Auth-Type REJECT { (0) attr_filter.access_reject: EXPAND %{User-Name} (0) attr_filter.access_reject: --> user.name (0) attr_filter.access_reject: Matched entry DEFAULT at line 11 (0) [attr_filter.access_reject] = updated (0) [eap] = noop (0) policy remove_reply_message_if_eap { (0) if (&reply:EAP-Message && &reply:Reply-Message) { (0) if (&reply:EAP-Message && &reply:Reply-Message) -> FALSE (0) else { (0) [noop] = noop (0) } # else = noop (0) } # policy remove_reply_message_if_eap = noop (0) } # Post-Auth-Type REJECT = updated (0) Delaying response for 1.000000 seconds Waking up in 0.3 seconds. Waking up in 0.6 seconds. (0) Sending delayed response (0) Sent Access-Reject Id 85 from 127.0.0.1:1812 to 127.0.0.1:42556 length 20 Waking up in 3.9 seconds.