Initial commit (history squashed)
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Build Test Deploy / authority-container (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / docs (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / deploy (push) Has been cancelled
				
			
		
			
				
	
				Build Test Deploy / build-test (push) Has been cancelled
				
			
		
			
				
	
				Docs CI / lint-and-preview (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Build Test Deploy / authority-container (push) Has been cancelled
				
			Build Test Deploy / docs (push) Has been cancelled
				
			Build Test Deploy / deploy (push) Has been cancelled
				
			Build Test Deploy / build-test (push) Has been cancelled
				
			Docs CI / lint-and-preview (push) Has been cancelled
				
			This commit is contained in:
		
							
								
								
									
										136
									
								
								docs/rfcs/authority-plugin-ldap.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								docs/rfcs/authority-plugin-ldap.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| # RFC: StellaOps.Authority.Plugin.Ldap | ||||
|  | ||||
| **Status:** Draft – for review by Auth Guild, Security Guild, DevEx (2025-10-10)   | ||||
| **Authors:** Plugin Team 4 (Auth Libraries & Identity Providers)   | ||||
| **Related initiatives:** PLG7 backlog, CORE5 event handlers, DOC4 developer guide | ||||
|  | ||||
| ## 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** (`IClientProvisioningStore`): maintains machine/service principals either in Mongo (metadata) or via LDAP `serviceConnectionPoint` entries based on configuration. | ||||
| 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 | ||||
|   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 | ||||
|   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" | ||||
|   extraAttributes: | ||||
|     displayName: "displayName" | ||||
|     email: "mail" | ||||
| clientProvisioning: | ||||
|   enabled: false | ||||
|   containerDn: "ou=service,dc=example,dc=internal" | ||||
|   secretAttribute: "userPassword" | ||||
| health: | ||||
|   probeIntervalSeconds: 60 | ||||
|   timeoutSeconds: 5 | ||||
| ``` | ||||
|  | ||||
| ## 7. Capability Mapping | ||||
| | Capability | Implementation Notes | | ||||
| |------------|---------------------| | ||||
| | `password` | Bind-as-user validation with Authority lockout integration. Mandatory. | | ||||
| | `clientProvisioning` | Optional; when enabled, creates/updates LDAP entries for machine clients or stores metadata in Mongo if directory writes are disabled. | | ||||
| | `bootstrap` | Exposed only when bootstrap manifest provides service account credentials AND directory write permissions are confirmed during startup. | | ||||
| | `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. | ||||
| - **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 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 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 and `/internal/users` bootstrap with LDAP-backed store. | ||||
|  | ||||
| ## 11. Implementation Plan | ||||
| 1. Scaffold `StellaOps.Authority.Plugin.Ldap` project + tests (net10.0, `<IsAuthorityPlugin>` true). | ||||
| 2. Implement configuration options + validation (mirroring Standard plugin guardrails). | ||||
| 3. Build connection factory + credential store with bind logic. | ||||
| 4. Implement claims enricher and optional cache layer. | ||||
| 5. Add client provisioning store (optional) with toggles for read-only deployments. | ||||
| 6. Wire bootstrapper to validate connectivity/permissions and record findings in startup logs. | ||||
| 7. Extend developer guide with LDAP specifics (post-RFC acceptance). | ||||
| 8. Update Docs and TODO trackers; produce release notes entry once merged. | ||||
|  | ||||
| ## 12. Open Questions | ||||
| - Should client provisioning default to storing metadata in Mongo even when LDAP writes succeed (to preserve audit history)? | ||||
| - Do we require LDAPS mutual TLS support (client certificates) for regulated environments? If yes, need to extend configuration schema. | ||||
| - How will we map LDAP groups to Authority scopes/roles when names differ significantly? Consider supporting regex or mapping 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:** _TBD_ | ||||
| - **Security Guild Representative:** _TBD_ | ||||
| - **DevEx Docs:** _TBD_ | ||||
|  | ||||
| --- | ||||
| Please add comments inline or via PR review. Once approved, track execution under PLG7. | ||||
		Reference in New Issue
	
	Block a user