Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to authenticate users via Google Secure LDAP #3399

Closed
plsanchezfaure opened this issue Aug 9, 2022 · 3 comments · Fixed by #5850
Closed

Unable to authenticate users via Google Secure LDAP #3399

plsanchezfaure opened this issue Aug 9, 2022 · 3 comments · Fixed by #5850
Labels
bug Something isn't working status/wontfix This will not be worked on

Comments

@plsanchezfaure
Copy link

plsanchezfaure commented Aug 9, 2022

Describe the bug

Once Google Secure LDAP is configured as the source and users and groups have been synced, they cannot login in.

The problem seems to be caused by a search against Google Secure LDAP with the filter "(objectClass=*)", which is not allowed for the bound specific user who is trying to log in.

Google shows the following messages:

Successfully bind LDAP with uid=plsan*****,ou=Users,dc=******,dc=com.
LDAP search with (objectClass=*) failed for the following reason: INSUFFICIENT_ACCESS_RIGHTS.

The "INSUFFICIENT_ACCESS_RIGHTS" can be also be seen in server logs (below).

To Reproduce
Steps to reproduce the behavior:

  1. Configure Google Secure LDAP as source.
  2. Correctly sync users and groups
  3. Attempt to login to authentik with a user from Google Secure LDAP.

Expected behavior

Correct login flow as the user is synced and the password is right.

Logs

{"backend": "authentik.sources.ldap.auth.LDAPBackend", "event": "Attempting authentication...", "host": "test-authentik.******.com", "level": "debug", "logger": "authentik.stages.password.stage", "pid": 20, "request_id": "4f17533c1a65480eb9b43d5e9b52c730", "timestamp": "2022-08-09T09:26:33.516377"}
{"event": "LDAP Auth attempt", "host": "test-authentik.******.com", "level": "debug", "logger": "authentik.sources.ldap.auth", "pid": 20, "request_id": "4f17533c1a65480eb9b43d5e9b52c730", "source": "<LDAPSource: Google Secure LDAP>", "timestamp": "2022-08-09T09:26:33.521620"}
{"event": "Attempting Binding as user", "host": "test-authentik.*******.com", "level": "debug", "logger": "authentik.sources.ldap.auth", "pid": 20, "request_id": "4f17533c1a65480eb9b43d5e9b52c730", "timestamp": "2022-08-09T09:26:33.525593", "user": "<User: plsan****>"}
{"event": "LDAPInsufficientAccessRightsResult - 50 - insufficientAccessRights - None - None - searchResDone - None", "host": "test-authentik.********.com", "level": "warning", "logger": "authentik.sources.ldap.auth", "pid": 20, "request_id": "4f17533c1a65480eb9b43d5e9b52c730", "timestamp": "2022-08-09T09:26:34.956817"}
{"event": "Failed to bind, password invalid", "host": "test-authentik.******.com", "level": "debug", "logger": "authentik.sources.ldap.auth", "pid": 20, "request_id": "4f17533c1a65480eb9b43d5e9b52c730", "timestamp": "2022-08-09T09:26:34.957475"}
{"backend": "authentik.sources.ldap.auth.LDAPBackend", "event": "Backend returned nothing, continuing", "host": "test-authentik.*******.com", "level": "debug", "logger": "authentik.stages.password.stage", "pid": 20, "request_id": "4f17533c1a65480eb9b43d5e9b52c730", "timestamp": "2022-08-09T09:26:34.957888"}

Version and Deployment (please complete the following information):

  • authentik version: 2022.7.3
  • Deployment: docker-compose

Additional context

Tell me if additional info, logs or whatever is needed.

@plsanchezfaure plsanchezfaure added the bug Something isn't working label Aug 9, 2022
@plsanchezfaure
Copy link
Author

I can perform a successful bind when I set the attribute get_info='NONE' into Server ldap3 object:

