Skip to content
Open
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
33 changes: 16 additions & 17 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 14 additions & 1 deletion changelog
Original file line number Diff line number Diff line change
@@ -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 <release@turnkeylinux.org> Mon, 24 Aug 2026 00:00:00 +0000

turnkey-openldap-18.1 (1) turnkey; urgency=low

* v18.1 rebuild - includes latest Debian & TurnKey packages.
Expand Down Expand Up @@ -299,4 +313,3 @@ turnkey-openldap-12.0 (1) turnkey; urgency=low
appliances. Here we only describe changes specific to this appliance.

-- Alon Swartz <alon@turnkeylinux.org> Wed, 01 Aug 2012 08:00:00 +0200

94 changes: 94 additions & 0 deletions docs/v19.0-testing.md
Original file line number Diff line number Diff line change
@@ -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.
10 changes: 3 additions & 7 deletions overlay/usr/lib/inithooks/bin/openldap-reinit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -154,9 +154,6 @@ olcTLSCertificateFile: $TLS_LDAP_CRT
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: $TLS_LDAP_KEY
-
add: olcTLSCipherSuite
olcTLSCipherSuite: normal
-
add: olcTLSVerifyClient
olcTLSVerifyClient: never
EOL
Expand Down Expand Up @@ -309,4 +306,3 @@ if [ "$SLAPD_RUNNING" == "0" ]; then
else
stop_slapd
fi

2 changes: 1 addition & 1 deletion overlay/usr/lib/inithooks/bin/openldap.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__":
Expand Down
2 changes: 1 addition & 1 deletion plan/main
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
118 changes: 118 additions & 0 deletions tests/v19.sh
Original file line number Diff line number Diff line change
@@ -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" <<EOF
dn: $entry_dn
objectClass: top
objectClass: inetOrgPerson
uid: $entry_uid
cn: TurnKey v19 LDAP Test
sn: Test
description: openldap-roundtrip-ok
EOF
ldapadd -x -H ldap://127.0.0.1 \
-D "$admin_dn" -w "$password" -f "$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" <<EOF
package_source=Debian 13 Trixie APT repositories for OpenLDAP, ldap-utils, phpLDAPadmin, Lighttpd, PHP-FPM and LDAP client configuration; TurnKey APT for Webmin LDAP module
installed_version=slapd $slapd_version; ldap-utils $ldap_utils_version; phpldapadmin $phpldapadmin_version; lighttpd $lighttpd_version; php-fpm $php_version; libldap-common $libldap_version
runtime_checks=normal init; slapd, Lighttpd and PHP-FPM active; administrator LDAP and trusted LDAPS binds; default Users and Groups structure; LDAP add, search and delete round trip; phpLDAPadmin HTTPS interface; Webmin LDAP module
updater_command=apt-get update; apt-cache policy slapd ldap-utils phpldapadmin lighttpd php-fpm libldap-common
updater_result=signed metadata refreshed; eligible candidates found; installed versions unchanged
updater_channel=Debian Trixie and TurnKey Trixie APT repositories
integrity_evidence=APT accepted signed repository metadata through configured Deb822 sources and keyrings; no Bookworm source remained
EOF