- docs/tutorials: template, OpenBao and SSH tutorials (unexercised) - tools/tutorial-verify + make tutorials-verify (NK-WP-0009-T06) - ADR-0009 proposed: expanded-mode Keycloak trigger and topology Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 295952@bnt-lap001 Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477
5.7 KiB
| id | type | title | status | owner | revision | proposed | last_reviewed | review_interval |
|---|---|---|---|---|---|---|---|---|
| NK-ADR-0009 | architecture-decision-record | Expanded-Mode Keycloak: Adoption Trigger and Federation Topology | proposed | net-kingdom | 1 | 2026-09-28 | 2026-09-28 | 12m |
ADR-0009 - Expanded-Mode Keycloak: Adoption Trigger and Federation Topology
Status: Proposed (awaiting Bernd's decision; NK-WP-0011-T01) Date: 2026-09-28 Deciders: Bernd Worsch, Claude
Context
The live IAM Profile issuer is KeyCape (Authelia + LLDAP + privacyIDEA) at
https://kc.coulomb.social. No Keycloak Deployment exists. The architecture
doc says expanded mode is capability-driven, chiefly inbound enterprise
federation (Entra ID, AD, SAML), and leaves the per-tenant trigger and
dual-issuer rule open. ADR-0014 defines the IAM capability role as a tenant
operating its own dedicated IAM instance. ADR-0006 keeps flex-auth as the PDP.
ADR-0016 makes MFA a user preference with workload opt-in step-up. The user-engine
extension-point doc requires immutable provider subjects, tenant-scoped group
mappings, and no privilege from unmapped upstream claims. The estate is one
node with single-instance databases, so no option here buys HA.
NK-WP-0001 decision D2 (Keycloak as primary internal user store) predates all of this.
Decision (proposed)
- Trigger. A tenant moves to expanded mode only when all three hold: a named tenant, a named owner of the upstream IdP, and a concrete federation requirement the lightweight stack cannot meet. User count, or the wish to have Keycloak, is not a trigger. Until then NK-WP-0011 implementation tasks stay unstarted.
- Role: OIDC identity broker only. Keycloak brokers upstream Entra ID (OIDC), AD (LDAP federation) and generic SAML 2.0 IdPs (SAML as an upstream, via Keycloak's identity-provider brokering). Downstream it issues only IAM Profile OIDC/PKCE tokens. Emitting SAML assertions to downstream applications is out of scope. Keycloak is not the primary user store (supersedes D2), not the PDP, and not a secret store.
- Isolation. Realms are the tenant boundary. The
tenant:platformrealm is separate and reserved. A tenant without theIAMrole gets a realm in the shared broker; a tenant with theIAMrole gets a dedicated broker instance (ADR-0014). A shared broker gives realm-level, not process-level, isolation, so it is acceptable only for tenants whose federation trust and data classification allow that. Realm admins may not alter IAM Profile semantics, the platform realm, federation trust settings, or audit retention. tenant-engine, not Keycloak, is the source of truth for tenant existence and capability roles. - Coexistence. Each tenant has exactly one active issuer at a time, recorded by tenant-engine and published as issuer configuration. KeyCape stays unchanged and keeps serving lightweight tenants. Applications target the IAM Profile and discover the issuer; they do not embed provider choice. Account bindings remain keyed on issuer plus immutable subject, so moving a tenant between issuers is a planned migration with an explicit subject-mapping step, never a silent dual login.
- Assurance. Federated login is AAL1 by default. Upstream MFA (for example Entra Conditional Access) counts as higher assurance only through a reviewed, per-IdP trust mapping written down before use. Privileged step-up continues to use privacyIDEA under ADR-0016 and the proposed IAM v0.4 step-up. The privacyIDEA provider JAR is added only if a tenant needs Keycloak-side MFA.
- Provisioning and mapping. Follow the user-engine extension-point rules: JIT creates a pending projection; group mappings are tenant-scoped, versioned and deny ambiguous envelopes; no platform role from raw upstream group names.
- Hostname and issuer. Not chosen here. It is assigned by the package
owner under ADR-0015 with the first tenant, as a distinct host from
kc.coulomb.social; the issuer URL is then treated as immutable. - Ownership. net-kingdom owns this contract and conformance checks. Packaging and runtime belong to the ADR-0015 owner; the database consumer to railiance-platform; OpenBao roles to railiance-platform.
Alternatives considered
- Keycloak replaces KeyCape. Rejected: unnecessary migration risk for working tenants; contradicts capability-driven adoption.
- Dedicated instance for every federated tenant. Strongest isolation, but on
one node the cost is multiplied database, memory and patching for tenants
that do not need it. Kept for
IAM-role tenants. - Realm-per-tenant only, including
IAMtenants. Contradicts ADR-0014's meaning ofIAM. - Keycloak as SAML IdP downstream too. No consumer requires it; widens the contract surface. Revisit on a named need.
- Trust upstream MFA claims by default. Rejected: assurance would depend on each customer's tenant configuration.
- Build ahead of demand. Rejected: adds an issuer, a database and a break-glass path with no owner or user.
Consequences
- NK-WP-0011 T02 onward stay gated on the trigger in decision 1.
- T06 conformance uses
tools/iam-profile-conformance/against IAM v0.3. - tenant-engine needs an issuer-per-tenant field; this is a dependency to raise with its owner, not something built here.
- Shared-broker realms cannot be described as strongly isolated tenants.
- No HA is implied; T02 and T08 must name backup ownership, off-host custody and an isolated restore proof.
Open questions
- Which named tenant and IdP owner will trigger the first deployment?
- Does key-cape require changes for issuer-per-tenant discovery (EP-NK-001)?
- Which claim carries the federated-assurance mapping so flex-auth can consume it?