>>> import ldap3
>>> server = ldap3.Server('<URL>', get_info='NONE')
>>> temp_connection = ldap3.Connection(server, user='uid=plsan***,ou=Users,dc=*****,dc=com', password='*****', raise_exceptions=True, receive_timeout=120)
>>> temp_connection.bind()
True

If the method https://github.com/goauthentik/authentik/blob/main/authentik/sources/ldap/auth.py#L54 only checks the password at login time, I think that get_info='NONE' can be included as the bind is successful.

Additional info

The library ldap3 performs a search without base when get_info='ALL' (default behaviour) after bind operation which causes the problem described above (see the empty baseObject):

DEBUG:ldap3:BASIC:start BIND operation via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=*****,dc=com - not lazy - unbound - closed - <no socket> - tls not started - not listening - SyncStrategy - internal decoder>
DEBUG:ldap3:NETWORK:opening connection for <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - unbound - closed - <no socket> - tls not started - not listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:address for <ldap://localhost:389 - cleartext> resolved as <[<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('127.0.0.1', 389)]>
DEBUG:ldap3:BASIC:address for <ldap://localhost:389 - cleartext> resolved as <[<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('127.0.0.1', 389)]>
DEBUG:ldap3:BASIC:obtained candidate address for <ldap://localhost:389 - cleartext>: <[<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('127.0.0.1', 389)]> with mode IP_V6_PREFERRED
DEBUG:ldap3:BASIC:obtained candidate address for <ldap://localhost:389 - cleartext>: <[<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('127.0.0.1', 389)]> with mode IP_V6_PREFERRED
DEBUG:ldap3:BASIC:try to open candidate address [<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('127.0.0.1', 389)]
DEBUG:ldap3:NETWORK:connection open for <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=*****,dc=com - not lazy - unbound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:PROTOCOL:performing simple BIND for <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - unbound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:PROTOCOL:simple BIND request <{'version': 3, 'name': 'uid=plsan****,ou=Users,dc=******,dc=com', 'authentication': {'simple': '*******', 'sasl': None}}> sent via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=*****,dc=com - not lazy - unbound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:PROTOCOL:new message id <1> generated
DEBUG:ldap3:NETWORK:sending 1 ldap message for <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - unbound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:EXTENDED:ldap message sent via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - unbound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>:
>>LDAPMessage:
>> messageID=1
>> protocolOp=ProtocolOp:
>>  bindRequest=BindRequest:
>>   version=3
>>   name=uid=plsan****,ou=Users,dc=*****,dc=com
>>   authentication=AuthenticationChoice:
>>    simple=*******
DEBUG:ldap3:NETWORK:sent 81 bytes via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=*****,dc=com - not lazy - unbound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:NETWORK:received 14 bytes via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - unbound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:NETWORK:received 1 ldap messages via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=*****,dc=com - not lazy - unbound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:EXTENDED:ldap message received via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - unbound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>:
<<{'controls': None,
<< 'messageID': 1,
<< 'payload': [(0, False, 10, 0), (0, False, 4, b''), (0, False, 4, b'')],
<< 'protocolOp': 1}
DEBUG:ldap3:PROTOCOL:BIND response <{'result': 0, 'description': 'success', 'dn': '', 'message': '', 'referrals': None, 'saslCreds': None, 'type': 'bindResponse'}> received via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=*****,dc=com - not lazy - unbound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:refreshing server info for <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - bound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:start SEARCH operation via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=*****,dc=com - not lazy - bound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:PROTOCOL:SEARCH request <{'base': '', 'scope': 0, 'dereferenceAlias': 3, 'sizeLimit': 0, 'timeLimit': 0, 'typesOnly': False, 'filter': '(objectClass=*)', 'attributes': ['subschemaSubentry', '+']}> sent via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - bound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:PROTOCOL:new message id <2> generated
DEBUG:ldap3:NETWORK:sending 1 ldap message for <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - bound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:EXTENDED:ldap message sent via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - bound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>:
>>LDAPMessage:
>> messageID=2
>> protocolOp=ProtocolOp:
>>  searchRequest=SearchRequest:
>>   baseObject=
>>   scope=baseObject
>>   derefAliases=derefAlways
>>   sizeLimit=0
>>   timeLimit=0
>>   typesOnly=False
>>   filter=Filter:
>>    present=objectClass
>>   attributes=AttributeSelection:
>>    subschemaSubentry    +
DEBUG:ldap3:NETWORK:sent 61 bytes via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - bound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:NETWORK:received 14 bytes via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - bound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:NETWORK:received 1 ldap messages via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=******,dc=com - not lazy - bound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:EXTENDED:ldap message received via <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=*******,dc=com - not lazy - bound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>:
<<{'controls': None,
<< 'messageID': 2,
<< 'payload': [(0, False, 10, 50), (0, False, 4, b''), (0, False, 4, b'')],
<< 'protocolOp': 5}
DEBUG:ldap3:PROTOCOL:operation result <{'result': 50, 'description': 'insufficientAccessRights', 'dn': '', 'message': '', 'referrals': None, 'type': 'searchResDone'}> for <ldap://localhost:389 - cleartext - user: uid=plsan****,ou=Users,dc=*****,dc=com - not lazy - bound - open - <local: 127.0.0.1:52795 - remote: 127.0.0.1:389> - tls not started - listening - SyncStrategy - internal decoder>

