Proxmox VEXV · Security & HardeningAccess control
OpenID Connect and single sign-on
What you'll learn
- Configure an openid realm end to end, including the redirect URI the IdP must trust
- Choose a username claim that is stable and map group claims onto Proxmox groups
- Explain what OIDC gives you that LDAP does not, and what it takes away
- Design and test a recovery path for the period when the identity provider is unavailable
Prerequisites
Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-12
An OIDC realm moves the login out of Proxmox entirely. The browser is redirected to your identity provider, the provider authenticates the person however it likes — password, hardware key, device trust, conditional access, whatever policy the organisation runs — and returns a signed token asserting who they are. Proxmox verifies the signature and issues a ticket.
The gain is real: Proxmox never sees a password, and every authentication policy your organisation has already bought applies to the hypervisor without Proxmox implementing any of it. The cost is equally real and less often stated: your hypervisor login now has a runtime dependency on a service that is not in your cluster, and the most likely time you need to log in is a time when that service might also be affected.
OIDC against LDAP: what actually changes
| LDAP / AD realm | OIDC realm | |
|---|---|---|
| Where the password is typed | The Proxmox login form | The IdP |
| What Proxmox stores | Bind DN and bind password | Client ID and client secret |
| Second factor | PVE-side TFA, or the directory’s | The IdP’s, applied before the token is issued |
| Group membership | Read by a scheduled sync job | Carried in a claim on every login |
| Membership freshness | As stale as the sync interval | Current at the moment of login |
| Works when the identity service is down | No | No |
| Works when the network path to it is down | No | No, and the failure is browser-visible |
The row worth dwelling on is group freshness. An LDAP realm resolves group membership on a schedule, so removing someone from a group takes effect at the next sync. OIDC carries membership in the token, so it takes effect on their next login. That is a genuine improvement for offboarding — and it means a long-lived Proxmox session still holds the permissions it was granted at login, because the ticket has already been issued.
Registering the client with the identity provider
Do this first, because two of the values it produces are inputs to the Proxmox side.
Register Proxmox as a confidential client using the authorization code flow. Implicit flow is deprecated and Proxmox does not use it.
The redirect URI is the piece that catches people out. It must match what the browser was actually pointed at, character for character, including scheme, host, port and path:
https://pve1.example.com:8006/
Three things follow from that.
The name matters. If your operators reach the GUI by IP address, the redirect URI has to be the IP, and every node needs its own registered URI. This is the point at which most estates decide to put a name in front of the cluster.
Port 8006 is part of it. Omitting the port produces a redirect URI mismatch at the IdP, which reports it as a client configuration error and not as a login failure — so the message the user sees comes from the IdP and mentions nothing about Proxmox.
Every node is a separate entry point. Either register a redirect URI per node, or put all nodes behind one name and register that. The latter is better for reasons that have nothing to do with OIDC, and OIDC makes the argument concrete.
Ask the IdP for the issuer URL — the base URL from which
/.well-known/openid-configuration resolves — and for the client ID
and client secret. Those three are what Proxmox needs.
Configuring the realm
ISSUER='https://idp.example.com/realms/corp'
CLIENT_ID='proxmox-production'
pveum realm add sso --type openid \
--issuer-url "$ISSUER" \
--client-id "$CLIENT_ID" \
--client-key "$OIDC_CLIENT_SECRET" \
--username-claim sub \
--autocreate 1 \
--groups-claim pve_groups \
--groups-autocreate 1 \
--groups-overwrite 1 \
--scopes 'openid email profile' \
--comment 'Corporate SSO, production cluster'Every one of those options is a decision. The two that are hard to
reverse are --username-claim and --groups-overwrite.
--username-claim decides your usernames forever
The claim you pick becomes the local part of the Proxmox username, and
the full user@realm string is what every ACL entry references. Change
the claim later and every existing user becomes a different user with no
permissions, while the old objects linger holding the ACLs.
Three sensible choices, in descending order of safety:
| Claim | Produces | Trade-off |
|---|---|---|
sub | a7f3e9c1-...@sso | Guaranteed stable and unique. Unreadable in the GUI and in audit logs. |
username | alice@sso | Readable. Stable only if your IdP says usernames never change. |
email | alice@corp.example.com@sso | Readable and familiar. Reassignable, and people change email when they change name. |
sub is the specification’s stable subject identifier and the correct
answer on the merits. The objection to it is real — a GUI full of UUIDs
is genuinely worse to operate — and the mitigation is to sync email and
name into the user properties so the GUI has something human to display
while the identity itself stays on sub.
Choosing email is the common shortcut and the one that bites. An
address gets reassigned after somebody leaves, or a person changes their
surname, and now a returning identifier lands on ACLs that belonged to
someone else.
Group claims
--groups-claim names the claim in the token that carries group
membership. It is not standard — groups, roles, and provider-specific
names are all common — so you have to look at an actual token from your
IdP rather than assume.
Configure the IdP to emit a claim containing only the groups relevant
to Proxmox. A claim carrying every group in the organisation works, and
it fills user.cfg with hundreds of group objects that exist for no
reason. Filtering to a pve- prefix on the IdP side is the same
discipline as the --group-filter on an LDAP realm.
--groups-autocreate 1 creates the Proxmox group when a claim names one
that does not exist yet. --groups-overwrite 1 replaces the user’s
group membership on every login with exactly what the token says.
--groups-overwrite 1 is what makes the token authoritative. Without
it, group membership accumulates: a user added to pve-admins today and
removed tomorrow keeps the Proxmox group membership from the first login,
because the second login only adds. That converts the best property of
OIDC — membership current at login — into the worst kind of stale state,
one that looks fresh.
With it on, a user removed from the IdP group loses the Proxmox group at their next login, and any group you manage manually inside Proxmox for that user is also wiped. That trade is worth making; just do not hand-manage groups for OIDC users and expect them to survive.
Scopes and prompts
--scopes defaults to email profile. Add openid explicitly and any
scope your IdP requires to emit the groups claim — some providers gate it
behind a dedicated scope, and the symptom of forgetting is a login that
succeeds with no groups at all.
--prompt maps to the OIDC prompt parameter and takes none, login,
consent or select_account. --prompt login forces re-authentication
at the IdP on every Proxmox login rather than silently reusing an
existing IdP session. For a hypervisor management interface that is a
defensible choice: it means walking away from an authenticated browser
does not leave the cluster one click away.
--acr-values requests a specific authentication context — the mechanism
by which you ask the IdP for a hardware-key authentication specifically,
rather than accepting whatever it last did. Support and value names are
provider-specific, and where it works it is the cleanest way to require
phishing-resistant authentication for hypervisor access without
implementing anything in Proxmox.
Verifying it
There is no pveum command that tests an OIDC realm, because the flow
requires a browser. Verify by using it, and check what arrived:
pveum user list
pveum group list
pveum user permissions 'alice@sso'If the username is not what you expected, stop and fix the claim before
anyone else logs in — every login from here makes the change more
expensive. If the groups are empty, the problem is on the IdP side:
either the claim name does not match --groups-claim, or the scope that
releases it was not requested.
Common mistakes
- A redirect URI missing the port. The IdP rejects it, and the error it shows the user says nothing about Proxmox.
--username-claim email. Readable, and reassignable. Usesuband sync a display name.--groups-overwrite 0. Group membership accumulates and never shrinks, which is stale state that looks fresh.- Forgetting the scope that releases the groups claim. The login succeeds with no groups, so the user sees an empty cluster and reports it as a permissions bug.
- Assuming PVE TFA applies. For an OIDC realm the second factor is the provider’s. Requiring it is a policy at the IdP.
- No node egress to the issuer. The browser redirect works and the token exchange fails, at the last step, looking like a Proxmox fault.
- A break-glass account with TFA disabled. A permanent unmonitored
administrator credential. Use
root@pamwith recovery keys instead.
Key takeaways
- OIDC replaces the login. Groups, ACLs, paths and privileges are unchanged.
- The redirect URI must match the URL the browser used, port included, for every entry point to the cluster.
--username-claim subis stable;emailis reassignable and will eventually land on someone else’s ACLs.--groups-overwrite 1is what makes the token authoritative and stops membership accumulating.- The second factor belongs to the IdP.
--prompt loginand--acr-valuesare how you ask for a stronger one. - The recovery path is
root@pamwith recovery keys and a monitored local operations account — not an account with the controls removed.
Knowledge check
Knowledge check · 4 questions
Q1. Which --username-claim value gives the most stable Proxmox identity, and what is the cost of choosing it?
Q2. A user is removed from the pve-admins group at the identity provider but retains Proxmox admin rights after logging in again. The realm has --groups-claim set correctly. What is the most likely cause?
Q3. An OIDC login redirects to the IdP, authenticates successfully, and then fails on return to Proxmox. Which are plausible causes? Select all that apply.
Q4. Because the identity provider enforces two-factor authentication before issuing the token, revoking a user session at the provider immediately terminates their active Proxmox session.
Passing score: 75%. Answers are checked in this browser.