Open jgraichen opened 11 months ago
I can't tell from your issue if you're using starttls or port 636 SSL, either way you might want to try https://goauthentik.io/docs/installation/configuration#authentik_ldap__tls__ciphers to adjust the ciphers, and/or adding the Root CA to authentik, I think by default ldap3 (which is the LDAP library we use) uses the system CA store
Actually on further looking, we don't install ca-certificates in the container which probably doesn't help with this
@BeryJu Thank you for looking! I am using direct TLS with LDAPS on port 636.
ca-certificates
seems to be installed in the container:
> docker exec -it -u root goauthentik-worker-1 bash -l
root@989bd69f9ec4:/# apt show ca-certificates
Package: ca-certificates
Version: 20230311
Status: install ok installed
Priority: standard
Section: misc
Maintainer: Julien Cristau <jcristau@debian.org>
Installed-Size: 393 kB
Depends: openssl (>= 1.1.1), debconf (>= 0.5) | debconf-2.0
Breaks: ca-certificates-java (<< 20121112+nmu1)
Enhances: openssl
Download-Size: unknown
APT-Manual-Installed: yes
APT-Sources: /var/lib/dpkg/status
Description: Common CA certificates
I further tried debugging using ldapsearch
from inside the container (by installing ldap-utils
via APT). I didn't get a result yet, but at least TLS seems to work, according to the slapd
log:
Dec 03 19:08:20 ldap slapd[1928874]: conn=1011 fd=20 ACCEPT from IP=10.72.0.217:57242 (IP=0.0.0.0:636)
Dec 03 19:08:20 ldap slapd[1928874]: conn=1011 fd=20 TLS established tls_ssf=256 ssf=256 tls_proto=TLS1.3 tls_cipher=AES-256-GCM
Dec 03 19:08:20 ldap slapd[1928874]: conn=1011 fd=20 closed (connection lost)
root@989bd69f9ec4:/# apt update && apt install ldap-utils
[..]
root@989bd69f9ec4:/# ldapsearch -x -H ldaps://ldap1.pdm.aixn.de -b 'o=altimos'
ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)
Therefore, it does not seem to be a fundamental system/TLS issue.
For reference, the same command from the host does work:
> ldapsearch -x -H ldaps://ldap1.pdm.aixn.de:636 -b 'o=altimos' | head -n5
# extended LDIF
#
# LDAPv3
# base <o=altimos> with scope subtree
# filter: (objectclass=*)
There are no issues when connecting from a minimal python script inside the authentik worker container to the LDAP server:
authentik@ea28a5d1abf1:~$ python3 /tmp/test.py
DEBUG:ldap3:ERROR:detail level set to BASIC
DEBUG:ldap3:BASIC:instantiated Tls: <Tls(validate=<VerifyMode.CERT_REQUIRED: 2>)>
DEBUG:ldap3:BASIC:instantiated Server: <Server(host='ldap1.pdm.aixn.de', port=636, use_ssl=True, allowed_referral_hosts=[('*', True)], tls=Tls(validate=<VerifyMode.CERT_REQUIRED: 2>), get_info='ALL', connect_timeout=10, mode='IP_V6_PREFERRED')>
DEBUG:ldap3:BASIC:instantiated <SyncStrategy>: <ldaps://ldap1.pdm.aixn.de:636 - ssl - user: None - not lazy - unbound - closed - <no socket> - tls not started - not listening - No strategy - internal decoder - async - real DSA - not pooled - cannot stream output>
DEBUG:ldap3:BASIC:instantiated Connection: <Connection(server=Server(host='ldap1.pdm.aixn.de', port=636, use_ssl=True, allowed_referral_hosts=[('*', True)], tls=Tls(validate=<VerifyMode.CERT_REQUIRED: 2>), get_info='ALL', connect_timeout=10, mode='IP_V6_PREFERRED'), auto_bind='DEFAULT', version=3, authentication='ANONYMOUS', client_strategy='SYNC', auto_referrals=True, check_names=True, read_only=False, lazy=False, raise_exceptions=False, fast_decoder=True, auto_range=True, return_empty_attributes=True, auto_encode=True, auto_escape=True, use_referral_cache=False)>
DEBUG:ldap3:BASIC:start START TLS operation via <ldaps://ldap1.pdm.aixn.de:636 - ssl - user: None - not lazy - unbound - closed - <no socket> - tls not started - not listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:address for <ldaps://ldap1.pdm.aixn.de:636 - ssl> resolved as <[<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('10.72.0.16', 636)]>
DEBUG:ldap3:BASIC:obtained candidate address for <ldaps://ldap1.pdm.aixn.de:636 - ssl>: <[<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('10.72.0.16', 636)]> with mode IP_V6_PREFERRED
DEBUG:ldap3:BASIC:try to open candidate address [<AddressFamily.AF_INET: 2>, <SocketKind.SOCK_STREAM: 1>, 6, '', ('10.72.0.16', 636)]
DEBUG:ldap3:BASIC:refreshing server info for <ldaps://ldap1.pdm.aixn.de:636 - ssl - user: None - not lazy - unbound - open - <local: 172.25.0.2:52601 - remote: 10.72.0.16:636> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:start SEARCH operation via <ldaps://ldap1.pdm.aixn.de:636 - ssl - user: None - not lazy - unbound - open - <local: 172.25.0.2:52601 - remote: 10.72.0.16:636> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:done SEARCH operation, result <True>
DEBUG:ldap3:BASIC:DSA info read for <ldaps://ldap1.pdm.aixn.de:636 - ssl> via <ldaps://ldap1.pdm.aixn.de:636 - ssl - user: None - not lazy - unbound - open - <local: 172.25.0.2:52601 - remote: 10.72.0.16:636> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:start SEARCH operation via <ldaps://ldap1.pdm.aixn.de:636 - ssl - user: None - not lazy - unbound - open - <local: 172.25.0.2:52601 - remote: 10.72.0.16:636> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:done SEARCH operation, result <True>
DEBUG:ldap3:BASIC:schema read for <ldaps://ldap1.pdm.aixn.de:636 - ssl> via <ldaps://ldap1.pdm.aixn.de:636 - ssl - user: None - not lazy - unbound - open - <local: 172.25.0.2:52601 - remote: 10.72.0.16:636> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:done START TLS operation, result <False>
DEBUG:ldap3:BASIC:start SEARCH operation via <ldaps://ldap1.pdm.aixn.de:636 - ssl - user: None - not lazy - unbound - open - <local: 172.25.0.2:52601 - remote: 10.72.0.16:636> - tls not started - listening - SyncStrategy - internal decoder>
DEBUG:ldap3:BASIC:done SEARCH operation, result <False>
Test script:
import logging
import ssl
from ldap3 import ALL, Connection, Server, Tls
from ldap3.utils.log import BASIC, set_library_log_detail_level
logging.basicConfig(level=logging.DEBUG)
set_library_log_detail_level(BASIC)
tls = Tls(validate=ssl.CERT_REQUIRED)
server = Server(
"ldaps://ldap1.pdm.aixn.de",
get_info=ALL,
connect_timeout=10,
tls=tls,
)
conn = Connection(server)
conn.start_tls(read_server_info=False)
conn.search(
search_base="o=altimos",
search_filter="(objectClass=inetOrgPerson)",
)
The issue seems to be inside the authentik source and how ldap3 is configured/called/used.
either way you might want to try goauthentik.io/docs/installation/configuration#authentik_ldaptlsciphers to adjust the ciphers, and/or adding the Root CA to authentik, I think by default ldap3 (which is the LDAP library we use) uses the system CA store
Changing AUTHENTIK_LDAP__TLS__CIPHERS
has no effect at all, and the connection from inside the container does work as shown by the test script. There seems to be issues with authentik and how it configures/uses ldap3 inside the worker.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
@BeryJu The issue is still present, and as shown with the standalone test script, directly related to authentik only. The standalone script can successfully connect to a modern, secured LDAP even when run inside the container. Therefore, it cannot be an issue with Python, openSSL, or the network.
Tested again with 2023.10.7, the issue is still present:
{
"event": "Task authentik.sources.ldap.tasks.ldap_sync_single[fe33cefe-e1d4-44e8-9930-9b2225a46db2] raised unexpected: LDAPSocketOpenError(\"('socket ssl wrapping error: [SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1006)',)\")",
"exception": [
{
"exc_type": "Exception",
"exc_value": "('socket ssl wrapping error: [SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1006)',)",
"frames": [
{
"filename": "/ak-root/venv/lib/python3.11/site-packages/celery/app/trace.py",
"line": "",
"lineno": 477,
"locals": {},
"name": "trace_task"
},
{
"filename": "/ak-root/venv/lib/python3.11/site-packages/celery/app/trace.py",
"line": "",
"lineno": 760,
"locals": {},
"name": "__protected_call__"
},
{
"filename": "/authentik/sources/ldap/tasks.py",
"line": "",
"lineno": 59,
"locals": {},
"name": "ldap_sync_single"
},
{
"filename": "/authentik/sources/ldap/tasks.py",
"line": "",
"lineno": 76,
"locals": {},
"name": "ldap_sync_paginator"
},
{
"filename": "/authentik/sources/ldap/sync/base.py",
"line": "",
"lineno": 30,
"locals": {},
"name": "__init__"
},
{
"filename": "/authentik/sources/ldap/models.py",
"line": "",
"lineno": 176,
"locals": {},
"name": "connection"
},
{
"filename": "/ak-root/venv/lib/python3.11/site-packages/ldap3/core/connection.py",
"line": "",
"lineno": 589,
"locals": {},
"name": "bind"
},
{
"filename": "/ak-root/venv/lib/python3.11/site-packages/ldap3/strategy/sync.py",
"line": "",
"lineno": 57,
"locals": {},
"name": "open"
},
{
"filename": "/ak-root/venv/lib/python3.11/site-packages/ldap3/strategy/base.py",
"line": "",
"lineno": 146,
"locals": {},
"name": "open"
}
],
"is_cause": false,
"syntax_error": null
}
],
"level": "error",
"logger": "celery.app.trace",
"timestamp": 1707382656.7532616
}
Tested with 2024.2:
{
"event": "Task authentik.sources.ldap.tasks.ldap_sync_single[b870f9f2-6216-4e0a-8683-e62009243a39] raised unexpected: LDAPSocketOpenError(\"('socket ssl wrapping error: [SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1000)',)\")",
"exception": [
{
"exc_type": "Exception",
"exc_value": "('socket ssl wrapping error: [SSL: UNEXPECTED_EOF_WHILE_READING] EOF occurred in violation of protocol (_ssl.c:1000)',)",
"frames": [
{
"filename": "/ak-root/venv/lib/python3.12/site-packages/celery/app/trace.py",
"line": "",
"lineno": 477,
"locals": {},
"name": "trace_task"
},
{
"filename": "/ak-root/venv/lib/python3.12/site-packages/celery/app/trace.py",
"line": "",
"lineno": 760,
"locals": {},
"name": "__protected_call__"
},
{
"filename": "/authentik/sources/ldap/tasks.py",
"line": "",
"lineno": 78,
"locals": {},
"name": "ldap_sync_single"
},
{
"filename": "/authentik/sources/ldap/tasks.py",
"line": "",
"lineno": 95,
"locals": {},
"name": "ldap_sync_paginator"
},
{
"filename": "/authentik/sources/ldap/sync/base.py",
"line": "",
"lineno": 40,
"locals": {},
"name": "__init__"
},
{
"filename": "/authentik/sources/ldap/models.py",
"line": "",
"lineno": 184,
"locals": {},
"name": "connection"
},
{
"filename": "/ak-root/venv/lib/python3.12/site-packages/ldap3/core/connection.py",
"line": "",
"lineno": 589,
"locals": {},
"name": "bind"
},
{
"filename": "/ak-root/venv/lib/python3.12/site-packages/ldap3/strategy/sync.py",
"line": "",
"lineno": 57,
"locals": {},
"name": "open"
},
{
"filename": "/ak-root/venv/lib/python3.12/site-packages/ldap3/strategy/base.py",
"line": "",
"lineno": 146,
"locals": {},
"name": "open"
}
],
"is_cause": false,
"syntax_error": null
}
],
"level": "error",
"logger": "celery.app.trace",
"timestamp": 1709202541.6433022
}
Disabling the SNI option works (on main
branch). The reason for that is, when giving an ldaps://server
address, the code fails to extract a correct SNI:
# authentik/sources/ldap/models.py:149
if self.sni:
tls_kwargs["sni"] = self.server_uri.split(",", maxsplit=1)[0].strip()
The tls_kwargs
ends up with {'sni': 'ldaps://server'}
.
The code might need to parse whatever is given with e.g. urllib.parse
and take either netloc
or path
:
>>> urlparse("server")
ParseResult(scheme='', netloc='', path='server', params='', query='', fragment='')
>>> urlparse("ldap://server")
ParseResult(scheme='ldap', netloc='server', path='', params='', query='', fragment='')
>>> urlparse("ldaps://server")
ParseResult(scheme='ldaps', netloc='server', path='', params='', query='', fragment='')
Regarding the discussion about ca-certificates
: There appears to be no way at all to have the server certificate be verified with the system CA certs. Either no validation mode is set (defaulting to ssl.CERT_NONE
) or a peer certificate must be specified:
if self.peer_certificate:
tls_kwargs["ca_certs_data"] = self.peer_certificate.certificate_data
tls_kwargs["validate"] = CERT_REQUIRED
This does match the description in the UI:
Yet, not a very secure way ;)
--
Note: Running poetry run ak server
fails unless I manually export TMPDIR
env to a writable path:
.../authentik/lifecycle/ak: line 49: /authentik-mode: Permission denied
@BeryJu I am working on a fix for SNI, but how is it expected to work with multiple LDAP servers?
To what should e.g. ldaps://a,b,c
be expanded?
ldaps://a
, ldaps://b
, ldaps://c
ldaps://a
, (ldap://)a
, (ldap://)c
Should ldaps://a,b,c:636
work at all?
ldaps://a:636
, ldaps://b:636
, ldaps://c:636
ldaps://a
, (ldap://)b
, (ldap://)c:636
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
The issue that full URLs are passed to SNI is still present at https://github.com/goauthentik/authentik/blob/main/authentik/sources/ldap/models.py#L151?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
The code appears to still pass the full URL, including protocol and port as the SNI host, and will pass the first host in the list to all connections:
https://github.com/goauthentik/authentik/blob/main/authentik/sources/ldap/models.py#L155
Describe the bug
I am testing Authentik with LDAP federation using a TLS-only openLDAP. The LDAP server runs on Debian Bookworm with a normal TLS setup, a valid LE cert, TLS 1.2+, etc.
The Authentik 2023.10.4 image fails to connect to that LDAP error and instead logs an SSL error. The LDAP server reports a TLS negotiation failure.
The wire trace indicates a TLS v1.0 connection failure.
To Reproduce
Expected behavior
Connect to LDAP server over TLS and sync users and groups.
Logs
Error logged in worker:
Compose logs:
Version and Deployment (please complete the following information):
Additional context
slapd.conf
:openLDAP log:
testssl
of the LDAP server (no issues connecting):Wireshark recorded TLS failure: