Skip to main content
RunBook Academy

Proxmox VEXV · Security & HardeningAccess control

OpenID Connect and single sign-on

Advanced⏱ ~26 minpveum

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

Not yet marked complete on this device.

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 realmOIDC realm
Where the password is typedThe Proxmox login formThe IdP
What Proxmox storesBind DN and bind passwordClient ID and client secret
Second factorPVE-side TFA, or the directory’sThe IdP’s, applied before the token is issued
Group membershipRead by a scheduled sync jobCarried in a claim on every login
Membership freshnessAs stale as the sync intervalCurrent at the moment of login
Works when the identity service is downNoNo
Works when the network path to it is downNoNo, 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

Configuration changean OIDC realm with group claims
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:

ClaimProducesTrade-off
suba7f3e9c1-...@ssoGuaranteed stable and unique. Unreadable in the GUI and in audit logs.
usernamealice@ssoReadable. Stable only if your IdP says usernames never change.
emailalice@corp.example.com@ssoReadable 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:

Read-only / Safewhat the login actually created
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. Use sub and 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@pam with 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 sub is stable; email is reassignable and will eventually land on someone else’s ACLs.
  • --groups-overwrite 1 is what makes the token authoritative and stops membership accumulating.
  • The second factor belongs to the IdP. --prompt login and --acr-values are how you ask for a stronger one.
  • The recovery path is root@pam with recovery keys and a monitored local operations account — not an account with the controls removed.

Knowledge check

Knowledge check · 4 questions

  1. Q1. Which --username-claim value gives the most stable Proxmox identity, and what is the cost of choosing it?

  2. 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?

  3. 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.

  4. 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.