diff --git a/README.md b/README.md index 76aaba1..6c4fdac 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,27 @@ c.LDAPAuthenticator.tls_kwargs = { } ``` +If you have received a TLS handshake error, it could be that no cipher accepted +by LDAPAuthenticator is also accepted by the LDAP server. The default ciphers +accepted by LDAPAuthenticator is dependent on the Python version, where +upgrading to Python 3.10 is known to reduce the set of accepted ciphers. The +default list of ciphers stem from +[ssl.create_default_context().get_ciphers()](https://docs.python.org/3/library/ssl.html#ssl.create_default_context). + +To configure LDAPAuthenticator's accepted ciphers explicitly, you can do: + +```python +# default ciphers accepted with LDAPAuthenticator in Python < 3.10 +pre_python310_ciphers = "AES128-SHA:AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES256-GCM-SHA384:AES256-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-CHACHA20-POLY1305:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256" + +# default ciphers accepted with LDAPAuthenticator in Python >= 3.10 +post_python310_ciphers = "DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-CHACHA20-POLY1305:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256" + +c.LDAPAuthenticator.tls_kwargs = { + "ciphers": pre_python310_ciphers, +} +``` + #### `LDAPAuthenticator.server_port` Port on which to contact the LDAP server. diff --git a/ldapauthenticator/ldapauthenticator.py b/ldapauthenticator/ldapauthenticator.py index 969cf02..5f720a8 100644 --- a/ldapauthenticator/ldapauthenticator.py +++ b/ldapauthenticator/ldapauthenticator.py @@ -4,7 +4,7 @@ import ldap3 from jupyterhub.auth import Authenticator -from ldap3.core.exceptions import LDAPBindError +from ldap3.core.exceptions import LDAPBindError, LDAPSocketOpenError from ldap3.core.tls import Tls from ldap3.utils.conv import escape_filter_chars from ldap3.utils.dn import escape_rdn @@ -536,6 +536,17 @@ def get_connection(self, userdn, password): password=password, auto_bind=auto_bind, ) + except LDAPSocketOpenError as e: + if "handshake" in str(e).lower(): + self.log.error( + "A TLS handshake failure has occurred. " + "It could be an indication that no cipher accepted by " + "LDAPAuthenticator was accepted by the LDAP server. For " + "details on how to handle this, refer to documentation of " + "the tls_kwargs config on how to configure ciphers " + "https://github.com/jupyterhub/ldapauthenticator/#ldapauthenticatortls_kwargs." + ) + raise except LDAPBindError as e: self.log.debug( "Failed to bind {userdn}\n{e_type}: {e_msg}".format(