Emulating the same query with ldapsearch produces the same result:

# ldapsearch -x -D "uid=plsan****,ou=Users,dc=******,dc=com" -w ****** -H ldap://*** -b "" '(objectClass=*)' 'subschemaSubentry' '+'
# extended LDIF
#
# LDAPv3
# base <> with scope subtree
# filter: (objectClass=*)
# requesting: subschemaSubentry + 
#

# search result
search: 2
result: 50 Insufficient access

# numResponses: 1

When setting the appropiate base parameter, the is no problem:

# ldapsearch -x -D "uid=plsan****,ou=Users,dc=******,dc=com" -w ******* -H ldap://localhost -b "dc=******,dc=com" '(objectClass=*)' 'subschemaSubentry' '+'
# extended LDIF
#
# LDAPv3
# base <dc=******,dc=com> with scope subtree
# filter: (objectClass=*)
# requesting: subschemaSubentry + 
#

[lot of results which are omitted]

# search result
search: 2
result: 0 Success

# numResponses: 1734
# numEntries: 1733

The library ldap3 do not set the base parameter and Google rejects the query. In fact, it is a cloud service which handles a lot of domains and You have to specify which one you belong to.

@plsanchezfaure
Copy link
Author

I have made these small changes to the code:

$ git diff authentik/sources/ldap/models.py
diff --git a/authentik/sources/ldap/models.py b/authentik/sources/ldap/models.py
index a6cb1a283..c1caa6026 100644
--- a/authentik/sources/ldap/models.py
+++ b/authentik/sources/ldap/models.py
@@ -3,7 +3,7 @@ from ssl import CERT_REQUIRED
 
 from django.db import models
 from django.utils.translation import gettext_lazy as _
-from ldap3 import ALL, RANDOM, Connection, Server, ServerPool, Tls
+from ldap3 import NONE, RANDOM, Connection, Server, ServerPool, Tls
 from rest_framework.serializers import Serializer
 
 from authentik.core.models import Group, PropertyMapping, Source
@@ -117,7 +117,7 @@ class LDAPSource(Source):
         if ciphers := CONFIG.y("ldap.tls.ciphers", None):
             tls_kwargs["ciphers"] = ciphers.strip()
         kwargs = {
-            "get_info": ALL,
+            "get_info": NONE,
             "connect_timeout": LDAP_TIMEOUT,
             "tls": Tls(**tls_kwargs),
         }

The images are generated fine and I have tested both the synchronization of users and groups, as well as the login of users from LDAP, and everything works correctly.

@stale
Copy link

stale bot commented Oct 10, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the status/wontfix This will not be worked on label Oct 10, 2022
@stale stale bot closed this as completed Oct 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working status/wontfix This will not be worked on
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant