diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ff7e12..9fa7cde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Project-specific changes only. See upstream [OpenLDAP release notes](https://www ### Added +- Add `OPENLDAP_BOOTSTRAP_DATA_LAST_BIND` and `OPENLDAP_BOOTSTRAP_DATA_LAST_BIND_PRECISION` + to track successful LDAP authentications in `pwdLastSuccess` (disabled by default). - Add `OPENLDAP_BOOTSTRAP_REFINT_NOTHING_DN` to configure `olcRefintNothing` and automatically bootstrap a passwordless replacement role for empty groups. An empty DN disables both, preserving the default behavior. diff --git a/README.md b/README.md index b656e47..94d2ef7 100644 --- a/README.md +++ b/README.md @@ -344,6 +344,8 @@ docker run --rm --user $UID -v $(pwd)/schema:/data \ | `OPENLDAP_BOOTSTRAP_CONFIG_ROOT_DN` | Root DN for `cn=config` | `cn=admin,cn=config` | | `OPENLDAP_BOOTSTRAP_CONFIG_ROOT_PASSWORD_HASHED` | Hashed password for `cn=config`; generated if empty | `` | | `OPENLDAP_BOOTSTRAP_DATA_DATABASE_MAX_SIZE` | MDB max size for main database | `10737418240` | +| `OPENLDAP_BOOTSTRAP_DATA_LAST_BIND` | Track successful LDAP authentications in `pwdLastSuccess` | `false` | +| `OPENLDAP_BOOTSTRAP_DATA_LAST_BIND_PRECISION` | Minimum interval between `pwdLastSuccess` updates, in seconds | `3600` | | `OPENLDAP_BOOTSTRAP_DATA_ROOT_DN` | Root DN for main database | `cn=admin,${OPENLDAP_BOOTSTRAP_SUFFIX}` | | `OPENLDAP_BOOTSTRAP_DATA_ROOT_PASSWORD_HASHED` | Hashed password for main database root DN; generated if empty | `` | | `OPENLDAP_BOOTSTRAP_DATA_READONLY` | Enable read-only data account creation | `false` | diff --git a/build/README.md b/build/README.md index 601596a..056a2e8 100644 --- a/build/README.md +++ b/build/README.md @@ -22,6 +22,8 @@ After the shared version check, `tests/` runs integration scenarios on each buil - default bootstrap; - alternative schemas (`nis`, `virtualMail`); +- last successful binds: disabled by default, precision throttling, failed binds + and compatibility with ppolicy; - refint empty-group placeholders: default behavior, automatic role creation and deletion of the last real group member with memberof enabled; - all features with 3 replicated nodes: all compatible modules and schemas, TLS required, diff --git a/build/tests/lastbind.go b/build/tests/lastbind.go new file mode 100644 index 0000000..da5c0e6 --- /dev/null +++ b/build/tests/lastbind.go @@ -0,0 +1,48 @@ +package tests + +import ( + "context" + _ "embed" + "fmt" + + "dagger.io/dagger" +) + +// LastBind checks successful bind tracking and precision, with and without ppolicy. +func LastBind(ctx context.Context, client *dagger.Client, image *dagger.Container) error { + passwords, err := rootPasswords(ctx, image) + if err != nil { + return err + } + + for _, mode := range []string{"default", "enabled", "precise", "ppolicy"} { + settings := env{} + if mode != "default" { + settings["OPENLDAP_BOOTSTRAP_DATA_LAST_BIND"] = "true" + } + if mode == "precise" { + settings["OPENLDAP_BOOTSTRAP_DATA_LAST_BIND_PRECISION"] = "0" + } + if mode == "ppolicy" { + settings["OPENLDAP_BOOTSTRAP_PPOLICY"] = "true" + } + if err := func() error { + node := "lastbind-" + mode + service, err := startNode(ctx, image, node, merge(passwords, settings), nil, nil) + if err != nil { + return err + } + defer stopNodes(ctx, []*dagger.Service{service}) + return check(ctx, image, nil, env{ + "TEST_NODES": node, + "TEST_LAST_BIND_MODE": mode, + }, lastBindCheck) + }(); err != nil { + return fmt.Errorf("last bind %s: %w", mode, err) + } + } + return nil +} + +//go:embed testdata/lastbind.sh +var lastBindCheck string diff --git a/build/tests/test.go b/build/tests/test.go index d995153..6ef4026 100644 --- a/build/tests/test.go +++ b/build/tests/test.go @@ -20,6 +20,7 @@ type scenario struct { var scenarios = []scenario{ {"default bootstrap", Default}, {"alternative schemas", AlternativeSchemas}, + {"last successful binds", LastBind}, {"refint empty-group placeholders", RefintNothing}, {"all features with 3 replicated nodes", AllFeatures}, {"backup and restore", BackupRestore}, diff --git a/build/tests/testdata/lastbind.sh b/build/tests/testdata/lastbind.sh new file mode 100644 index 0000000..ec3c74d --- /dev/null +++ b/build/tests/testdata/lastbind.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +set -euo pipefail + +# if container log level is trace: +# print commands and their arguments as they are executed +container log level eq trace && set -x + +# shellcheck disable=SC1091 +source /tmp/openldap-test-lib.sh + +URI="ldap://${TEST_NODES}:3890" +USER_DN="uid=lastbind-user,${TEST_SUFFIX}" + +eventually has_entry "${URI}" "${TEST_SUFFIX}" +expect_rc 0 bind_search "${URI}" "${CONFIG_ADMIN_DN}" \ + -b 'olcDatabase={1}mdb,cn=config' -s base olcLastBind olcLastBindPrecision + +if [ "${TEST_LAST_BIND_MODE}" = "default" ]; then + expect_output '^olcLastBind: FALSE$' +else + expect_output '^olcLastBind: TRUE$' +fi +if [ "${TEST_LAST_BIND_MODE}" = "precise" ]; then + expect_output '^olcLastBindPrecision: 0$' +else + expect_output '^olcLastBindPrecision: 3600$' +fi + +expect_rc 0 bind_modify "${URI}" "${ADMIN_DN}" -a <