Waldur-authoritative accounts in OpenLDAP
Where GLAuth renders a directory from Waldur on every
refresh, an existing OpenLDAP tree is a stateful directory you write into and
reconcile against. The Waldur site agent does that: it takes the username, UID,
primary GID, home directory and login shell Waldur already holds and writes them
into the directory as ordinary posixAccount entries, which a Linux host then
consumes with SSSD.
Use this page when a provider already runs OpenLDAP — often shared by several of its services — and wants Waldur to be the source of truth for who exists in it.
Which direction the identity flows
This is the decision that matters, and the agent supports both.
account_source |
Who decides the username and UID | Use when |
|---|---|---|
ldap (default) |
The plugin: it derives a name from the user's first and last name and scans the directory for a free UID | The directory is authoritative and Waldur is one of several consumers |
waldur |
Waldur: the agent writes the values the POSIX ID pool allocated | Waldur is the source of truth, and especially when one directory serves several of the provider's offerings |
The second mode exists because of a specific failure. A provider's POSIX ID pool already guarantees one UID and one primary GID per user across all of its offerings. If the plugin allocates locally instead, two offerings pointing at the same directory hand the same person two different UIDs behind one DN and one home directory — and whichever agent writes last wins.
flowchart LR
subgraph Waldur
P["POSIX ID pool<br/>(service provider scope)"]
O1["Offering A<br/>offering user"]
O2["Offering B<br/>offering user"]
P --> O1
P --> O2
end
O1 --> AG["Site agent<br/>account_source: waldur"]
O2 --> AG
AG --> L[("OpenLDAP<br/>one posixAccount per user")]
L -->|LDAP| S["SSSD on the login node"]
S --> N["getent / id / PAM"]
Under account_source: waldur the second offering finds the entry the first one
wrote and leaves it alone, so both converge on a single account.
Configuring the agent
Step by step
For an ordered checklist with screenshots — creating the pool, setting the account scope and username policy on the provider and its offerings, configuring the agent and checking the first accounts — see Shared POSIX identity, step by step.
account_source lives in the LDAP plugin's settings, alongside the connection
details:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
username_format is rejected in this mode — Waldur names the accounts, so
leaving the setting in place would only mislead. The uid_range_* settings are
ignored with a warning for the same reason.
Keep gid_range_* clear of the POSIX ID pool
User UIDs and primary GIDs come from Waldur, but project group GIDs are
still allocated inside the directory from gid_range_*. OpenLDAP does not
enforce gidNumber uniqueness, so if that range overlaps the provider's POSIX
ID pool, a project group can silently take a GID already issued as somebody's
primary group — and files end up ambiguously owned. Keep the two ranges
disjoint. The agent cannot check this for you, but it logs the configured
range at start-up so the overlap is at least visible.
These are the resource backend's own groups. Groups kept per project, with GIDs Waldur allocates, are a separate feature: see Project groups in the directory.
Naming the accounts
Under account_source: waldur the login name is whatever Waldur minted for the
offering user, so the Username generation policy decides what the directory
ends up holding. Set it once on the service provider's Account settings page
(Accounts → Account settings); every offering inherits it unless
it sets its own. Any policy works except service_provider — under
that one Waldur waits for the agent to submit a name, which is the opposite of
this mode.
| Policy | Login name | Fits a shared directory? |
|---|---|---|
anonymized |
<prefix><POSIX UID>, for example hpc_9001 |
Yes — see below |
waldur_username |
The user's Waldur username | Yes, if Waldur usernames are directory-safe |
identity_claim |
A username claim from the identity provider | Yes, if the claim is present for every user |
full_name |
jane_smith_01 — a per-offering counter |
Only for a single offering |
freeipa |
The user's FreeIPA profile name | Yes |
service_provider |
Assigned by the agent | Rejected in this mode |
Anonymized names are derived from the pool
An anonymized name is a pure function of the account's POSIX UID: the prefix
followed by the number the POSIX ID pool allocated. Because
the pool is provider-wide, the same person gets the same name on every offering
of the provider that draws from that pool — two site agents writing the same
directory agree on uid=hpc_9001 without ever talking to each other, and a
second offering's reconcile becomes a no-op on an entry the first one created.
The prefix is an account setting resolved the same way a pool is — the
offering's own value first, otherwise the provider's, otherwise the default
waldur_:
| Where | Field | Scope |
|---|---|---|
| Service provider → Accounts → Account settings | Anonymized username prefix (account_options.username_anonymized_prefix) |
Every offering of the provider that does not override it |
| Offering → Edit → Integration → User management → Accounts | Username anonymized prefix (username_anonymized_prefix) |
This offering only |
When all offerings share a directory, set it once on the provider and leave the offering field unset; the offering form then shows the value it inherits.
If no UID resolves for a user — no pool is attached, or POSIX accounts are
disabled on the offering — Waldur falls back to a per-offering counter
(hpc_00001) and logs the gap. Such a name is not stable across offerings, so
attach the pool before users are created rather than after.
One person, one entry
A directory shared by several offerings also needs Waldur to hold one account
per person per provider rather than one per offering. On the service
provider's Account settings page, set Account scope to Per service
provider (account_scope: provider; an individual offering can override it back to
offering when it runs its own separate directory). Switching a provider that
already has offering users links their existing accounts, and is refused while
one person holds different usernames on different offerings. With provider-scoped accounts the UID, primary GID, home directory
and — under anonymized — the username are held once and read through by every
offering user, so the directory entry has exactly one owner in Waldur.
getent on a node then shows the pool identity under the derived name:
1 2 | |
Renames
A Linux username can change while the person stays the same — the username
generation policy changes, say, or the identity provider sends another name.
Waldur decides the new name; the agent has to recognise which directory entry
it belongs to. It does that with a stable key stored on the entry: the person's
Waldur username, in the attribute named by waldur_username_attribute.
employeeNumber is the recommended attribute.
1 2 3 4 | |
- Stamping. The agent writes the key when it creates an entry, and on an
existing entry whose
uidanduidNumberalready match Waldur. Entries that exist when you turn the setting on are therefore stamped on the first cycle. - Renaming. When Waldur reports a new Linux username, the agent looks up the
entry carrying that person's Waldur username and, if its
uidNumbermatches, renames it in place (modrdn uid=<old>touid=<new>).uidNumberstays; the home directory and profile follow Waldur, the personal group is renamed with it, access-group memberships are rewritten, and project groups follow from Waldur's member lists. - A changed Waldur username. If the Waldur username changes while the Linux
name and
uidNumberstay the same, the agent rewrites the stored key, but only on the entry whoseuidanduidNumberboth match the account, and logs the old and new value. It never overwrites a key that belongs to another account. - Group memberships during a rename. While it moves the renamed user's
group memberships, the agent may leave a temporary
descriptionvaluewaldur-site-agent:renamed-from=<old>on the entry, so that an interrupted rename is finished on the next cycle. The agent removes the value itself once every membership has moved; do not edit or remove it by hand.
The agent refuses, logs the reason and leaves the entry alone when two entries
carry the same key, when the entry carrying the key has another uidNumber,
or when the wanted UID is held by an entry whose key is missing or different.
It also refuses when the Linux username and the Waldur username change at
once: neither the old key nor the old name then points at the entry. Rename
such an entry by hand — set its uid to the new name and the key to the new
Waldur username — and the next cycle picks it up.
Without waldur_username_attribute the agent never renames: a renamed account
is reported as a UID collision until the entry is renamed by hand.
Renames also need the offering to expose Waldur usernames to the service provider: Username under User attribute exposure in the offering's integration settings, on by default. Without it the agent cannot see the key it compares, logs once that renames cannot be recognised, and treats a renamed account as a UID collision.
Reserve the attribute for the agent
The agent owns the attribute it stores the key in. Choose one that nothing
else in your directory writes — if employeeNumber already holds HR data,
pick another attribute your schema allows on the user entries — and do not
edit its values by hand except to repair a refused rename.
The agent does not move files: a home directory path that embeds the old name needs moving by the site.
When an account cannot be written
| Situation | What the agent does |
|---|---|
| No POSIX ID pool resolves, or the offering has POSIX accounts disabled | Governed by on_missing_posix_ids. The account is never given a locally-invented id — that is the behaviour this mode exists to remove |
The entry exists but its uidNumber/gidNumber disagree with Waldur |
Governed by on_posix_mismatch |
| The UID Waldur wants is already held by an unrelated entry | Always an error, whether the account would be created on that UID or renumbered onto it. No on_posix_mismatch setting overrides this |
on_missing_posix_ids defaults to error: an account Waldur holds no ids for is
logged with the remedy — attach a POSIX ID pool to the provider, or enable POSIX
accounts on the offering — and skipped, while the rest of the cycle carries on.
Set it to skip to log at debug instead, which is useful while rolling the mode
out across offerings that are not all configured yet.
Two shapes of "no ids" are reported differently, because they mean different things. If every account comes back without them, the server never returned the fields at all — an older Waldur, or an agent not requesting them — and that is one error for the offering rather than one per user.
on_posix_mismatch defaults to report: it logs a before/after diff and changes
nothing. That default is deliberate — rewriting a live account's uidNumber
orphans every file that user owns. Set it to adopt for a one-shot migration once
you are ready to follow up with chown -R, or fail to stop the cycle outright.
Project groups in the directory
Waldur keeps one project group per project at each service
provider: a name, a GID from the provider's POSIX ID pool, and the usernames of
the project's members who hold an account at the provider. With
project_groups.enabled the agent writes those groups into the directory and
lists them in the entries that grant access to a cluster, so a new project's
members can use the cluster without anyone editing the directory.
Example layout
A classic RFC 2307 (nis schema) directory, in which posixGroup is structural
and allows memberUid only:
1 2 3 4 5 | |
For example, with the project groups shown on the Project groups page:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Agent settings
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
| Setting | Default | Meaning |
|---|---|---|
waldur_username_attribute |
— | Attribute holding each person's Waldur username, the key renames are recognised by; recommended employeeNumber, reserved for the agent. Unset: no renames (see Renames) |
personal_groups |
true |
true gives every account a group cn=<username> in groups_ou with its primary GID. false writes accounts with the primary GID from Waldur and no group entry; it requires account_source: waldur |
project_groups.enabled |
false |
Write the project groups |
project_groups.ou |
ou=projects |
OU, relative to base_dn, the groups are written under; it must exist |
project_groups.object_classes |
["top", "posixGroup"] |
Object classes of a new group entry |
project_groups.member_attribute |
memberUid |
memberUid writes usernames; member writes user DNs (uid=<name>,<people_ou>,<base_dn>) |
project_groups.membership |
sync |
sync adds and removes members to match Waldur; add_only never removes one |
project_groups.on_gid_mismatch |
report |
A same-named entry with another GID: report keeps the directory's GID and logs it every cycle; adopt rewrites it to Waldur's |
project_groups.organization_description |
unset | Template of a description value naming the project's organization; it must contain {slug} once, e.g. organization={slug}. Unset, nothing is written (see The organization in the description) |
project_groups.parents |
[] |
Entries that list the DN of each group whose project has a resource on the offering |
parents[].dn |
— | Full DN of the entry |
parents[].attribute |
member |
Attribute that holds the group DNs |
parents[].offering_uuids |
the agent's offering | Offerings whose projects the entry lists, for an entry shared by several offerings |
The project-group pass runs on the agent's periodic reconcile, after the accounts, and also when the offering has no accounts left, so cluster entries are still cleaned up.
With event processing (STOMP) enabled, the offering also subscribes to Waldur's project-group events — a group numbered, renumbered or deleted, or project groups switched on or off for the provider — and the same pass runs a few seconds after one arrives, so a new project's group reaches the directory without waiting for the periodic reconcile. Events arriving close together, such as an import or the backfill when project groups are switched on, share one pass. The pass always covers every group, since which groups a cluster entry lists is decided across all of them, and it never runs at the same time as the periodic one. An agent waits for its first periodic reconcile before acting on events.
What the agent writes
For each group Waldur lists:
- No entry yet — the agent creates it with Waldur's name, GID and
members, unless the GID is already used anywhere under
base_dn(users' primary GIDs included) or aposixGroupof the same name exists outside the project OU. Such a group is reported every cycle and not written; fix it in Waldur with Change GID or by adopting the directory's GID. - An entry with the same name and GID — the agent adopts it and reconciles the members and cluster entries.
- An entry with the same name and another GID — under
reportthe GID is left alone and logged every cycle, while members and cluster entries are still reconciled. Underadoptthe agent rewritesgidNumberto Waldur's. - No GID in Waldur yet — the group is skipped and logged.
Use on_gid_mismatch: adopt only after chgrp
Renumbering a group orphans every file owned by its old GID. Switch to
adopt only once those files have been chgrp-ed to Waldur's GID, which is
why report is the default.
Group entries are never deleted: their GIDs stay reserved in Waldur. A user is
named in a group only once their account entry exists under people_ou.
Cluster entries
The agent makes each cluster entry in parents list the DNs of the groups
whose project has a resource on the entry's offerings. It only ever removes a DN
that lies under the project OU and belongs to a group Waldur lists, and then
only when its project no longer has a resource on those offerings, or the group
could not be written.
A group under the project OU that Waldur does not list is never removed, nor is any DN outside the project OU. That covers a hand-made group, and also a group Waldur stops listing because its offering moved to another provider or the provider was deleted — take such a DN out of the cluster entry by hand. Waldur never renames or deletes a project group otherwise. The directory is left untouched for the cycle when Waldur cannot be read or returns no groups at all.
One cluster entry, several offerings
Two offerings of one provider on one directory see the same groups with the
same GIDs: the first agent creates a group and the second adopts it. If they
share a cluster entry, list both offerings in parents[].offering_uuids on
both; otherwise each removes the groups the other adds. The agent warns at
start-up when two offerings in its configuration point at one entry without
offering_uuids.
Groups written by earlier agents
Earlier agents added a waldur-managed value to the description of
every group they created or adopted. Current agents neither read nor write
it, and leave existing values in place; remove them by hand if you like.
The organization in the description
With organization_description set, every group also gets a description
value naming the organization its project belongs to: the template with
{slug} replaced by the organization's slug. organization={slug} gives
organization=big-science in the example above. The value is written on new
groups, added to adopted ones next to your own values, and
checked on every pass:
- Text around
{slug}— when an organization's slug changes, the agent replaces its previous value, recognised by that text, with the new one. Your own description values stay as long as they do not have the same shape. - A bare
{slug}— the agent cannot tell its own values from yours, so it only ever adds the current one and never removes an old one. It warns about this at start-up; prefer a template with a prefix.
A group whose project has been deleted keeps the value it has.
Members and renames
With membership: sync the members follow Waldur: a user who leaves the
project, or whose account is removed or restricted, is taken out. When Waldur
renames an account and the entry carries the person's key, the agent renames
the entry in place and the groups pick up the new username
(see Renames). With membership: add_only the old username stays in
the group next to the new one, since that mode never removes members.
Connecting SSSD
The agent writes standard posixAccount entries keyed on uid, and personal
groups as posixGroup. Unlike the GLAuth setup,
no attribute-mapping overrides are needed — GLAuth serves users as cn and
groups as ou, which forces ldap_user_name/ldap_group_name overrides; here the
stock RFC 2307 schema applies:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
SSSD must read group membership the way the groups are written:
project_groups.member_attribute |
Members stored as | sssd.conf |
|---|---|---|
memberUid (default) |
Usernames | ldap_schema = rfc2307 |
member |
User DNs | ldap_schema = rfc2307bis |
With the wrong schema the groups still resolve by name and GID, but id and
getent group show them without members, so access through them fails. With
project groups under their own OU, point ldap_group_search_base at it (or at
the base, to see personal and project groups together).
Bind with a dedicated read-only service account rather than the directory
admin. Add sss to the passwd, group and shadow databases in
/etc/nsswitch.conf, enable pam_sss in your PAM stack, and start SSSD.
A Waldur user then resolves as an ordinary POSIX account:
1 2 3 4 5 | |
Add pam_mkhomedir to the session stack if home directories should be created on
first login.
When a user leaves
Removing the SLURM association is the resource backend's job; releasing the directory entry is the LDAP plugin's, and in this mode it is on by default. The chain starts in Waldur, not in the agent:
- Turn on Enable automatic deletion of offering users on the offering
(
offering_user_auto_deletion: true). When a person loses their last project role on the offering, Waldur moves the offering user to Requested deletion. Without this option the offering user stays OK and the entry is kept — the agent only ever acts on Waldur's own request. - The agent picks the request up (on the next membership cycle, or at once from
the offering-user event when it runs in event mode), drops the SLURM
association, then releases the entry as
on_departuresays. - Because one directory serves every offering of the provider, the agent first asks Waldur whether an account with the same username is still live on any sibling offering. If one is, the entry stays enabled; only when every remaining account is in a deletion state (or none remains) is it released. The check runs against Waldur, never the directory, and a failed lookup keeps the entry and retries next cycle.
What "release" means is on_departure:
1 2 3 4 | |
on_departure |
Directory result |
|---|---|
disable (default under waldur) |
The entry is parked: DN, uidNumber, gidNumber and personal group stay as they are. loginShell becomes /usr/sbin/nologin, the shadowAccount class is added with shadowExpire: 1 (an expiry in the past), every memberUid it held in access and project groups is dropped, and description: waldur-site-agent:disabled marks the entry as parked by the agent rather than by an operator |
delete |
The entry, its personal group and its group memberships are removed |
disable is the default because a UID must never be reissued while files owned
by it exist: keeping the entry keeps ls -l honest and keeps the pool's
reservation and the directory in agreement. The name still resolves on the
cluster — getent passwd answers — but the login is shut and, on SLURM, it is
the missing association that refuses a job.
For the parked entry to lock the account out, SSSD on the nodes must honour the
shadow expiry. Add to the [domain/...] section of sssd.conf:
1 | |
Coming back
A returning member — re-added to a project that still holds an allocation —
gets the same entry re-enabled, not a new one. Waldur re-mints the same
username (an anonymized name derives from the pool UID, which the
provider-wide account keeps), so the reconcile finds a live offering user whose
entry exists but carries the parked marker. It restores loginShell from
Waldur, drops shadowExpire and the marker, re-adds the configured
access_groups, and the SLURM association brings project group membership back
with it. An entry an operator disabled by hand (no marker) is treated as an
ordinary profile update and left disabled.
Authentication
Identity and authentication are separate concerns, and the table above only covers identity. For a user to log in, the directory also needs a credential:
- Password. The agent writes
userPasswordonly whengenerate_vpn_passwordis enabled, which produces a random secret delivered by the welcome email. A provider-wideshared_user_passwordon the offering is the other option. - SSH keys. No
sshPublicKeyattribute is written on this path yet, sosss_ssh_authorizedkeys— the mechanism the GLAuth path uses for key-based login — has nothing to serve. The reason is an API one rather than a directory one: a user's keys are exposed on the GLAuth-specific endpoint but not on the offering-user list the agent reads, so the agent never sees them. (The schema is usually present: distributions of OpenLDAP that bundle openssh-lpk already definesshPublicKey.) Tracked in waldur-site-agent#18. Until it lands, use the GLAuth path where key-based SSH is the requirement.
Trying it end to end
The site agent repository ships a runnable demo that brings up Waldur and OpenLDAP, populates the directory, and logs in through SSSD on a throwaway host:
1 2 | |
It prints the whole chain — Waldur's values, the directory entries, getent/id,
a login session, and a PAM check against both a correct and an incorrect password
— and its fixture gives one provider two offerings against one directory, so the
convergence behaviour above is visible in the reconcile log.
For the full chain — pool-derived names, the directory, a Keycloak token claim and FirecREST acting on two clusters under one identity — see POSIX identity with OpenLDAP and FirecREST.