Open with AI
User federation: LDAP, Kerberos, and Active Directory
This document covers Issuerd’s user federation: connecting a realm to an external user directory (LDAP — OpenLDAP, Samba AD DC, Microsoft Active Directory — or a Kerberos KDC) so that users authenticate against the directory while Issuerd issues the tokens. It describes the federation model, every supported configuration key, user synchronization, group-mapping semantics, SPNEGO/Kerberos login, and day-to-day operation of a federated realm. It is written for system operators and administrators. For declaring federation providers in a seed file, see also provisioning.md; for the Admin API access model and admin tokens, see administration.md. (External identity brokering to OIDC/social IdPs is a different feature — see identity-brokering.md.)
Contents
Section titled “Contents”- How federation works
- Supported directories
- Configuring an LDAP provider
- User synchronization
- Group mapping semantics
- Kerberos and SPNEGO
- Active Directory specifics
- Operating a federated realm
- Test rigs reference
How federation works
Section titled “How federation works”A federation provider is a per-realm identity-provider record whose provider_id is ldap or kerberos, plus a string→string config map holding the connection settings. Records are stored with the realm (same storage as everything else) and loaded on demand by the federation manager (crates/issuerd-federation/src/manager.rs): disabled records are skipped, and providers are tried in descending priority order (default 0). Provider configuration is re-read from storage on every use — changes made through the Admin API or console take effect immediately, with no restart and no caching layer in between.
Federated users and federation_link
Section titled “Federated users and federation_link”A federated user is a normal local user row whose federation_link field holds the id of the provider it came from. Federated users appear in the admin console and Admin API like any other user and can hold local group memberships, role mappings, required actions, and (if you choose) local credentials.
Password validation via LDAP bind
Section titled “Password validation via LDAP bind”When a federated user logs in through the browser login form, Issuerd validates the password against the directory, not against a local hash (crates/issuerd-auth-flow/src/built_in.rs, UsernamePasswordAuthenticator):
- Local user exists and has a
federation_link— the password is checked with the linked provider. The LDAP provider builds the user DN as{rdnLdapAttribute}={username},{usersDn}(the username is RFC 4514-escaped,crates/issuerd-federation/src/ldap/provider.rs) and performs a simple bind with the supplied password. A rejected bind fails the login with the same generic “invalid credentials” error as a bad local password (and counts toward brute-force lockout). If the provider errors (directory down, misconfiguration) or no provider matches the link, Issuerd falls back to the user’s local Argon2 credential, so a local break-glass password keeps working during a directory outage. - No local user exists — Issuerd looks the username up across the realm’s providers in priority order. On the first provider that finds the user and accepts the bind, the user is imported on first login: a local row is created with the federation link and — when a group mapper is configured — the
memberOfattribute and reconciled group memberships. OnlymemberOf-derived group information is mapped from the directory; email and name attributes are not (see the warning under What a full sync does).
To avoid a timing side-channel that would reveal whether a username exists, the LDAP provider performs a bind attempt even when its search found no such user.
The token endpoint’s Resource Owner Password Credentials grant (grant_type=password) applies the same rules: a federated user is validated against the linked directory first — an active rejection fails the grant (no local fallback), while a provider error or a dead link falls back to the local Argon2 credential, mirroring the browser flow.
Full synchronization vs. first-login import
Section titled “Full synchronization vs. first-login import”You do not have to choose one model:
- First-login import (always on) creates local rows lazily, the first time each directory user logs in.
- Full synchronization (operator-triggered, see User synchronization) bulk-imports every user the directory search returns, so users exist locally before their first login — useful for assigning groups/roles ahead of time and for searching users in the console.
Both paths create the same kind of local row and reconcile group memberships the same way.
Resilience behavior
Section titled “Resilience behavior”- Each LDAP provider instance holds a pool of 5 LDAP connections (
crates/issuerd-federation/src/ldap/pool.rs); the pool connects eagerly when the provider is instantiated, so a completely unreachable directory surfaces as a provider-load error rather than a per-request hang. - During cross-provider lookups a provider that errors is skipped with a warning and the next provider is tried (
DynamicFederationManager::find_user).
Supported directories
Section titled “Supported directories”| Directory | provider_id |
vendor |
Status |
|---|---|---|---|
| Samba AD DC | ldap |
SAMBA (or ACTIVE_DIRECTORY) |
Primary test target (tests/integration/federation_samba.rs) |
| OpenLDAP (generic RFC 4519 schema) | ldap |
GENERIC |
Tested (tests/integration/federation_ldap.rs) |
| Microsoft Active Directory | ldap |
ACTIVE_DIRECTORY |
Tested against an AD DS lab (tests/integration/federation_ad.rs) |
| Kerberos KDC (SPNEGO) | kerberos |
— | Tested against the Samba DC KDC |
The vendor key selects directory-flavor defaults (username/UUID attribute names) and, for ACTIVE_DIRECTORY, the password-write encoding (unicodePwd). SAMBA currently shares the ACTIVE_DIRECTORY attribute defaults.
Warning: Kerberos/SPNEGO is Unix-only. The SPNEGO acceptor is gated on
cfg(unix)(crates/issuerd-federation/src/kerberos/provider.rs); on a Windows build the Kerberos provider returnsNotSupportedfor SPNEGO. LDAP federation works on all platforms.
Configuring an LDAP provider
Section titled “Configuring an LDAP provider”Three equivalent surfaces write the same per-realm identity-provider record:
- Admin console —
/admin/console→ Identity Providers → add a provider with type LDAP. The form (connection + mapping sections) is driven by the server’s enum endpoints, and the vendor selector swaps in the matching attribute presets. - Admin API —
POST /admin/realms/{realm}/identity-provider/instances(requiresmanage-realm). - Provision file — the
identity_providers:list (see below andexamples/provision.federation.yaml).
Configuration key reference
Section titled “Configuration key reference”All keys live in the provider’s config map and are strings (quote numbers and booleans: "true", "5000"). Spellings follow Keycloak’s camelCase convention; the parser is LdapConfig::from_hashmap (crates/issuerd-federation/src/ldap/config.rs) — except priority, which DynamicFederationManager reads directly from the same config map.
| Key | Required | Default | Meaning |
|---|---|---|---|
connectionUrl |
yes | — | ldap://host:389 or ldaps://host:636. |
bindDn |
"" |
Service-account DN used for searches. Passed verbatim to the LDAP simple bind. | |
bindCredential |
"" |
Service-account password. Stored as part of the provider configuration — protect storage and the Admin API accordingly. | |
usersDn |
"" |
Search base for all user lookups and syncs. | |
baseDn |
"" |
Parsed for Keycloak parity; not currently used — searches are rooted at usersDn. |
|
usernameLdapAttribute |
sAMAccountName (AD/Samba), uid (GENERIC) |
Attribute matched against the login username. | |
rdnLdapAttribute |
same as usernameLdapAttribute |
Attribute used to build the user DN for bind/password operations. | |
uuidLdapAttribute |
objectGUID (AD/Samba), entryUUID (GENERIC) |
Directory’s stable entry identifier. | |
userObjectClasses |
inetOrgPerson,organizationalPerson |
Comma-separated object classes; all are ANDed into user search filters — except the username lookup filter drops them when customUserSearchFilter is set. |
|
customUserSearchFilter |
— | Extra RFC 4515 filter ANDed into single-user lookups (e.g. (employeeType=internal)). |
|
searchScope |
SUBTREE |
SUBTREE, ONELEVEL, or BASE. |
|
useStartTls |
"false" |
"true" issues StartTLS after connecting (for plain ldap:// URLs). |
|
noTlsVerify |
"false" |
"true" disables TLS certificate verification on ldaps:///StartTLS connections. Insecure — dev/lab only (self-signed directory certs). |
|
pagination |
"false" |
Parsed for parity. Note: username lookups and syncs always use RFC 2696 paged results with batchSize as the page size, regardless of this flag. |
|
batchSize |
1000 |
Page size for paged searches. Keep it at or below the server’s limit (AD’s MaxPageSize defaults to 1000). |
|
maxConditions |
1000 |
Parsed for Keycloak parity; not currently used. | |
vendor |
GENERIC |
GENERIC, ACTIVE_DIRECTORY, or SAMBA — see Supported directories. |
|
editMode |
READONLY |
READONLY, WRITABLE, or UNSYNCED. Currently gates directory password writes at the provider level (READONLY rejects them); see Operating a federated realm. |
|
priority |
0 |
Integer; providers are consulted highest-priority first during user lookup. |
Group-mapping keys (enable the group-ldap-mapper; parsed by GroupMapper::from_config in crates/issuerd-federation/src/mapper/group.rs — see Group mapping semantics):
| Key | Required | Default | Meaning |
|---|---|---|---|
groupsDn |
to enable | — | Base DN of the groups subtree. The mapper is active only when this key is present and non-empty. |
groupNameLdapAttribute |
cn |
Group name attribute. With the implemented memberOf strategy the group name is always the first RDN value of the memberOf DN; this key only matters for the not-yet-implemented LoadGroupsByMemberAttribute strategy. |
|
memberOfLdapAttribute |
memberOf |
User attribute holding membership DNs. The provider explicitly requests it in searches (some directories do not return operational attributes for "*"). |
|
groupsInclude |
— | Comma-separated allowlist of group names (CNs), matched case-insensitively. Absent/empty means “every group under groupsDn”. |
Admin API example
Section titled “Admin API example”# TOKEN: an admin access token — see administration.md, "Getting an admin token"curl -s -X POST "http://localhost:8080/admin/realms/acme/identity-provider/instances" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "alias": "corp-ldap", "provider_id": "ldap", "enabled": true, "config": { "connectionUrl": "ldaps://dc1.corp.example:636", "bindDn": "CN=issuerd-bind,OU=Service Accounts,DC=corp,DC=example", "bindCredential": "s3cret", "usersDn": "OU=People,DC=corp,DC=example", "usernameLdapAttribute": "sAMAccountName", "rdnLdapAttribute": "cn", "uuidLdapAttribute": "objectGUID", "userObjectClasses": "person,organizationalPerson,user", "vendor": "ACTIVE_DIRECTORY", "searchScope": "SUBTREE", "editMode": "READONLY", "batchSize": "1000", "priority": "1", "groupsDn": "OU=Groups,DC=corp,DC=example", "groupsInclude": "developers,ops" } }'The record can then be read/updated/replaced via GET|PUT /admin/realms/{realm}/identity-provider/instances/{alias} and removed with DELETE on the same path.
Provision YAML example
Section titled “Provision YAML example”Provisioned identity providers are created only if the alias does not exist yet (the provisioner never updates existing records — see provisioning.md). A complete multi-directory lab ships as examples/provision.federation.yaml:
identity_providers: - realm: samba alias: samba-ldap provider_id: ldap enabled: true config: connectionUrl: ldap://localhost:389 bindDn: CN=Administrator,CN=Users,DC=test,DC=issuerd,DC=local bindCredential: AdminPass123! usersDn: CN=Users,DC=test,DC=issuerd,DC=local usernameLdapAttribute: sAMAccountName rdnLdapAttribute: cn uuidLdapAttribute: objectGUID userObjectClasses: person,organizationalPerson,user vendor: ACTIVE_DIRECTORY searchScope: SUBTREE editMode: WRITABLE batchSize: "5000" priority: "1"User synchronization
Section titled “User synchronization”Triggering a sync
Section titled “Triggering a sync”- Admin console — Identity Providers → {alias} → Sync Users button; the result (
added/updated/removed/failed) is shown when the run finishes. - Admin API —
POST /admin/realms/{realm}/user-federation/{provider_id}/sync, where{provider_id}is the provider’s id or alias. Requiresmanage-realmormanage-users.
curl -s -X POST \ "http://localhost:8080/admin/realms/acme/user-federation/corp-ldap/sync" \ -H "Authorization: Bearer $TOKEN"# {"added":1180,"updated":12,"removed":0,"failed":0,"last_sync":"2026-09-12T09:41:03Z"}?strategy=full (the default) runs a full sync. ?strategy=incremental resumes from the lastSyncTime watermark persisted into the provider config after every successful sync — but the LDAP provider does not implement incremental sync, so it returns 501 Not Implemented; use full syncs. Every sync run records an admin event (UserFederation resource).
What a full sync does
Section titled “What a full sync does”Implemented by UserSynchronizer (crates/issuerd-federation/src/sync.rs):
- Streams all directory users with a paged search over
usersDn, matching theuserObjectClassesfilter (page size =batchSize). - Pre-fetches all local users of the realm (pages of 1000) to avoid per-user lookups.
- Inserts new users in batches of 1000 (
Storage::bulk_create_users; on PostgreSQL this is a singleINSERT ... SELECT * FROM UNNEST(...)per batch). If a batch fails, the synchronizer falls back to per-user inserts for that batch and counts individual failures. - Updates existing users whose stored
federation_linkmatches the reporting provider: email, email-verified flag, first/last name, enabled flag, and attributes are overwritten from the directory record. An unparsable email value is dropped (stored as no email) rather than failing the user. - Reconciles group memberships for every added/updated user (see the next section) and logs a summary line (
LDAP group sync reconciled) with per-run counters.
Warning: No LDAP attribute mappers are wired in production —
build_default_mappersinstalls only the group mapper (crates/issuerd-federation/src/manager.rs), so directory email/first/last-name attributes never reach theFederatedUserrecord. Because step 4 overwrites those fields on every run, each sync clears an email address or name an administrator set locally on a federated user (the provider reports them as empty); first-login imports likewise start with these fields empty.
Conflict handling: a directory user whose username matches a local, non-federated user (or one linked to a different provider) is not touched — the run counts it in failed and logs a warning. removed is always 0: synchronization never deletes local users, even when they disappear from the directory.
What a synced user looks like
Section titled “What a synced user looks like”federation_linkset to the provider id; no local password credential (authentication happens via LDAP bind).- Username from
usernameLdapAttribute; enabled (note: directory account-state attributes such as AD’suserAccountControlare not mapped to the enabled flag — see Active Directory specifics). - A
memberOfuser attribute containing the mapped membership names when a group mapper is configured.
Group mapping semantics
Section titled “Group mapping semantics”Group sync activates when the provider config carries a non-empty groupsDn. The mapper (crates/issuerd-federation/src/mapper/group.rs) implements Keycloak’s group-ldap-mapper with the GetGroupsFromUserMemberOf strategy:
- Read the user’s
memberOfattribute (memberOfLdapAttribute). - Keep only DNs under
groupsDn. The suffix test is normalization-aware: case-insensitive, insensitive to cosmetic spacing around commas, and anchored on whole RDN components —OU=Groups2does not match baseOU=Groups. - Take the first RDN value of each surviving DN as the group name (
CN=Smith\, John,OU=Groups,...→Smith, John), reversing RFC 4514 escaping; escaped commas in group names are handled correctly. - Apply the optional
groupsIncludeallowlist (case-insensitive).
Reconciliation into local groups (issuerd_core::roles::reconcile_group_memberships_indexed) then applies these rules:
SomevsNoneis authoritative vs untouched. When a group mapper is wired,FederatedUser.groupsisSome(list)— authoritative: a user reported with an empty list is member of nothing, and stale memberships are removed. When no group mapper is wired (nogroupsDn), groups areNone— “provider does not report groups” — and reconciliation leaves every local membership untouched. RemovinggroupsDnfrom a provider therefore never wipes memberships.- The
ldap_syncmarker. Groups created by sync are top-level groups carrying the attributeldap_sync=true. A pre-existing group (e.g. provisioned) that sync assigns to a user is adopted: the marker is set on it so future runs manage its membership. - Only marker-carrying memberships are ever removed. A group membership an administrator granted manually (group without the marker) survives every sync; directory-side group removal propagates by deleting memberships in marked groups that are no longer reported.
- Group role mappings are never touched — sync manages membership only.
- Group names match case-insensitively (directories like AD treat CNs case-insensitively); a per-sync-run shared index avoids per-user lookups for the same groups, and a find-or-create race with a concurrent reconcile falls back to the existing row. Names that are not valid Issuerd group names are skipped and counted.
The same reconciliation runs for first-login imports and for full syncs, so both paths produce identical group state.
Kerberos and SPNEGO
Section titled “Kerberos and SPNEGO”A provider_id: kerberos record turns on desktop single sign-on: browsers that already hold a Kerberos ticket log in without seeing the login form.
Configuration keys
Section titled “Configuration keys”Parsed by KerberosConfig::from_hashmap (crates/issuerd-federation/src/kerberos/config.rs) — except priority, which DynamicFederationManager reads directly from the same config map:
| Key | Required | Default | Meaning |
|---|---|---|---|
kerberosRealm |
yes | — | Kerberos realm, e.g. TEST.ISSUERD.LOCAL. |
serverPrincipal |
"" |
Accept-side service principal, e.g. HTTP/[email protected]. |
|
keyTab |
"" |
Path to the keytab file, readable by the issuerd process. | |
allowKerberosAuthentication |
"true" |
Master switch for SPNEGO. When "true", the keytab must exist — otherwise provider loading fails for the whole realm (every federation lookup errors). |
|
allowPasswordAuthentication |
"false" |
Parsed for Keycloak parity. Kerberos password validation is not implemented — it returns NotSupported even when enabled. |
|
updateProfileFirstLogin |
"true" |
Parsed for Keycloak parity; reserved. | |
debug |
"false" |
Parsed for Keycloak parity; reserved. | |
priority |
0 |
Provider ordering, as with LDAP. |
Provision example (from examples/provision.federation.yaml):
identity_providers: - realm: kerberos alias: samba-kerberos provider_id: kerberos enabled: true config: kerberosRealm: TEST.ISSUERD.LOCAL keyTab: tests/fixtures/samba-dc/shared/issuerd.keytab allowKerberosAuthentication: "true"Prerequisites
Section titled “Prerequisites”- Unix build — see the warning in Supported directories.
- SPN + keytab — register the service principal
HTTP/<host>in the KDC, where<host>is the hostname users type into the browser, and export its keytab to the configured path. The Samba fixture shows the mechanics (tests/fixtures/samba-dc/entrypoint.sh):samba-tool spn add HTTP/issuerd.test.issuerd.local issuerd-spnplussamba-tool domain exportkeytab ... --principal=HTTP/.... - KDC reachability and working client-side Kerberos config on the Issuerd host (the acceptor uses the system GSSAPI/
krb5.conf), plus synchronized clocks (Kerberos tolerates only small skew) and DNS that resolves the KDC and the service hostname consistently. - Browsers on client machines must be configured to negotiate (Kerberos ticket present; the Issuerd origin in the browser’s negotiate/ trusted-zone list).
The SPNEGO endpoint
Section titled “The SPNEGO endpoint”GET /realms/{realm}/kerberos (crates/issuerd-server/src/routes/kerberos.rs) implements the Keycloak-style negotiation:
- No
Authorization: Negotiate <token>header →401withWWW-Authenticate: Negotiate(the browser answers with its ticket). - Multi-round exchanges return
401+WWW-Authenticate: Negotiate <response-token>until complete. - On success Issuerd finds or creates the local user (federation link +
KERBEROS_PRINCIPALattribute; disabled users are rejected), creates an SSO session (auth methodspnego), and redirects302to the client’sredirect_uriwith an authorizationcode.
Because this endpoint grants codes without user interaction, it validates the request like the authorization endpoint: client_id must exist and be enabled, redirect_uri must match the client’s registered URIs, every requested scope must be in the client’s default/optional scopes, public clients must send a PKCE code_challenge, and an explicit response_mode (form_post, JARM) is honored. Successful logins emit a login event with method=spnego.
SPNEGO is also wired into the built-in browser flow as an ALTERNATIVE stage (auth-spnego, right after the cookie stage, crates/issuerd-core/src/flows.rs): the authorization handler forwards the browser’s Authorization header into the flow, so a negotiate-capable browser is signed in transparently while everyone else falls through to the username/password form.
The Kerberos provider does not support user synchronization (stream_users → NotSupported) — pair it with an LDAP provider against the same directory if you need searchable local users and group sync.
Active Directory specifics
Section titled “Active Directory specifics”- Use
vendor: ACTIVE_DIRECTORY(defaults:sAMAccountName/objectGUID). ThebindDnis passed verbatim to the simple bind — use the full DN (e.g.CN=issuerd-bind,OU=Service Accounts,DC=corp,DC=example). - Password writes require a secure channel. For the AD vendor the provider writes passwords by replacing
unicodePwdwith the UTF-16LE, quote-wrapped encoding AD expects (crates/issuerd-federation/src/ldap/provider.rs); AD only accepts that attribute over LDAPS (or StartTLS) — pointconnectionUrlatldaps://<dc>:636(this is exactly what the Samba integration test does for its password-write case). The generic vendor writes the plaintextuserPasswordattribute instead. For lab directories with self-signed certificates,noTlsVerify: "true"disables TLS certificate verification onldaps:///StartTLS connections — insecure, dev/lab only. - Samba accepts the previous password for a grace period. Samba AD DC honors the current and previous password after a change (verified against the container; Windows AD does this only for computer accounts, OpenLDAP never). Right after a federated password change against Samba, the old password can still authenticate for a while.
- Account state is not mapped. A
userAccountControl/pwdLastSetmapper exists in the codebase (crates/issuerd-federation/src/mapper/msad_account_control.rs) but is not wired into the runtime mapper chain — sync always imports users as enabled, and AD’s “user must change password at next logon” is not surfaced. Disabling an account in AD still blocks federated password logins, because AD itself refuses the LDAP bind; to lock a user out of everything (including existing sessions and SPNEGO), disable the local Issuerd user as well.
Operating a federated realm
Section titled “Operating a federated realm”Disabling users
Section titled “Disabling users”- Locally (console/Admin API
enabled: false): blocks every login path immediately — the browser flow rejects disabled users before any federation check, and the SPNEGO endpoint refuses to issue sessions for them. - In the directory: blocks password logins (the bind fails) but does not, by itself, disable the local row or existing sessions. Prefer disabling in both places for offboarding.
Password changes
Section titled “Password changes”With editMode: WRITABLE (or UNSYNCED), password changes are propagated to the directory (write_through_federated_password, crates/issuerd-auth-flow/src/built_in.rs). Every password-set path shares the write-through:
- the account console change-password API (verifies the current password against the directory first),
- the
UPDATE_PASSWORDrequired action, - the reset-credentials (forgot-password) email flow,
- the admin
users/{id}/reset-passwordendpoint (atemporaryreset writes the temporary password to the directory and still forces the UPDATE_PASSWORD rotation at next login).
Rules: the write-through only fires when the user’s federation_link matches a live provider; the directory write replaces the local credential write entirely, and any stale local password credentials are deleted on success (login only consults local credentials when the provider errors — a leftover local password would otherwise act as a dormant fallback during directory outages). With editMode: READONLY the write fails with a 400-level “the external directory for this user does not accept password changes” error — keep READONLY and manage passwords in the directory if you do not want users changing them through Issuerd. Non-federated users and links pointing at removed providers keep the pure local-credential behavior. Remember the AD caveat above: unicodePwd writes need ldaps:// (or StartTLS).
Monitoring sync results
Section titled “Monitoring sync results”- The sync response/admin console dialog reports
added/updated/removed/failedper run; anything infailedhas a corresponding warning in the server log (username conflicts, per-user insert/update errors). - Each run persists a
lastSyncTimewatermark into the provider config and writes an admin event. - Group reconciliation logs a structured summary (
LDAP group sync reconciled) with memberships added/removed, groups created/adopted, and names skipped. - For log/metrics plumbing in general, see monitoring.md.
Common pitfalls
Section titled “Common pitfalls”- Bind DN format differs per directory — AD/Samba accept the full DN form (
CN=...,CN=Users,DC=...); OpenLDAP typically usescn=admin,dc=.... The value is used verbatim; test it withldapsearch -Dbefore saving. - Referrals are not chased (
crates/issuerd-federation/src/ldap/connection.rs) — pointusersDndirectly at the partition that holds the users instead of relying on referral following. - Paging is always on for username lookups and syncs (page size
batchSize); the directory must allow the paged-results control and the size must not exceed the server limit (ADMaxPageSize= 1000 by default). - A down directory at login time fails provider loading for the realm; local (non-federated) users can still log in, and federated users fall back to local credentials if they have any.
- A missing keytab file breaks the whole realm’s federation loading when
allowKerberosAuthenticationis"true"— remove or disable the provider instead of leaving a stale path. - Clock skew and DNS are the classic Kerberos failure modes: keep the Issuerd host, KDC, and clients within skew tolerance, and make sure the SPN’s hostname matches the URL users actually browse to.
- For diagnosis steps, see troubleshooting.md.
Test rigs reference
Section titled “Test rigs reference”The contributor integration stack (docker-compose.integration.yml) doubles as a local federation lab. It is dev/test infrastructure, not a production topology.
docker compose -f docker-compose.integration.yml up -d samba-dc openldap| Rig | Endpoint | Bind / credentials | Seeded data |
|---|---|---|---|
samba-dc |
LDAP localhost:389, LDAPS localhost:636, KDC localhost:88 (tcp+udp) |
CN=Administrator,CN=Users,DC=test,DC=issuerd,DC=local / AdminPass123! |
Realm TEST.ISSUERD.LOCAL; users testuser / Password123!, testuser2 / Password123!; group developers (with testuser); SPN HTTP/issuerd.test.issuerd.local with keytab exported to tests/fixtures/samba-dc/shared/issuerd.keytab |
openldap |
LDAP localhost:1389 |
cn=admin,dc=test,dc=issuerd,dc=local / admin |
Base dc=test,dc=issuerd,dc=local (add users via ldapadd) |
examples/provision.federation.yaml wires ready-made realms (samba, openldap, kerberos, plus an ad placeholder realm) against these services — point your server config’s provision key at it for a one-command lab. The Samba container’s healthcheck doubles as a connectivity smoke test (ldapsearch against (sAMAccountName=testuser)), and tests/integration/federation_samba.rs is a known-good reference for the exact attribute layout.