changeset 6330:7ce3897bacd0

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!
author Mads Kiilerich <madski@unity3d.com>
date Tue, 15 Nov 2016 22:53:41 +0100
parents 16b685da1117
children 949c843bb535
files docs/setup.rst kallithea/lib/auth_modules/auth_ldap.py kallithea/tests/functional/test_admin_auth_settings.py
diffstat 3 files changed, 31 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/docs/setup.rst	Tue Nov 15 22:53:41 2016 +0100
+++ b/docs/setup.rst	Tue Nov 15 22:53:41 2016 +0100
@@ -235,10 +235,8 @@
 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 @@
     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
--- a/kallithea/lib/auth_modules/auth_ldap.py	Tue Nov 15 22:53:41 2016 +0100
+++ b/kallithea/lib/auth_modules/auth_ldap.py	Tue Nov 15 22:53:41 2016 +0100
@@ -49,7 +49,7 @@
 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 @@
         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 @@
         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 @@
             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 @@
                 "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 @@
             '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'),
--- a/kallithea/tests/functional/test_admin_auth_settings.py	Tue Nov 15 22:53:41 2016 +0100
+++ b/kallithea/tests/functional/test_admin_auth_settings.py	Tue Nov 15 22:53:41 2016 +0100
@@ -30,6 +30,7 @@
                        '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',