1. Prepare Proxmox
Everything the platform does on the hypervisor goes through one scoped API token over
the REST API. No SSH, no qm, no writing under /etc/pve. Getting the privileges right
here prevents a long tail of confusing failures later.
Three principals, deliberately separated
Section titled “Three principals, deliberately separated”| Principal | Purpose |
|---|---|
zone9-ro@pve!audit | Read-only. Inspection, operator tooling, anything an assistant is pointed at |
zone9-rw@pve!ctl | The platform’s write token. Narrow by design |
| Your own operator account | Everything the platform is not allowed to do |
Keeping the read-only principal separate is worth the two extra commands: it means diagnostic tooling can never make a change.
The control role
Section titled “The control role”Run on any cluster node. One line — a role definition broken across lines silently produces an empty privilege list, and the failures that follow do not point back here.
pveum role add PanelCtl -privs "VM.Allocate VM.Clone VM.Config.Disk VM.Config.CPU VM.Config.Memory VM.Config.Network VM.Config.Cloudinit VM.Config.Options VM.Config.CDROM VM.Config.HWType VM.PowerMgmt VM.Audit VM.Console VM.GuestAgent.Audit Datastore.AllocateSpace Datastore.Audit SDN.Audit SDN.Use Pool.Allocate Pool.Audit Sys.Audit"
pveum user add zone9-rw@pve --comment "zone9 control plane"pveum aclmod / --user zone9-rw@pve --role PanelCtlpveum user token add zone9-rw@pve ctl --privsep 1The last command prints the secret once.
Why the unusual entries are there
Section titled “Why the unusual entries are there”VM.GuestAgent.Audit— the verification stage asks the guest agent for its network interfaces. Without it a server never gets an observed address.SDN.Use— attaching a network interface to a VNet.Pool.Allocate— per-organisation pools, so ownership is visible in the Proxmox UI.VM.Console— the panel’s console relay.
What is deliberately absent
Section titled “What is deliberately absent”Sys.Modify is not in the list, and that is the point of the whole design.
Customer firewalls were originally implemented as cluster security groups, which need system-modification rights. Rather than widen the token, the firewall was redesigned to write rules directly onto each virtual machine — keeping the token inside “manage virtual machines, do not change the system”. The user-visible model did not change.
SDN.Allocate is likewise absent: the SDN fabric is created once by an operator, not by
the platform.
If you add a feature that seems to need a wider privilege, treat that as a signal to redesign the feature. That is the rule the codebase follows.
The ISO role — needed for managed Kubernetes
Section titled “The ISO role — needed for managed Kubernetes”Talos machine configuration is delivered as a cidata ISO uploaded to a storage and
attached to the VM. That needs privileges on one storage only:
pveum role add PanelISO --privs Datastore.AllocateTemplate,Datastore.Allocate,Datastore.Auditpveum acl modify /storage/<iso-storage> --tokens 'zone9-rw@pve!ctl' --roles PanelISOpveum acl modify /storage/<iso-storage> --users 'zone9-rw@pve' --roles PanelISOThree things here trip people up:
Both ACL lines are mandatory. With privilege separation on, the effective permission is the intersection of the user’s permission and the token’s ACL. Granting only the token leaves the intersection empty, and the endpoint still returns 403 — with an error naming the privilege, which makes it look like the grant did not apply.
Datastore.Allocate is for deletion, not upload. The cidata ISO contains
certificates and is removed once the node has booted. Without this privilege every
cluster leaves one behind.
The grant is scoped to one storage on purpose. Attaching these privileges at / would
propagate them to every storage in the cluster.
The read-only principal
Section titled “The read-only principal”pveum role add PanelAudit -privs "VM.Audit Datastore.Audit SDN.Audit Sys.Audit Pool.Audit"pveum user add zone9-ro@pve --comment "zone9 inspection"pveum aclmod / --user zone9-ro@pve --role PanelAuditpveum user token add zone9-ro@pve audit --privsep 1Verify before moving on
Section titled “Verify before moving on”pvesh get /access/roles/PanelCtlConfirm the privilege list is populated and that Sys.Modify is not in it. An empty list
here is the single most common cause of unexplained 403s later.
Then check the token works from wherever the agent will run:
curl -sk -H "Authorization: PVEAPIToken=zone9-rw@pve!ctl=<secret>" \ https://<node>:8006/api2/json/versionStorage
Section titled “Storage”zone9 places disks on the storage you name in the region configuration. Ceph and ZFS with replication both work. The platform does not manage the backend, its encryption or its replication policy — configure those the way you would without zone9.