diff --git a/README.rst b/README.rst index 686c82c..84dfb32 100644 --- a/README.rst +++ b/README.rst @@ -12,31 +12,30 @@ and on top of that: - OpenLDAP configurations: - - Installed and maintained through package management system (slapd - ldap-utils packages) - - Set LDAP domain and admin password on firstboot (convenience, - security). - - TLS support for ldaps out of the box (security). Note, you can - find the pre-generated CA certificate as /etc/ldap/tls/ca_cert.pem - and/or regenerate one with the 'turnkey-regen-ldap-certs' command. - - Includes Users/Groups OU and default PosixGroup (convenience). - -- Includes phpLDAPadmin for web based LDAP administration, with SSL - support out of the box. - - - Installed from upstream source code to /var/www/phpldapadmin + - OpenLDAP and its client utilities installed and maintained through + Debian's package management system. + - LDAP domain and administrator password configured at first boot. + - TLS support for LDAPS out of the box. The generated CA certificate is + available at ``/etc/ldap/tls/ca_cert.pem`` and certificates can be + regenerated with ``turnkey-regen-ldap-certs``. + - Users and Groups organizational units plus a default ``users`` POSIX + group. + +- phpLDAPadmin installed from Debian and served over TLS for web-based LDAP + administration. -- Webmin modules for configuring Apache2, PHP, MySQL and Postfix. +- Webmin LDAP server module. See the `OpenLDAP docs`_ for further details. Credentials *(passwords set at first boot)* ------------------------------------------- -- Webmin, SSH, MySQL: username **root** -- OpenLDAP: default domain **example.com** +- Webmin, SSH: username **root** +- OpenLDAP and phpLDAPadmin: administrator DN **cn=admin,dc=example,dc=com** + for the default domain **example.com** -.. _OpenLDAP: http://www.openldap.org/ +.. _OpenLDAP: https://www.openldap.org/ .. _TurnKey Core: https://www.turnkeylinux.org/core .. _OpenLDAP docs: https://www.turnkeylinux.org/docs/openldap diff --git a/changelog b/changelog index 339affb..5e82a80 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,17 @@ +turnkey-openldap-19.0 (1) turnkey; urgency=low + + * Upgrade the base distribution to Debian 13/Trixie. + + * Update OpenLDAP, phpLDAPadmin, Lighttpd and PHP-FPM from the Debian Trixie + repositories. + + * Retain first-boot domain and administrator configuration, generated LDAPS + certificates, the default directory structure and the Webmin LDAP module. + + * See the TurnKey Core 19.0 changelog for changes common to all appliances. + + -- TurnKey Linux release engineering Mon, 24 Aug 2026 00:00:00 +0000 + turnkey-openldap-18.1 (1) turnkey; urgency=low * v18.1 rebuild - includes latest Debian & TurnKey packages. @@ -299,4 +313,3 @@ turnkey-openldap-12.0 (1) turnkey; urgency=low appliances. Here we only describe changes specific to this appliance. -- Alon Swartz Wed, 01 Aug 2012 08:00:00 +0200 - diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md new file mode 100644 index 0000000..7b5c3d7 --- /dev/null +++ b/docs/v19.0-testing.md @@ -0,0 +1,94 @@ +# OpenLDAP 19.0 testing + +## Scope and source decision + +OpenLDAP 19.0 uses Debian 13 Trixie packages for the OpenLDAP server and +clients, phpLDAPadmin, Lighttpd, PHP-FPM, LDAPS certificate tooling and LDAP +client configuration. Debian supplies the complete application stack, so no +upstream repository or downloaded application artifact is required. The +Webmin LDAP server module continues to come from the signed TurnKey Trixie +repository. + +The acceptance command is: + +```sh +/sandboxed-git/turnkey/tools/test-v19-appliance openldap \ + --source /home/agent/.local/worktrees/turnkey-apps/openldap/wish-openldap-v19-trixie +``` + +It builds a Trixie root filesystem in TKLDev, imports the configured root into +Docker, starts the normal systemd init path with first-boot credentials and the +test domain, then runs `tests/v19.sh`. The retained report under +`~/.local/state/turnkey-v19-harness/runs/openldap/` records the exact source +and harness commits, archive and tree hashes, installed versions, +configured-root mode, application evidence, updater evidence, cleanup result, +and verdict. + +## Acceptance result + +Run `20260824t090202z-3087-7105` passed against source commit +`91028f2a8367e620409dbb729168ddc368071075` with harness commit +`b6f8b8c2f3e8f00fd5cf36869e645fc08f01f87e`. The source archive and +transport archive both have SHA-256 +`5591f973238e5a2be00a9239092094e69f9ed52830b2535bbfde99736b831406`. +The retained report is +`~/.local/state/turnkey-v19-harness/runs/openldap/20260824t090202z-3087-7105/report.txt`. + +The run recorded these results: + +- Trixie HTTPS preflight, root filesystem build, Docker import, normal systemd + boot, runtime tests and cleanup all passed. +- `slapd`, Lighttpd and PHP-FPM were active after first boot. +- Administrator LDAP and trusted LDAPS binds passed. The default directory + structure and an add, search and delete round trip passed. +- The phpLDAPadmin HTTPS interface and installed Webmin LDAP module passed. +- Installed identity-defining versions were `slapd 2.6.10+dfsg-1`, + `ldap-utils 2.6.10+dfsg-1`, `phpldapadmin 1.2.6.7-4`, + `lighttpd 1.4.79-2`, `php-fpm 2:8.4+96` and + `libldap-common 2.6.10+dfsg-1`. +- The updater check refreshed signed Trixie metadata, found eligible + candidates for every checked package and left the installed versions + unchanged. + +During migration, a disposable Debian Trixie container isolated an LDAP error +80 to the old `olcTLSCipherSuite: normal` value. Trixie links slapd to OpenSSL +3, so v19 uses the maintained Debian/OpenSSL cipher defaults. The CA, +certificate, key and client verification attributes were accepted unchanged. + +## README crosswalk + +| README contract | Focused check | Required result | Evidence | +| --- | --- | --- | --- | +| OpenLDAP server and clients come from Debian packages | Query installed versions, refresh APT, and inspect candidates for `slapd`, `ldap-utils` and `libldap-common` | Signed Trixie metadata is accepted and each installed package retains an eligible candidate | `tests/v19.sh`; harness updater fields | +| First boot configures the LDAP domain and administrator password | Discover the naming context and perform an authenticated bind as its `cn=admin` DN using the first-boot password | Administrator bind succeeds against the configured naming context | `tests/v19.sh`; harness `runtime_checks` | +| Users and Groups organizational units plus the default `users` POSIX group are present | Search the configured naming context for the three documented entries | All documented entries are returned | `tests/v19.sh` | +| OpenLDAP stores and retrieves directory entries | Add a disposable `inetOrgPerson`, search its attributes, delete it, and prove it is no longer searchable | Add, search, attribute verification and delete complete successfully | `tests/v19.sh`; harness `runtime_checks` | +| LDAPS is available with a generated CA certificate | Bind through the appliance hostname on port 636 with certificate verification required and the generated CA configured | Trusted administrator LDAPS bind succeeds and port 636 is listening | `tests/v19.sh`; harness `runtime_checks` | +| phpLDAPadmin provides TLS-protected web administration | Request `https://127.0.0.1/` while Lighttpd and PHP-FPM are active | phpLDAPadmin login interface is returned over HTTPS | `tests/v19.sh`; installed versions in report | +| Webmin includes the LDAP server module | Query dpkg state for `webmin-ldap-server` | Module is installed | `tests/v19.sh` | +| Webmin and SSH use the inherited root credential; Postfix is locally bound | Covered by the unchanged Core implementation | Passed by the Core 19 baseline | Core run `20260824t010251z-1634-32241`, source `24c82ee3540ce545422742b0e28ba6b687c53ec2`, verdict `PASS` | + +The README's external reference links are descriptive and do not add runtime +behaviors. + +## Updater check + +`tests/v19.sh` runs `apt-get update` and checks eligible candidates for +`slapd`, `ldap-utils`, `phpldapadmin`, `lighttpd`, `php-fpm` and +`libldap-common` with `apt-cache policy`. It records installed versions before +the refresh and proves they remain unchanged afterward. A pass means APT +accepted signed Debian and TurnKey Trixie metadata, every identity-defining +package has an eligible candidate, and no Bookworm source remains configured. + +## Known limitation + +The Docker acceptance path does not exercise the installer, kernel, bootloader +or physical hardware. This appliance adds no behavior at those boundaries, so +the validated Core 19 baseline supplies the inherited evidence. + +## Deferred issues + +No nonblocking application issues were observed in the supported OpenLDAP, +LDAPS or management-interface flows. The configured-root overlayfs fallback +and Docker hostname and sysctl warnings are limitations of this inexpensive +test path and are recorded in the retained harness report. diff --git a/overlay/usr/lib/inithooks/bin/openldap-reinit.sh b/overlay/usr/lib/inithooks/bin/openldap-reinit.sh index f0c3e2a..2387669 100755 --- a/overlay/usr/lib/inithooks/bin/openldap-reinit.sh +++ b/overlay/usr/lib/inithooks/bin/openldap-reinit.sh @@ -22,12 +22,12 @@ stop_slapd() { if systemd-detect-virt -c 2>&1>/dev/null; then # workaround for systemctl stop not working for slapd on container builds echo "WARN: using workaround for stopping slapd on container ..." - pid=$(pgrep slapd) + pid=$(pgrep slapd || true) sleep 5 if [[ -n "$pid" ]] && ps -p $pid >/dev/null; then kill $pid else - return + return 0 fi for _ in {0..10}; do if ! ps -p $pid >/dev/null; then @@ -48,7 +48,7 @@ restart_slapd() { if systemd-detect-virt -c 2>&1>/dev/null; then # workaround for systemctl stop not working for slapd on container builds stop_slapd - systemctl start slapd + service slapd start else systemctl restart slapd fi @@ -154,9 +154,6 @@ olcTLSCertificateFile: $TLS_LDAP_CRT add: olcTLSCertificateKeyFile olcTLSCertificateKeyFile: $TLS_LDAP_KEY - -add: olcTLSCipherSuite -olcTLSCipherSuite: normal -- add: olcTLSVerifyClient olcTLSVerifyClient: never EOL @@ -309,4 +306,3 @@ if [ "$SLAPD_RUNNING" == "0" ]; then else stop_slapd fi - diff --git a/overlay/usr/lib/inithooks/bin/openldap.py b/overlay/usr/lib/inithooks/bin/openldap.py index 2ced702..2d2842c 100755 --- a/overlay/usr/lib/inithooks/bin/openldap.py +++ b/overlay/usr/lib/inithooks/bin/openldap.py @@ -65,7 +65,7 @@ def main(): inithooks_cache.write('APP_DOMAIN', domain) script = os.path.join(os.path.dirname(__file__), 'openldap-reinit.sh') - subprocess.check_output([script, domain, password]) + subprocess.check_call([script, domain, password]) if __name__ == "__main__": diff --git a/plan/main b/plan/main index c6b35d0..7328b21 100644 --- a/plan/main +++ b/plan/main @@ -4,7 +4,7 @@ samba slapd ldap-utils ldapscripts -libldap-common /* is only recommends in Bookworm - closes #1939 */ +libldap-common /* provides the TLS-aware LDAP client configuration */ gnutls-bin lighttpd diff --git a/tests/v19.sh b/tests/v19.sh new file mode 100755 index 0000000..6f0dd32 --- /dev/null +++ b/tests/v19.sh @@ -0,0 +1,118 @@ +#!/bin/bash +set -Eeuo pipefail +umask 077 + +result=${TKL_TEST_RESULT:?TKL_TEST_RESULT is required} +password=${TKL_TEST_APP_PASS:?TKL_TEST_APP_PASS is required} +entry_uid=tklv19$$ +entry_file=/tmp/tkl-openldap-entry.$$.ldif +response=/tmp/tkl-openldap-response.$$ +policy=/tmp/tkl-openldap-policy.$$ +entry_dn= +admin_dn= + +cleanup() { + if [[ -n $entry_dn ]]; then + ldapdelete -x -H ldap://127.0.0.1 \ + -D "$admin_dn" -w "$password" "$entry_dn" >/dev/null 2>&1 || true + fi + rm -f -- "$entry_file" "$response" "$policy" +} +trap cleanup EXIT + +php_fpm_service=$(systemctl list-unit-files 'php*-fpm.service' --no-legend | + awk 'NR == 1 {print $1}') +test -n "$php_fpm_service" +systemctl --quiet is-active slapd.service lighttpd.service \ + "$php_fpm_service" multi-user.target + +slapd_version=$(dpkg-query -W -f='${Version}' slapd) +ldap_utils_version=$(dpkg-query -W -f='${Version}' ldap-utils) +phpldapadmin_version=$(dpkg-query -W -f='${Version}' phpldapadmin) +lighttpd_version=$(dpkg-query -W -f='${Version}' lighttpd) +php_version=$(dpkg-query -W -f='${Version}' php-fpm) +libldap_version=$(dpkg-query -W -f='${Version}' libldap-common) + +base_dn=$(ldapsearch -LLL -x -H ldap://127.0.0.1 \ + -s base -b '' namingContexts | + awk '/^namingContexts: / && !found { + sub(/^namingContexts: /, ""); print; found=1 + }') +test -n "$base_dn" +admin_dn="cn=admin,$base_dn" + +ldapwhoami -x -H ldap://127.0.0.1 \ + -D "$admin_dn" -w "$password" >"$response" +grep -Fqi "dn:$admin_dn" "$response" + +ldapsearch -LLL -x -H ldap://127.0.0.1 \ + -D "$admin_dn" -w "$password" -b "$base_dn" \ + '(|(ou=Users)(ou=Groups)(cn=users))' dn >"$response" +grep -Fqi "dn: ou=Users,$base_dn" "$response" +grep -Fqi "dn: ou=Groups,$base_dn" "$response" +grep -Fqi "dn: cn=users,ou=Groups,$base_dn" "$response" + +entry_dn="uid=$entry_uid,ou=Users,$base_dn" +cat >"$entry_file" </dev/null +ldapsearch -LLL -x -H ldap://127.0.0.1 \ + -D "$admin_dn" -w "$password" -b "$entry_dn" -s base \ + '(objectClass=inetOrgPerson)' uid description >"$response" +grep -Fxq "uid: $entry_uid" "$response" +grep -Fxq 'description: openldap-roundtrip-ok' "$response" +ldapdelete -x -H ldap://127.0.0.1 \ + -D "$admin_dn" -w "$password" "$entry_dn" +entry_dn= +if ldapsearch -LLL -x -H ldap://127.0.0.1 \ + -D "$admin_dn" -w "$password" -b "uid=$entry_uid,ou=Users,$base_dn" \ + -s base dn >/dev/null 2>&1; then + echo 'deleted LDAP entry remained searchable' >&2 + exit 1 +fi + +ldap_host=$(hostname -f) +test -s /etc/ldap/tls/ca_cert.pem +test -s /etc/ldap/tls/openldap_crt.pem +LDAPTLS_CACERT=/etc/ldap/tls/ca_cert.pem LDAPTLS_REQCERT=demand \ + ldapwhoami -x -H "ldaps://$ldap_host" \ + -D "$admin_dn" -w "$password" >"$response" +grep -Fqi "dn:$admin_dn" "$response" +ss -ltn | awk '$4 ~ /:636$/ { found=1 } END { exit !found }' + +curl --insecure --fail --location --silent --show-error \ + https://127.0.0.1/ >"$response" +grep -qi 'phpLDAPadmin' "$response" +dpkg-query -W webmin-ldap-server >/dev/null + +before="$slapd_version|$ldap_utils_version|$phpldapadmin_version|$lighttpd_version|$php_version|$libldap_version" +apt-get update >/dev/null +for package in slapd ldap-utils phpldapadmin lighttpd php-fpm libldap-common; do + apt-cache policy "$package" >"$policy" + candidate=$(awk '/Candidate:/ {print $2}' "$policy") + test -n "$candidate" + test "$candidate" != '(none)' + grep -Eq 'http://deb\.debian\.org/debian trixie/main|http://security\.debian\.org/debian-security trixie-security/main' "$policy" +done +after="$(dpkg-query -W -f='${Version}' slapd)|$(dpkg-query -W -f='${Version}' ldap-utils)|$(dpkg-query -W -f='${Version}' phpldapadmin)|$(dpkg-query -W -f='${Version}' lighttpd)|$(dpkg-query -W -f='${Version}' php-fpm)|$(dpkg-query -W -f='${Version}' libldap-common)" +test "$after" = "$before" +grep -Rqs '^Suites: trixie' /etc/apt/sources.list.d +! grep -Rqi bookworm /etc/apt/sources.list.d + +cat >"$result" <