From 3c4cf4fc4dca2da938bb99abd4ef5bef58571ad6 Mon Sep 17 00:00:00 2001 From: Tom Payerle Date: Tue, 10 Dec 2024 14:00:36 -0500 Subject: [PATCH] Updates to support TLS in ldap_user_search plugin This PR should address concerns of #631 Basically: 1) Adds new config parameter LDAP_USER_SEARCH_CERT_VALIDATE_MODE which is passed to the ldap3.Tls constructor as validate. Accepts as values: 'required' : Certs are required and must validate 'optional' : Certs are optional, but must validate if provided 'none' (or None): Certs are ignored. The default is None 2) The LDAP_USER_SEARCH_CERT_VALIDATE_MODE is passed as the validate field to the ldap3.Tls constructor 3) If LDAP_USE_TLS is set, we pass the connection parameter 'auto_bind' as ldap3.AUTO_BIND_TLS_BEFORE_BIND instead of simply True Inspection of ldap3 code shows that when this parameter is set to True (a value which is no longer listed in docs as valid) it is treated as AUTO_BIND_NO_TLS, so the previous before of leaving this as True was not doing TLS despite claiming to do TLS. This fix should change that. --- coldfront/config/plugins/ldap_user_search.py | 1 + coldfront/plugins/ldap_user_search/README.md | 1 + coldfront/plugins/ldap_user_search/utils.py | 18 ++++++++++++++++-- docs/pages/config.md | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/coldfront/config/plugins/ldap_user_search.py b/coldfront/config/plugins/ldap_user_search.py index 9197bf264..1438d96d6 100644 --- a/coldfront/config/plugins/ldap_user_search.py +++ b/coldfront/config/plugins/ldap_user_search.py @@ -20,5 +20,6 @@ LDAP_USER_SEARCH_PRIV_KEY_FILE = ENV.str("LDAP_USER_SEARCH_PRIV_KEY_FILE", default=None) LDAP_USER_SEARCH_CERT_FILE = ENV.str("LDAP_USER_SEARCH_CERT_FILE", default=None) LDAP_USER_SEARCH_CACERT_FILE = ENV.str("LDAP_USER_SEARCH_CACERT_FILE", default=None) +LDAP_USER_SEARCH_CERT_VALIDATE_MODE = ENV.str("LDAP_USER_SEARCH_CERT_VALIDATE_MODE", default=None) ADDITIONAL_USER_SEARCH_CLASSES = ['coldfront.plugins.ldap_user_search.utils.LDAPUserSearch'] diff --git a/coldfront/plugins/ldap_user_search/README.md b/coldfront/plugins/ldap_user_search/README.md index d33d530c7..57da2b468 100644 --- a/coldfront/plugins/ldap_user_search/README.md +++ b/coldfront/plugins/ldap_user_search/README.md @@ -39,6 +39,7 @@ To enable this plugin set the following applicable environment variables: | `LDAP_USER_SEARCH_PRIV_KEY_FILE` | None | Path to the private key file | | `LDAP_USER_SEARCH_CERT_FILE` | None | Path to the certificate file | | `LDAP_USER_SEARCH_CACERT_FILE` | None | Path to the CA certificate file | +| `LDAP_USER_SEARCH_CERT_VALIDATE_MODE` | none | The extent to which the certificate is validated. Can be 'required' (the certificate is required and validated), 'optional' (certificate is optional but validated if provided), 'none' (certs are ignored) | The following can be set in your local settings: | `LDAP_USER_SEARCH_ATTRIBUTE_MAP` | `{"username": "uid", "last_name": "sn", "first_name": "givenName", "email": "mail"}` | A mapping from ColdFront user attributes to LDAP attributes. | diff --git a/coldfront/plugins/ldap_user_search/utils.py b/coldfront/plugins/ldap_user_search/utils.py index 1ee7ba5e5..b52751d5d 100644 --- a/coldfront/plugins/ldap_user_search/utils.py +++ b/coldfront/plugins/ldap_user_search/utils.py @@ -4,7 +4,8 @@ import ldap.filter from coldfront.core.user.utils import UserSearch from coldfront.core.utils.common import import_from_settings -from ldap3 import Connection, Server, Tls, get_config_parameter, set_config_parameter, SASL +from ldap3 import Connection, Server, Tls, get_config_parameter, set_config_parameter, SASL, AUTO_BIND_TLS_BEFORE_BIND +import ssl logger = logging.getLogger(__name__) @@ -26,6 +27,7 @@ def __init__(self, user_search_string, search_by): self.LDAP_PRIV_KEY_FILE = import_from_settings('LDAP_USER_SEARCH_PRIV_KEY_FILE', None) self.LDAP_CERT_FILE = import_from_settings('LDAP_USER_SEARCH_CERT_FILE', None) self.LDAP_CACERT_FILE = import_from_settings('LDAP_USER_SEARCH_CACERT_FILE', None) + self.LDAP_CERT_VALIDATE_MODE = import_from_settings('LDAP_USER_SEARCH_CERT_VALIDATE_MODE', None) self.USERNAME_ONLY_ATTR = import_from_settings('LDAP_USER_SEARCH_USERNAME_ONLY_ATTR', 'username') self.ATTRIBUTE_MAP = import_from_settings('LDAP_USER_SEARCH_ATTRIBUTE_MAP', { "username": "uid", @@ -37,14 +39,26 @@ def __init__(self, user_search_string, search_by): tls = None if self.LDAP_USE_TLS: + ldap_cert_validate_mode = ssl.CERT_NONE + if self.LDAP_CERT_VALIDATE_MODE == 'none': + ldap_cert_validate_mode = ssl.CERT_NONE + elif self.LDAP_CERT_VALIDATE_MODE == 'optional': + ldap_cert_validate_mode = ssl.CERT_OPTIONAL + elif self.LDAP_CERT_VALIDATE_MODE == 'required': + ldap_cert_validate_mode = ssl.CERT_REQUIRED + tls = Tls( local_private_key_file=self.LDAP_PRIV_KEY_FILE, local_certificate_file=self.LDAP_CERT_FILE, ca_certs_file=self.LDAP_CACERT_FILE, + validate=ldap_cert_validate_mode, ) self.server = Server(self.LDAP_SERVER_URI, use_ssl=self.LDAP_USE_SSL, connect_timeout=self.LDAP_CONNECT_TIMEOUT, tls=tls) - conn_params = {"auto_bind": True} + auto_bind = True + if self.LDAP_USE_TLS: + auto_bind = AUTO_BIND_TLS_BEFORE_BIND + conn_params = {"auto_bind": auto_bind} if self.LDAP_SASL_MECHANISM: conn_params["sasl_mechanism"] = self.LDAP_SASL_MECHANISM conn_params["sasl_credentials"] = self.LDAP_SASL_CREDENTIALS diff --git a/docs/pages/config.md b/docs/pages/config.md index e2f4ec396..1decfba9e 100644 --- a/docs/pages/config.md +++ b/docs/pages/config.md @@ -266,6 +266,7 @@ exist in your backend LDAP to show up in the ColdFront user search. | LDAP_USER_SEARCH_PRIV_KEY_FILE | Path to the private key file. | | LDAP_USER_SEARCH_CERT_FILE | Path to the certificate file. | | LDAP_USER_SEARCH_CACERT_FILE | Path to the CA cert file. | +| LDAP_USER_SEARCH_CERT_VALIDATE_MODE | Whether to require/validate certs. If 'required', certs are required and validated. If 'optional', certs are optional but validated if provided. If 'none' (the default) certs are ignored. | ## Advanced Configuration