audit work, fixed StellaOps.sln warnings/errors, fixed tests, sprints work, new advisories
This commit is contained in:
165
docs/technical/adr/authority-plugin-ldap.md
Normal file
165
docs/technical/adr/authority-plugin-ldap.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# RFC: StellaOps.Authority.Plugin.Ldap
|
||||
|
||||
**Status:** Accepted – Auth Guild, Security Guild, DevEx Docs sign-off (2025-11-03)
|
||||
**Authors:** Plugin Team 4 (Auth Libraries & Identity Providers)
|
||||
**Related initiatives:** PLG7 backlog, CORE5 event handlers, DOC4 developer guide
|
||||
|
||||
> Review log captured in `docs/notes/2025-11-03-authority-plugin-ldap-review.md`. Decisions below reflect consensus from Auth Guild, Security Guild, and DevEx Docs walkthroughs held on 2025-11-03.
|
||||
|
||||
## 0. Review Summary (2025-11-03)
|
||||
|
||||
- Confirmed plugin charter: deliver offline-friendly LDAP credential verification with deterministic caching and audit parity with the Standard plugin.
|
||||
- Resolved open questions:
|
||||
- **Client provisioning audit trail:** plugin always mirrors credential lifecycle metadata (actor, timestamp, hashed secret reference) into Authority’s Mongo store even when LDAP writes succeed; directory writes remain optional and secrets never persist locally.
|
||||
- **Mutual TLS support:** LDAPS client certificates are required for regulated installations; configuration gains optional client certificate bindings with secret-provider integration and deterministic trust-store selection.
|
||||
- **Group-to-role mapping:** mappings accept static DN dictionaries and deterministic `regex` matchers; regex captures project to canonical roles via substitution (documented contract for policy automation).
|
||||
- Follow-up implementation issues filed in `StellaOps.Authority.Plugin.Standard/TASKS.md` (see Section 11) to track scaffolding, mutual TLS enablement, audit mirror, and mapping enhancements.
|
||||
|
||||
## 1. Problem Statement
|
||||
Many on-prem StellaOps deployments rely on existing LDAP/Active Directory domains for workforce identity. The current Standard Mongo-backed plugin requires duplicating users and secrets, which increases operational overhead and violates corporate policy in some regulated environments. We need a sovereign, offline-friendly LDAP plugin that:
|
||||
|
||||
- Supports password grant and bootstrap provisioning flows without storing credentials in Mongo.
|
||||
- Enforces StellaOps security policies (lockout, password policy hints, audit logging) while delegating credential validation to LDAP.
|
||||
- Operates deterministically in offline or partially connected environments by caching directory metadata when necessary.
|
||||
|
||||
## 2. Goals
|
||||
- Provide a first-party `StellaOps.Authority.Plugin.Ldap` plugin advertising `password` and optional `clientProvisioning` capabilities at launch.
|
||||
- Support username/password authentication against LDAP bind operations with configurable DN templates.
|
||||
- Allow optional bootstrap seeding of service accounts by writing into LDAP (guarded behind explicit configuration) or by mapping to pre-existing entries.
|
||||
- Surface directory-derived claims (groups, attributes) for downstream authorization via `IClaimsEnricher`.
|
||||
- Integrate with Authority lockout telemetry and structured logging without persisting secrets locally.
|
||||
|
||||
## 3. Non-Goals
|
||||
- Implement multi-factor authentication out of the box (future enhancement once TOTP/WebAuthn strategy is finalised).
|
||||
- Provide write-heavy directory management (e.g., user creation workflows) beyond optional bootstrap service account seeding.
|
||||
- Replace the Standard plugin; both must remain supported and selectable per environment.
|
||||
|
||||
## 4. Key Constraints & Assumptions
|
||||
- Offline-first posture: deployments may operate without outbound internet and with intermittent directory connectivity (e.g., read-only replicas). The plugin must tolerate transient LDAP connectivity failures and degrade gracefully.
|
||||
- Deterministic behaviour: identical configuration and directory state must yield identical token issuance results. Cached metadata (e.g., group lookups) must have defined expiration.
|
||||
- Security: No plaintext credential storage; TLS must be enforced for LDAP connections unless explicitly overridden for air-gapped lab environments.
|
||||
|
||||
## 5. High-Level Architecture
|
||||
1. **Configuration binding** (`ldap.yaml`): defines server endpoints, bind strategy, claim mapping, and optional bootstrap overrides.
|
||||
2. **Connection factory**: pooled LDAP connections using a resilient client (preferred dependency: `Novell.Directory.Ldap.NETStandard`).
|
||||
3. **Credential validator** (`IUserCredentialStore`): performs bind-as-user flow with optional fallback bind using service account when directories disallow anonymous search.
|
||||
4. **Claims enricher** (`IClaimsEnricher`): queries group membership/attributes and projects them into canonical roles/claims.
|
||||
5. **Optional client provisioning / bootstrap** (`IClientProvisioningStore` + `IUserCredentialStore.UpsertUserAsync`): maintains machine/service principals either in Mongo (metadata) or via LDAP entries based on configuration. Capabilities are only advertised when the manifest requests them, configuration enables them, **and** the plug-in proves the bind identity can add/delete entries beneath the configured containers; otherwise the feature is automatically downgraded so read-only deployments remain safe.
|
||||
6. **Health checks**: periodic LDAP `whoami` or `search` probes surfaced through `AuthorityPluginHealthResult`.
|
||||
|
||||
```
|
||||
Authority Host
|
||||
├── Plugin Manifest (ldap)
|
||||
├── Registrar → registers ConnectionFactory, LdapCredentialStore, LdapClaimsEnricher
|
||||
├── Password Grant Handler → CredentialStore.VerifyPasswordAsync → LDAP Bind
|
||||
└── Claims Pipeline → ClaimsEnricher.EnrichAsync → LDAP group lookup
|
||||
```
|
||||
|
||||
## 6. Configuration Schema (Draft)
|
||||
```yaml
|
||||
connection:
|
||||
host: "ldaps://ldap.example.internal"
|
||||
port: 636
|
||||
useStartTls: false
|
||||
validateCertificates: true
|
||||
clientCertificate:
|
||||
pfxPath: "file:/etc/stellaops/certs/ldap-client.pfx"
|
||||
passwordSecret: "file:/etc/stellaops/secrets/ldap-client-pfx.txt"
|
||||
sendChain: true
|
||||
trustStore:
|
||||
mode: "system" # system | bundle
|
||||
bundlePath: "file:/etc/stellaops/trust/ldap-root.pem"
|
||||
bindDn: "cn=stellaops-bind,ou=service,dc=example,dc=internal"
|
||||
bindPasswordSecret: "file:/etc/stellaops/secrets/ldap-bind.txt"
|
||||
searchBase: "dc=example,dc=internal"
|
||||
usernameAttribute: "uid"
|
||||
userDnFormat: "uid={username},ou=people,dc=example,dc=internal" # optional template
|
||||
security:
|
||||
requireTls: true
|
||||
allowInsecureWithEnvToggle: false # requires STELLAOPS_LDAP_ALLOW_INSECURE=true
|
||||
allowedCipherSuites: [] # optional allow-list
|
||||
referralChasing: false
|
||||
lockout:
|
||||
useAuthorityPolicies: true # reuse Authority lockout counters
|
||||
directoryLockoutAttribute: "pwdAccountLockedTime"
|
||||
claims:
|
||||
groupAttribute: "memberOf"
|
||||
groupToRoleMap:
|
||||
"cn=stellaops-admins,ou=groups,dc=example,dc=internal": "operators"
|
||||
"cn=stellaops-read,ou=groups,dc=example,dc=internal": "auditors"
|
||||
regexMappings:
|
||||
- pattern: "^cn=stellaops-(?P<role>[a-z-]+),ou=groups,dc=example,dc=internal$"
|
||||
roleFormat: "{role}" # yields operators/investigate/etc.
|
||||
extraAttributes:
|
||||
displayName: "displayName"
|
||||
email: "mail"
|
||||
clientProvisioning:
|
||||
enabled: false
|
||||
containerDn: "ou=service,dc=example,dc=internal"
|
||||
secretAttribute: "userPassword"
|
||||
auditMirror:
|
||||
enabled: true
|
||||
collectionName: "ldap_client_provisioning"
|
||||
health:
|
||||
probeIntervalSeconds: 60
|
||||
timeoutSeconds: 5
|
||||
```
|
||||
|
||||
## 7. Capability Mapping
|
||||
| Capability | Implementation Notes |
|
||||
|------------|---------------------|
|
||||
| `password` | Bind-as-user validation with Authority lockout integration. Mandatory. Requires TLS and optionally client certificate binding per regulated install posture. |
|
||||
| `clientProvisioning` | Optional; when enabled, creates/updates LDAP entries for machine clients **and** mirrors lifecycle metadata into Mongo for audit parity. |
|
||||
| `bootstrap` | Exposed only when bootstrap manifest provides service account credentials AND directory write permissions are confirmed during startup; always records audit mirror entries. |
|
||||
| `mfa` | Not supported in MVP. Future iteration may integrate TOTP attributes or external MFA providers. |
|
||||
|
||||
## 8. Operational Considerations
|
||||
- **Offline cache:** provide optional Mongo cache for group membership to keep `/ready` responsive if LDAP is temporarily unreachable. Cache entries must include TTL and invalidation hooks.
|
||||
- **Secrets management:** accept `file:` and environment variable references; integrate with existing `StellaOps.Configuration` secret providers.
|
||||
- **Mutual TLS & trust anchors:** support client certificate authentication with deterministic trust-store selection (`system` vs bundled file) to satisfy regulated deployments; surface validation outcomes via health endpoints.
|
||||
- **Audit mirror:** write deterministic Mongo records capturing provisioning operations (actor, LDAP DN, operation type, hashed secret reference) to align with Authority audit policy even when LDAP is authoritative.
|
||||
- **Observability:** emit structured logs with event IDs (`LDAP_BIND_START`, `LDAP_BIND_FAILURE`, `LDAP_GROUP_LOOKUP`), counters for success/failure, and latency histograms.
|
||||
- **Throttling:** reuse Authority rate-limiting middleware; add per-connection throttles to avoid saturating directory servers during brute-force attacks.
|
||||
|
||||
## 9. Security & Compliance
|
||||
- Enforce TLS (`ldaps://` or STARTTLS) by default. Provide explicit `allowInsecure` flag gated behind environment variable for lab/testing only.
|
||||
- Support optional mutual TLS (client cert authentication) with secret-backed PFX loader and deterministic trust bundle selection.
|
||||
- Support password hash migration by detecting directory lockout attributes and surfacing `RequiresPasswordReset` when policies demand changes.
|
||||
- Log distinguished names only at `Debug` level to avoid leaking sensitive structure in default logs.
|
||||
- Coordinate with Security Guild for penetration testing before GA; incorporate audit log entries for bind attempts and provisioning changes.
|
||||
|
||||
## 10. Testing Strategy
|
||||
- **Unit tests:** mock LDAP connections to validate DN formatting, error mapping, and capability negotiation.
|
||||
- **Integration tests:** run against an ephemeral OpenLDAP container (seeded via LDIF fixtures) within CI. Include mutual TLS handshake verification (valid/expired certs) and offline cache regression (disconnect LDAP mid-test).
|
||||
- **Determinism tests:** feed identical LDIF snapshots and configuration to ensure output tokens/claims remain stable across runs.
|
||||
- **Smoke tests:** `dotnet test` harness plus manual `dotnet run` scenario verifying `/token` password grants, `/internal/users` bootstrap with LDAP-backed store, and Mongo audit mirror entries.
|
||||
|
||||
## 11. Implementation Plan
|
||||
1. Scaffold `StellaOps.Authority.Plugin.Ldap` project + companion test project (net10.0, `<IsAuthorityPlugin>` true).
|
||||
2. Implement configuration binding/validation, including secret-backed client certificate + trust-store options and `allowInsecureWithEnvToggle`.
|
||||
3. Build connection factory + credential store with bind logic, TLS enforcement, and deterministic retry policies.
|
||||
4. Implement claims enricher with regex mapping support and optional Mongo-backed cache layer.
|
||||
5. Add client provisioning store with LDAP write toggles and Mongo audit mirror (`ldap_client_provisioning` collection).
|
||||
6. Wire health checks, telemetry, and structured audit events (bind attempts, provisioning, cache fallbacks).
|
||||
7. Deliver bootstrap validation that inspects directory permissions and logs deterministic capability summary.
|
||||
8. Extend developer guide and samples with LDAP configuration guidance; include mutual TLS and regex mapping examples.
|
||||
9. Update docs/TASKS trackers and release notes entry; ensure CI coverage (unit, integration with OpenLDAP, determinism, smoke tests).
|
||||
|
||||
## 12. Resolved Questions
|
||||
- **Audit mirror:** Client provisioning always persists lifecycle metadata in Mongo for audit parity; LDAP remains the credential source of truth.
|
||||
- **Mutual TLS:** Plugin must support optional client certificate authentication with secret-backed key material and deterministic trust-store selection.
|
||||
- **Group mapping:** Provide deterministic regex mapping support to translate directory DNs into Authority roles/scopes without custom scripts.
|
||||
|
||||
## 13. Timeline (Tentative)
|
||||
- **Week 1:** RFC review & sign-off.
|
||||
- **Week 2-3:** Implementation & unit tests.
|
||||
- **Week 4:** Integration tests + documentation updates.
|
||||
- **Week 5:** Security review, release candidate packaging.
|
||||
|
||||
## 14. Approval
|
||||
- **Auth Guild Lead:** ✅ Approved 2025-11-03 (see review log).
|
||||
- **Security Guild Representative:** ✅ Approved 2025-11-03 (see review log).
|
||||
- **DevEx Docs:** ✅ Approved 2025-11-03 (see review log).
|
||||
|
||||
---
|
||||
Please add comments inline or via PR review. Once approved, track execution under PLG7.
|
||||
Reference in New Issue
Block a user