# HG changeset patch # User Mads Kiilerich # Date 2016-11-15 22:53:41 # Node ID 7ce3897bacd0334452a8b02551220add42506ae4 # Parent 16b685da1117b6ec5f71846685d2cd46cd1d1133 auth: make ldap OPT_X_TLS_CACERTDIR configurable A location was hardcoded. The location was wrong for many systems and prevented actual TLS from working. Also, it should not be necessary with modern Pythons. For some reason, instead of removing it, we now decide to expose it to the user. Choice FTW! diff --git a/docs/setup.rst b/docs/setup.rst --- a/docs/setup.rst +++ b/docs/setup.rst @@ -235,10 +235,8 @@ Connection Security : required Certificate Checks : optional How SSL certificates verification is handled -- this is only useful when `Enable LDAPS`_ is enabled. Only DEMAND or HARD offer full SSL security - while the other options are susceptible to man-in-the-middle attacks. SSL - certificates can be installed to /etc/openldap/cacerts so that the - DEMAND or HARD options can be used with self-signed certificates or - certificates that do not have traceable certificates of authority. + with mandatory certificate validation, while the other options are + susceptible to man-in-the-middle attacks. NEVER A serve certificate will never be requested or checked. @@ -260,6 +258,16 @@ Certificate Checks : optional HARD The same as DEMAND. +.. _Custom CA Certificates: + +Custom CA Certificates : optional + Directory used by OpenSSL to find CAs for validating the LDAP server certificate. + Python 2.7.10 and later default to using the system certificate store, and + this should thus not be necessary when using certificates signed by a CA + trusted by the system. + It can be set to something like `/etc/openldap/cacerts` on older systems or + if using self-signed certificates. + .. _Base DN: Base DN : required diff --git a/kallithea/lib/auth_modules/auth_ldap.py b/kallithea/lib/auth_modules/auth_ldap.py --- a/kallithea/lib/auth_modules/auth_ldap.py +++ b/kallithea/lib/auth_modules/auth_ldap.py @@ -49,7 +49,7 @@ except ImportError: class AuthLdap(object): def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='', - tls_kind='PLAIN', tls_reqcert='DEMAND', ldap_version=3, + tls_kind='PLAIN', tls_reqcert='DEMAND', cacertdir=None, ldap_version=3, ldap_filter='(&(objectClass=user)(!(objectClass=computer)))', search_scope='SUBTREE', attr_login='uid'): if ldap is None: @@ -67,6 +67,8 @@ class AuthLdap(object): OPT_X_TLS_DEMAND = 2 self.TLS_REQCERT = getattr(ldap, 'OPT_X_TLS_%s' % tls_reqcert, OPT_X_TLS_DEMAND) + self.cacertdir = cacertdir + # split server into list self.LDAP_SERVER_ADDRESS = server.split(',') self.LDAP_SERVER_PORT = port @@ -107,9 +109,11 @@ class AuthLdap(object): if "," in username: raise LdapUsernameError("invalid character in username: ,") try: - if hasattr(ldap, 'OPT_X_TLS_CACERTDIR'): - ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, - '/etc/openldap/cacerts') + if self.cacertdir: + if hasattr(ldap, 'OPT_X_TLS_CACERTDIR'): + ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, self.cacertdir) + else: + log.debug("OPT_X_TLS_CACERTDIR is not available - can't set %s", self.cacertdir) ldap.set_option(ldap.OPT_REFERRALS, ldap.OPT_OFF) ldap.set_option(ldap.OPT_RESTART, ldap.OPT_ON) ldap.set_option(ldap.OPT_TIMEOUT, 20) @@ -168,7 +172,8 @@ class AuthLdap(object): log.debug("LDAP says no such user '%s' (%s)", uid, username) raise LdapUsernameError() except ldap.SERVER_DOWN: - raise LdapConnectionError("LDAP can't access authentication server") + # [0] might be {'info': "TLS error -8179:Peer's Certificate issuer is not recognized.", 'desc': "Can't contact LDAP server"} + raise LdapConnectionError("LDAP can't connect to authentication server") class KallitheaAuthPlugin(auth_modules.KallitheaExternalAuthPlugin): @@ -231,6 +236,13 @@ class KallitheaAuthPlugin(auth_modules.K "formname": "Certificate Checks" }, { + "name": "cacertdir", + "validator": self.validators.UnicodeString(strip=True), + "type": "string", + "description": "Optional: Custom CA certificate directory for validating LDAPS", + "formname": "Custom CA Certificates" + }, + { "name": "base_dn", "validator": self.validators.UnicodeString(strip=True), "type": "string", @@ -314,6 +326,7 @@ class KallitheaAuthPlugin(auth_modules.K 'bind_pass': settings.get('dn_pass'), 'tls_kind': settings.get('tls_kind'), 'tls_reqcert': settings.get('tls_reqcert'), + 'cacertdir': settings.get('cacertdir'), 'ldap_filter': settings.get('filter'), 'search_scope': settings.get('search_scope'), 'attr_login': settings.get('attr_login'), diff --git a/kallithea/tests/functional/test_admin_auth_settings.py b/kallithea/tests/functional/test_admin_auth_settings.py --- a/kallithea/tests/functional/test_admin_auth_settings.py +++ b/kallithea/tests/functional/test_admin_auth_settings.py @@ -30,6 +30,7 @@ class TestAuthSettingsController(TestCon 'auth_ldap_port': '999', 'auth_ldap_tls_kind': 'PLAIN', 'auth_ldap_tls_reqcert': 'NEVER', + 'auth_ldap_cacertdir': '', 'auth_ldap_dn_user': 'test_user', 'auth_ldap_dn_pass': 'test_pass', 'auth_ldap_base_dn': 'test_base_dn',