django-auth-ldap / django-auth-ldap

Django authentication backend that authenticates against an LDAP service.
https://django-auth-ldap.readthedocs.io/
BSD 2-Clause "Simplified" License
339 stars 96 forks source link

Trying to use LDAPS instead of LDAP #198

Open ganeshramcg opened 4 years ago

ganeshramcg commented 4 years ago

i am trying to replace LDAP with LDAPS but i am not sure which part i am missing. Below is my code for LDAPS. The code is not working with LDAPS but it is working fine with LDAP.

import ldap from django_auth_ldap.config import LDAPSearch, NestedActiveDirectoryGroupType AUTH_LDAPS_SERVER_URI = "ldaps://example1.com:636" AUTH_LDAP_BIND_DN = "CN=SVC_fwauto_prod,OU=Service Accounts,OU=ADCCreated,DC=example1,DC=com"

AUTH_LDAP_USER_SEARCH = LDAPSearch("DC=example1,DC=com",
ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)")

Cache settings

AUTH_LDAP_CACHE_GROUPS = True
AUTH_LDAP_GROUP_CACHE_TIMEOUT = 300

AUTH_LDAP_USER_ATTR_MAP = {
"first_name": "givenName",
"last_name": "sn",
"email": "mail"
}

AUTHENTICATION_BACKENDS = (
'django_auth_ldap.backend.LDAPBackend',
'django.contrib.auth.backends.ModelBackend')

th3cr0w commented 4 years ago

possibly TLS verification issue like it was in my case try like this (less secure):

AUTH_LDAP_CONNECTION_OPTIONS = { ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_ALLOW }

Sispheor commented 4 years ago

Same here. I tried with ldaps or ldap with TLS enabled. I get an error: SERVER_DOWN({'result': -1, 'desc': "Can't contact LDAP server", 'ctrls': [], 'info': '(unknown error code)'})

Seems to be related to the certificate verification. If I test manual from my CLI with ldapsearch it's ok so I'm sure i can reach the remote server.

Here is my conf

# LDAP auth backend
AUTH_LDAP_SERVER_URI = "ldaps://ed.domain.net:636"
AUTH_LDAP_BIND_DN = "dn=ocpkgweb,ou=Applications,o=domain.com"
AUTH_LDAP_BIND_PASSWORD = "password"
# AUTH_LDAP_USER_SEARCH = LDAPSearch(
#     "ou=people,o=hp.com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"
# )
AUTH_LDAP_USER_DN_TEMPLATE = "uid=%(user)s,ou=people,o=domain.com"
AUTH_LDAP_START_TLS: False
AUTH_LDAP_GLOBAL_OPTIONS: {
    ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_NEVER
}
Sispheor commented 4 years ago

Fixed by providing the LDAP CA

LDAP_CA_FILE_PATH = str(BASE_DIR) + os.sep + "ldap_ca.cert"
AUTH_LDAP_CONNECTION_OPTIONS: {
    ldap.OPT_X_TLS_CACERTFILE: LDAP_CA_FILE_PATH,
    ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_ALLOW,
    ldap.OPT_X_TLS_NEWCTX: 0
}

And added an env variable LDAPTLS_CACERT=/path/to/ldap_ca.cert

iaminarush commented 3 years ago

I'm having a similar problem, I can get start_tls_s() from python ldap to work but I can't get AUTH_LDAP_START_TLS from this package to work. Log shows this error Caught LDAPError while authenticating cpc417: CONNECT_ERROR({'desc': 'Connect error', 'info': 'error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed (unable to get local issuer certificate)'})

Code for python ldap

import ldap

CACERTFILE = "path\cert.pem"

l = ldap.initialize('ldap://domain')
l.protocol_version = ldap.VERSION3
l.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW)
l.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERTFILE)
l.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
l.start_tls_s()

Settings for django-auth-ldap

AUTH_LDAP_CONNECTION_OPTIONS: {
    ldap.OPT_PROTOCOL_VERSION: 3,
    ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_ALLOW,
    ldap.OPT_X_TLS_CACERTFILE: CACERTFILE,
    ldap.OPT_X_TLS_NEWCTX: 0,
}

AUTH_USER_MODEL = 'users.User'
AUTH_LDAP_SERVER_URI = 'ldap://domain'
AUTH_LDAP_START_TLS = True
AUTH_LDAP_ALWAYS_UDPATE_USER = True
AUTH_LDAP_BIND_DN = os.environ['CORP_USER']
AUTH_LDAP_BIND_PASSWORD = os.environ['CORP_PASS']

Anybody got any ideas what I am doing wrong, thanks.

francoisfreitag commented 3 years ago

Based on the error, the certificate verification failed.

You may find the following stackoverflow useful to troubleshoot. https://stackoverflow.com/questions/22027418/openssl-python-requests-error-certificate-verify-failed

iaminarush commented 3 years ago

Thanks for the reply, I've managed to solve the problem but the solution is probably not ideal. I've added the settings above directly to LDAPSettings in this package's backend.py. Not sure why that solved the problem.


#backend.py
class LDAPSettings:

    defaults = {
        "ALWAYS_UPDATE_USER": True,
        "AUTHORIZE_ALL_USERS": False,
        "BIND_AS_AUTHENTICATING_USER": False,
        "BIND_DN": "",
        "BIND_PASSWORD": "",
        #Custom
        "CONNECTION_OPTIONS": {
            ldap.OPT_PROTOCOL_VERSION: 3,
            ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_DEMAND,
            ldap.OPT_X_TLS_CACERTFILE: CACERTFILE,
            ldap.OPT_X_TLS_NEWCTX: 0,
        },
        "DENY_GROUP": None,
        "FIND_GROUP_PERMS": False,
        "CACHE_TIMEOUT": 0,
        "GROUP_SEARCH": None,
        "GROUP_TYPE": None,
        "MIRROR_GROUPS": None,
        "MIRROR_GROUPS_EXCEPT": None,
        "PERMIT_EMPTY_PASSWORD": False,
        "REQUIRE_GROUP": None,
        "NO_NEW_USERS": False,
        "SERVER_URI": "ldap://localhost",
        "START_TLS": False,
        "USER_QUERY_FIELD": None,
        "USER_ATTRLIST": None,
        "USER_ATTR_MAP": {},
        "USER_DN_TEMPLATE": None,
        "USER_FLAGS_BY_GROUP": {},
        "USER_SEARCH": None,
    }
WindoC commented 3 years ago

although it is not suggested. But can bypass the ldaps cert check by below code.

ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)

Sispheor commented 3 years ago

Guys,

I just pass here to fix my snippet. It contains a typo. : instead of = in the dict declaration. Yet another example of a single char that cost 6 hours of debug XD

Corrected version

LDAP_CA_FILE_PATH = str(BASE_DIR) + os.sep + "ldap_ca.cert"
AUTH_LDAP_CONNECTION_OPTIONS = {     
    ldap.OPT_X_TLS_CACERTFILE: LDAP_CA_FILE_PATH,
    ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_ALLOW,
    ldap.OPT_X_TLS_NEWCTX: 0
}
johnthagen commented 2 years ago

LDAPS works fine for me if I ignore TLS cert errors:

AUTH_LDAP_GLOBAL_OPTIONS = {ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_NEVER}

But if I try @Sispheor's method to use a CA file and actually authenticate the server properly:

AUTH_LDAP_CONNECTION_OPTIONS = {
    ldap.OPT_X_TLS_CACERTFILE: LDAP_CA_FILE_PATH,
    ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_ALLOW,
    ldap.OPT_X_TLS_NEWCTX: 0,
}

I get an option error when a client tries to log in:

