Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a13eab-1e6a-7b33-9c37-372e90ada3a2
136 lines
6.8 KiB
Markdown
136 lines
6.8 KiB
Markdown
# Owner-invoked LLDAP group maintenance
|
|
|
|
Source repair: NK-WP-0049-T02, 2026-10-14. The shell entry points now delegate to
|
|
`lldap_admin.py`, which reuses the existing identity-provisioner directory
|
|
operations and service-group validation. This is the bootstrap/maintenance path,
|
|
not a new account lifecycle service, credential broker or authority boundary.
|
|
|
|
## Scope and custody
|
|
|
|
An authorized IAM custodian supplies an explicit private temporary custody root
|
|
outside Git. The root and `lldap/` directory must be owned by the caller and mode
|
|
0700; `lldap/secrets.env` must be an owned, single-link, non-symlink regular file
|
|
with mode 0600. The existing custody reader parses `LLDAP_LDAP_USER_PASS` literally.
|
|
Do not add shell quotes, `export`, or executable commands. Other generated fields
|
|
may remain in that file; this tool does not rewrite it.
|
|
|
|
Credential ownership/routing and an exact live change approval remain required.
|
|
The adapter does not fetch Kubernetes Secrets, use an ambient admin password,
|
|
create accounts or credentials, invoke curl/kubectl, or install a permanent key.
|
|
A preview digest is an observation binding, not an approval, caller identity or
|
|
runtime isolation proof. Use the owner's protected execution context; do not
|
|
hand the directory admin credential to an unisolated agent.
|
|
|
|
The default origin is `https://lldap.coulomb.social`. An explicit literal
|
|
`127.0.0.1` or `[::1]` origin with a port is accepted for an owner-approved forward
|
|
or isolated fixture. `localhost`, arbitrary hosts, HTTP on the public host,
|
|
userinfo, paths, queries and fragments are refused. TLS uses system trust;
|
|
redirects and ambient proxies are disabled. Credentials stay in Python memory
|
|
and the HTTP body/header, never in child-process arguments. Provider errors are
|
|
contained; output has fixed failure phases, not raw responses.
|
|
|
|
## Bootstrap: only three empty groups
|
|
|
|
```bash
|
|
./bootstrap-users.sh --root /absolute/private/custody
|
|
```
|
|
|
|
The default is a nonmutating preview. It names missing `net-kingdom-users`,
|
|
`net-kingdom-admins` and `activity-core-operators` groups, with a
|
|
`snapshot_sha256`. After reviewing that exact operation under the existing live
|
|
approval, apply using the preview digest:
|
|
|
|
```bash
|
|
./bootstrap-users.sh --root /absolute/private/custody \
|
|
--apply --expected-digest REVIEWED_PREVIEW_SHA256
|
|
```
|
|
|
|
Existing groups, IDs, users and memberships must survive unchanged. This command
|
|
only creates missing groups without members. It cannot grant platform-admin
|
|
membership, reset a password or reconstruct missing users after data loss.
|
|
|
|
## App-scoped membership
|
|
|
|
Ordinary account onboarding stays in User Engine / identity-provisioner. To
|
|
preview an exact service-group change for an existing directory identity:
|
|
|
|
```bash
|
|
./manage-group-members.sh add --user agent-codex --group activity-core-operators \
|
|
--root /absolute/private/custody
|
|
./manage-group-members.sh remove --user agent-codex --group activity-core-operators \
|
|
--root /absolute/private/custody
|
|
./manage-group-members.sh list --group activity-core-operators \
|
|
--root /absolute/private/custody
|
|
```
|
|
|
|
Add/remove require `--apply --expected-digest REVIEWED_PREVIEW_SHA256` to mutate.
|
|
List is read-only and emits only member identifiers. Group and user must already
|
|
exist; this path never creates either. Tenant, platform (`net-kingdom-`),
|
|
suspension (`netkingdom-`) and LLDAP administrative groups are refused by the
|
|
existing service-group validator before login. Use their respective owners for
|
|
those operations; the old unrestricted membership helper is not a fallback.
|
|
|
|
## Sign-in diagnosis: one uid's groups
|
|
|
|
To see which directory groups a login will carry (NK-WP-0051,
|
|
[`docs/sign-in-diagnosis.md`](../../../docs/sign-in-diagnosis.md)):
|
|
|
|
```bash
|
|
python3 lldap_admin.py groups --user tegwick \
|
|
--root /absolute/private/custody
|
|
```
|
|
|
|
It is read-only and has no `--apply`. It prints `uid` and `groups` (sorted
|
|
display names, never DNs, mail or credentials). An unknown uid is refused, not
|
|
reported as an empty list. Matching the names to an OpenBao role stays a
|
|
metadata read in ops-mason.
|
|
|
|
## Preservation, interruption and recovery
|
|
|
|
The digest binds origin, operation, target and the observed directory users,
|
|
groups, IDs and memberships. Apply re-reads before its first write and refuses
|
|
stale state. Final readback must equal precisely the intended change, including
|
|
unchanged unrelated memberships. An already-satisfied change is a verified no-op.
|
|
|
|
LLDAP does not provide a cross-query compare-and-swap transaction here. The owner
|
|
must hold an exclusive maintenance window across preview/apply; two reads are
|
|
not a lock. A failure during multiple group creations can leave partial state.
|
|
There is no blind rollback or retry and no group deletion. On failure retain the
|
|
non-secret receipt, inspect current state and reconcile under the owner before a
|
|
new reviewed preview. Do not infer failure means nothing changed.
|
|
|
|
## T07 is a structural diagnostic
|
|
|
|
```bash
|
|
../verify-t07.sh --root /absolute/private/custody
|
|
```
|
|
|
|
This checks actual directory structure and reports missing baseline groups,
|
|
user count and break-glass account/group presence without declaring them usable.
|
|
It does not read KeyCape's configuration Secret, execute inside a pod, infer OIDC
|
|
claims or treat a reachable portal as user acceptance. Even complete structural
|
|
observations return **2**, with `native_acceptance: unverified`. Invalid input,
|
|
authentication/transport failure or malformed responses return **1**. Both mean
|
|
the old T07 completion gate is unsatisfied. Do not use this diagnostic alone to
|
|
close user lifecycle, MFA, session revocation, client access or recovery tasks.
|
|
|
|
Use current owner-specific issuer/consumer checks and actual authorized human
|
|
lifecycle/login/recovery receipts for those properties. The
|
|
[legacy lifecycle retirement notice](../../../docs/legacy-lldap-lifecycle-retirement.md)
|
|
and [verification audit](../../../docs/verification-audit-2026-09-05.md) describe
|
|
these limits. `openbao-client-config.py` remains the specific OpenBao client
|
|
configuration checker; a configuration check is not actual client login.
|
|
|
|
## Verification and live limits
|
|
|
|
Regression tests cover private input, refusals, previews, state drift,
|
|
preservation and malformed/failed readbacks. `tests/lldap_groups_native_fixture.py`
|
|
exercises the actual adapter against an immutable existing local LLDAP image,
|
|
with synthetic credentials, loopback-only publication and disposable storage.
|
|
It removes its own fixture container and private files. The pinned native test
|
|
image and results are in Custodian's `lldap-group-maintenance.json` receipt.
|
|
|
|
Those receipts do not prove the currently deployed directory version, current
|
|
credential validity, attended authority, independent recovery or live enforcement
|
|
of earlier identity-provisioner source fixes. NK-WP-0049-T03 retains native owner
|
|
acceptance, and T02 retains incident-cutover/full caller-inventory work.
|