Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ and this project adheres to

- log: Clear handlers in `setup_logger()` by default to avoid duplicate log
lines, with optional clear=False to keep existing handlers (#88).
- auth: LDAP debug log search base error.
- auth:
- LDAP debug log search base error.
- LDAP TLS certificate validation with default system CA trust store by
preferring PEM bundle (CAFILE) over capath (CADIR).

## [1.8.0] - 2026-05-22

Expand Down
36 changes: 26 additions & 10 deletions src/authentication/rfl/authentication/ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import Optional, List, Tuple
from pathlib import Path
import logging
import os

try:
import ldap
Expand Down Expand Up @@ -91,19 +92,34 @@ def connection(self):
connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
# For LDAPS and STARTTLS, libldap require the path to CA certificates to be
# defined to authentication server certificate. If the cacert option is
# defined, use it else use default system CA certificates directory defined
# in OpenSSL library.
# defined, use it else use default system CA certificates defined in
# OpenSSL library. Prefer the PEM bundle file (CAFILE) when available,
# as modern distributions store trust anchors there rather than in a
# hashed certificate directory (CADIR).
if self.cacert is None:
import ssl

logger.debug(
"Using default system OpenSSL CA certificate directory to "
"authenticate server"
)
connection.set_option(
ldap.OPT_X_TLS_CACERTDIR,
ssl.get_default_verify_paths().openssl_capath,
)
paths = ssl.get_default_verify_paths()
if paths.openssl_cafile and os.path.isfile(paths.openssl_cafile):
logger.debug(
"Using default system OpenSSL CA certificate file %s to "
"authenticate server",
paths.openssl_cafile,
)
connection.set_option(
ldap.OPT_X_TLS_CACERTFILE,
paths.openssl_cafile,
)
elif paths.openssl_capath and os.path.isdir(paths.openssl_capath):
logger.debug(
"Using default system OpenSSL CA certificate directory %s "
"to authenticate server",
paths.openssl_capath,
)
connection.set_option(
ldap.OPT_X_TLS_CACERTDIR,
paths.openssl_capath,
)
else:
logger.debug(
"Using CA certification %s to authenticate server", self.cacert
Expand Down
15 changes: 11 additions & 4 deletions src/authentication/rfl/tests/test_ldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#
# SPDX-License-Identifier: LGPL-3.0-or-later

import os
import unittest
from unittest.mock import patch, Mock
from pathlib import Path
Expand Down Expand Up @@ -42,15 +43,21 @@ def test_connection_ssl_cert(self, mock_ldap):
mock_ldap_object.set_option.assert_not_called()

# With ldaps URI and no CA certificate path, check LDAP server certificate is
# required and validated with default system OpenSSL certificates directory.
# required and validated with default system OpenSSL CA certificates.
self.authentifier.uri = urllib.parse.urlparse("ldaps://localhost")
self.authentifier.connection()
mock_ldap_object.set_option.assert_any_call(
mock_ldap.OPT_X_TLS_REQUIRE_CERT, mock_ldap.OPT_X_TLS_DEMAND
)
mock_ldap_object.set_option.assert_any_call(
mock_ldap.OPT_X_TLS_CACERTDIR, ssl.get_default_verify_paths().openssl_capath
)
paths = ssl.get_default_verify_paths()
if paths.openssl_cafile and os.path.isfile(paths.openssl_cafile):
mock_ldap_object.set_option.assert_any_call(
mock_ldap.OPT_X_TLS_CACERTFILE, paths.openssl_cafile
)
elif paths.openssl_capath and os.path.isdir(paths.openssl_capath):
mock_ldap_object.set_option.assert_any_call(
mock_ldap.OPT_X_TLS_CACERTDIR, paths.openssl_capath
)
mock_ldap_object.reset_mock()

# With CA certificate path, check LDAP server certificate is required and
Expand Down
Loading