Traceback ``` Traceback (most recent call last): File "venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner response = get_response(request) File "venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 197, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "venv/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view return view_func(*args, **kwargs) File "venv/lib/python3.9/site-packages/django/views/generic/base.py", line 84, in view return self.dispatch(request, *args, **kwargs) File "venv/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch response = self.handle_exception(exc) File "venv/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception self.raise_uncaught_exception(exc) File "venv/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception raise exc File "venv/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch response = handler(request, *args, **kwargs) File "venv/lib/python3.9/site-packages/drf_spectacular/drainage.py", line 156, in wrapped_method return method(self, request, *args, **kwargs) File "venv/lib/python3.9/site-packages/rest_framework_simplejwt/views.py", line 43, in post serializer.is_valid(raise_exception=True) File "venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 227, in is_valid self._validated_data = self.run_validation(self.initial_data) File "venv/lib/python3.9/site-packages/rest_framework/serializers.py", line 429, in run_validation value = self.validate(value) File "venv/lib/python3.9/site-packages/rest_framework_simplejwt/serializers.py", line 68, in validate data = super().validate(attrs) File "venv/lib/python3.9/site-packages/rest_framework_simplejwt/serializers.py", line 49, in validate self.user = authenticate(**authenticate_kwargs) File "venv/lib/python3.9/site-packages/django/views/decorators/debug.py", line 42, in sensitive_variables_wrapper return func(*func_args, **func_kwargs) File "venv/lib/python3.9/site-packages/django/contrib/auth/__init__.py", line 77, in authenticate user = backend.authenticate(request, **credentials) File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 147, in authenticate user = self.authenticate_ldap_user(ldap_user, password) File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 205, in authenticate_ldap_user return ldap_user.authenticate(password) File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 347, in authenticate self._authenticate_user_dn(password) File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 478, in _authenticate_user_dn if self.dn is None: File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 443, in dn self._load_user_dn() File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 514, in _load_user_dn self._user_dn = cache.get_or_set( File "venv/lib/python3.9/site-packages/django/core/cache/backends/base.py", line 228, in get_or_set default = default() File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 539, in _search_for_user_dn results = search.execute(self.connection, {"user": self._username}) File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 465, in connection self._bind() File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 822, in _bind self._bind_as(self.settings.BIND_DN, self.settings.BIND_PASSWORD, sticky=True) File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 833, in _bind_as self._get_connection().simple_bind_s(bind_dn, bind_password) File "venv/lib/python3.9/site-packages/django_auth_ldap/backend.py", line 849, in _get_connection self._connection.set_option(opt, value) File "venv/lib/python3.9/site-packages/ldap/ldapobject.py", line 698, in set_option return self._ldap_call(self._l.set_option,option,invalue) File "venv/lib/python3.9/site-packages/ldap/ldapobject.py", line 128, in _ldap_call result = func(*args,**kwargs) ValueError: option error ```

Has anyone seen this before?

johnthagen commented 2 years ago

Okay, when I test using the same code on Ubuntu 20.04, I don't get the option error as above. This seems to be a macOS specific issue with python-ldap. I can confirm that @Sispheor's solution works on Ubuntu 20.04.

sneha2310 commented 2 years ago

Hey there, I am unable to access through LDAPS. Can anyone guide me what I am doing wrong?

import ldap conn = ldap.initialize("ldaps://172.22.63.55:636") conn.set_option(ldap.OPT_X_TLS_CACERTFILE,'/home/snehavishwakarma/Downloads/cacert-2017-01-18.pem') conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_ALLOW) conn.set_option(ldap.OPT_X_TLS_NEWCTX, 0) conn.start_tls_s() Traceback (most recent call last): File "", line 1, in File "/home/snehavishwakarma/Documents/Backup@server-21March2022/Ldap/Django-registration-and-login-system/venv/lib/python3.8/site-packages/ldap/ldapobject.py", line 643, in start_tls_s return self._ldap_call(self._l.start_tls_s) File "/home/snehavishwakarma/Documents/Backup@server-21March2022/Ldap/Django-registration-and-login-system/venv/lib/python3.8/site-packages/ldap/ldapobject.py", line 128, in _ldap_call result = func(*args,*kwargs) ldap.SERVER_DOWN: {'result': -1, 'desc': "Can't contact LDAP server", 'ctrls': [], 'info': 'The TLS connection was non-properly terminated.'} conn = ldap.initialize("ldap://172.22.63.55:389") conn.start_tls_s() Traceback (most recent call last): File "", line 1, in File "/home/snehavishwakarma/Documents/Backup@server-21March2022/Ldap/Django-registration-and-login-system/venv/lib/python3.8/site-packages/ldap/ldapobject.py", line 643, in start_tls_s return self._ldap_call(self._l.start_tls_s) File "/home/snehavishwakarma/Documents/Backup@server-21March2022/Ldap/Django-registration-and-login-system/venv/lib/python3.8/site-packages/ldap/ldapobject.py", line 128, in _ldap_call result = func(args,**kwargs) ldap.CONNECT_ERROR: {'result': -11, 'desc': 'Connect error', 'ctrls': [], 'info': 'The TLS connection was non-properly terminated.'}

fumiyas commented 2 months ago

See also: python-ldap/python-ldap#55