Proxmox VEXV · Security & HardeningAccess control
Authentication realms: LDAP, AD and sync jobs
What you'll learn
- Configure an LDAP or AD realm with TLS, a bind account and a correctly scoped base DN
- Map directory groups onto Proxmox groups so authorisation follows directory membership
- Run pveum realm sync with --dry-run and read the diff before committing it
- Choose a remove-vanished policy and understand what each option does to a departing user
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
A realm answers one question — is this person who they claim to be — and nothing else. It does not decide what they can do; that is the ACL model, and it stays entirely separate. The value of getting realms right is that joiners and leavers stop being a Proxmox task.
The failure this lesson exists to prevent is specific and common: an
estate where the directory is the source of truth for identity everywhere
except the hypervisor, so a person who left three months ago still has
a working pve account with PVEVMAdmin on the production pool.
The realms you can configure
| Type | Backing store | Use it for |
|---|---|---|
pam | Local Linux users in /etc/passwd | root@pam only |
pve | /etc/pve/user.cfg | Service accounts and token owners |
ldap | Any LDAPv3 directory | Human accounts, non-Microsoft directory |
ad | Active Directory | Human accounts, Microsoft directory |
openid | An OIDC provider | Human accounts behind an SSO IdP |
pam and pve are not a scaling problem so much as an evidence
problem. A local account has no lifecycle owner outside Proxmox, so
nothing outside Proxmox will ever tell you it should be gone. Keep them
for the two cases that genuinely cannot use the directory: root@pam,
and service identities that own API tokens.
Configuring the realm
Four things matter and the rest is detail: the servers, the transport, the bind account, and the base DN.
AD_DOMAIN='corp.example.com'
BIND_DN='CN=svc-proxmox,OU=Service Accounts,DC=corp,DC=example,DC=com'
BASE_DN='OU=Staff,DC=corp,DC=example,DC=com'
GROUP_DN='OU=Groups,DC=corp,DC=example,DC=com'
pveum realm add corp --type ad \
--domain "$AD_DOMAIN" \
--server1 dc01.corp.example.com \
--server2 dc02.corp.example.com \
--port 636 \
--secure 1 \
--verify 1 \
--capath /etc/ssl/certs \
--bind-dn "$BIND_DN" \
--base-dn "$BASE_DN" \
--group-dn "$GROUP_DN" \
--comment 'Corporate AD, staff OU only'The bind password is supplied interactively or with --password, and it
is stored under /etc/pve/priv/ where only root can read it. It is also
replicated to every node by pmxcfs, which is correct — every node
authenticates independently — and worth knowing when you scope the bind
account: it exists on every node in the cluster, so treat it as a
cluster-wide credential.
Four options carry more weight than their length suggests.
--secure 1 with --port 636 is LDAPS. Without it the bind password
and every username crosses the network in the clear. There is no reason
to run a realm without TLS in 2026; if the directory does not offer it,
that is a directory problem to escalate, not a Proxmox setting to skip.
--verify 1 validates the directory’s certificate. It is not the
default. A realm with --secure 1 and --verify 0 encrypts the traffic
and accepts any certificate, which stops passive capture and does nothing
about an active attacker. Turning verification on and pointing --capath
at the CA bundle — or --cacert at an internal CA file — is the
difference between transport encryption and authenticated transport.
A narrow --base-dn. Scoping to the OU that holds staff accounts
rather than the domain root means a sync cannot pull in service accounts,
disabled accounts in a tombstone OU, or a contractor OU nobody told you
about. It is also much faster on a large directory.
A read-only bind account. The bind account needs to read user and group objects and nothing more. It should not be a domain admin, and it should be excluded from any policy that expires or rotates it silently, because when it stops working nobody can log in.
Mapping directory groups onto Proxmox groups
This is the part that turns a realm from an authentication convenience into an authorisation mechanism.
Proxmox does not read AD group membership at permission-check time. The sync job reads it, creates a matching Proxmox group, and populates its membership. Your ACLs then reference the Proxmox group. The chain is:
AD group → synced PVE group → ACL entry → privileges on a path.
Change someone’s AD group membership and the next sync moves them between PVE groups; their permissions change without anybody touching an ACL. That is the entire point.
pveum acl modify /pool/production --groups 'pve-vm-admins' --roles PVEVMAdmin
pveum acl modify / --groups 'pve-auditors' --roles PVEAuditor
pveum acl modify /nodes --groups 'pve-node-ops' --roles PVESysAdminThe group names on the Proxmox side are whatever the directory group
names sync to. Naming the AD groups with a pve- prefix is worth doing
on day one: it makes the AD side self-documenting, and it means a search
of the directory for who can administer the hypervisor returns an answer.
The sync job
pveum realm sync reads the directory and reconciles PVE users and
groups against it. It is the operation that carries risk, because it can
delete things.
Configure its defaults on the realm so that a scheduled run and a manual run behave identically:
pveum realm modify corp \
--sync-defaults-options 'scope=both,enable-new=0,remove-vanished=acl;properties' \
--user-classes 'user' \
--user-filter '(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))' \
--group-filter '(&(objectClass=group)(cn=pve-*))' \
--sync_attributes 'email=mail,firstname=givenName,lastname=sn'Reading those left to right:
scope=bothsyncs users and groups.usersorgroupsalone are for staged rollouts and for directories where groups come from somewhere else.enable-new=0creates newly discovered users in a disabled state. They exist, they appear in the GUI, and they cannot log in until someone enables them. On a first sync against a large directory this is the difference between an inventory and an access grant.remove-vanishedis the leaver policy, covered below.--user-filteris doing real work in that example: theuserAccountControlbit-and filter excludes accounts disabled in AD, so a disabled AD account never becomes an enabled PVE user.--group-filterrestricts the sync to groups whose name starts withpve-, which keeps several thousand irrelevant AD groups out ofuser.cfg.--sync_attributesmaps directory attributes onto PVE user properties, so the email that backup-failure notifications go to is the one in the directory.
Always dry-run first
pveum realm sync corp --dry-run 1Read the output before you commit it. Two numbers matter more than the rest: how many users it would create, and how many it would remove. A sync that proposes to remove most of your users has almost always got a filter wrong or lost its connection to a part of the directory tree — that is a configuration bug presenting as a mass deletion, and the dry-run is where you catch it.
$ pveum realm sync corp --dry-run 1starting sync for realm corp (dry-run)
users: found 3, created 0, updated 3
groups: found 2, created 0, updated 2
vanished users: 41 (would remove: acl, properties)
sync finished (dry-run, nothing written)Illustrative output
The leaver policy
remove-vanished takes a semicolon-separated list of what to clean up
when a user or group is no longer returned by the directory query. The
three values, and what each one actually means for the departing person:
| Value | Effect | The risk it leaves |
|---|---|---|
| (unset) | Nothing is removed | The account persists with all its ACLs. This is the state that leaves a leaver with PVEVMAdmin. |
acl | ACL entries referencing the principal are deleted | The user object remains but grants nothing. Auditable, reversible, safe. |
properties | Synced attributes not present in the directory are cleared | Email and name go stale-free; no authorisation change. |
entry | The user or group object is deleted from user.cfg | Immediate and complete — and it removes the evidence along with the access. |
acl;properties is the policy to default to. It removes the access on
the next sync, keeps the user object so an audit can still see that the
account existed and what it used to hold, and is undone by re-adding one
ACL if the removal was wrong.
entry is defensible when a compliance requirement demands that
identities be deleted rather than deactivated, and it should be a
deliberate decision recorded somewhere, not a default someone copied.
Scheduling it
The sync is a datacenter-level job, configured under Datacenter → Permissions → Realms → realm → Sync options, with the schedule set on the realm. A daily sync is the usual cadence — it bounds how long a leaver keeps access to one day, which is what most access-control policies actually ask for.
Do not schedule a sync that has never been dry-run with its current filters. And send its notifications somewhere a human reads: a sync that has been failing for six weeks looks exactly like a sync that has nothing to do.
Common mistakes
--secure 1without--verify 1. Encrypted and unauthenticated. Set--capathor--cacertand turn verification on.- Base DN at the domain root. Slow, and it pulls in every service and disabled account in the directory.
- No
--dry-runbefore changing a filter. The dry run is where a mass removal is a number on a screen instead of an incident. remove-vanished=entryon a scheduled job. The sync’s own failure mode looks identical to a mass departure, andentrymakes it destructive.- Assuming an AD group change revokes access immediately. It revokes at the next sync. Disabling the account revokes at the next login attempt.
- A bind account subject to the normal password-rotation policy. When it expires, nobody can log in, and the error message does not say why.
Key takeaways
- Realms authenticate; ACLs authorise. Changing one never changes the other.
- Map directory groups onto PVE groups and bind ACLs to the groups. After that, joiners and leavers are a directory operation.
enable-new=0makes a first sync an inventory rather than an access grant.remove-vanished=acl;propertiesremoves access, keeps evidence, and survives a sync that misfires.entrydoes not.- Always
--dry-run 1after changing a filter, a base DN or the remove-vanished policy, and read the removal count first. - The fast revocation is disabling the directory account, because authentication is live and authorisation is a snapshot.
Knowledge check
Knowledge check · 4 questions
Q1. An engineer is removed from the pve-vm-admins group in Active Directory at 09:00. The realm sync runs daily at 02:00. When does their Proxmox access actually end?
Q2. On an LDAP or AD realm, --secure 1 encrypts the connection while --verify 1 is what validates the certificate the directory presents, so a realm with --secure 1 and --verify 0 will accept any certificate.
Q3. Why is remove-vanished=acl;properties a safer default for a scheduled sync than remove-vanished=entry? Select all that apply.
Q4. You are running the first sync against a corporate directory with several thousand accounts. Which sync option most reduces the risk of that first run?
Passing score: 75%. Answers are checked in this browser.