Follow-up to #306, which landed suspended pending two things. Both are now done and verified, so this enables the schedule.
The privsep caveat is resolved
#306 warned that if pve-exporter@pve!monitoring used privsep=1 with its own ACL, DELETE+re-create would silently drop the exporter's access. Checked on the cluster:
Check
Result
token privsep
0
ACL
/ → PVEAuditor on the user
token-level ACL
none
So re-creating with privsep=0 inherits the user's PVEAuditor and there is nothing to re-grant.
Bootstrap credential: least-privilege, not root
#306 suggested root@pam!<id> as an option. Avoided. PVE rejects per-user ACL paths (/access/users/<id> fails check_path) and authorises the token endpoints via a userid-group check, so the tightest available scope is a group containing only the target user:
role SecretRotator (User.Modify)
user secret-rotator@pve
ACL /access/groups/secret-rotated → SecretRotator # only member: pve-exporter@pve
Verified against the live API: it can mint/delete pve-exporter tokens, sees empty VM and storage lists, /access/users returns only pve-exporter@pve, and minting a root@pam token returns 403.
api-url: the in-cluster Service
Set to https://pve.apps.svc.cluster.local:8006, backed by all three PVE nodes, so one node being down is tolerated.
Two endpoints that do not work, documented in the header so they aren't retried:
https://pve.renner.uno — correct externally, but pods cannot hairpin to their own cluster's ingress LB. The job hangs (confirmed: nc from the openbao ns is BLOCKED to the LB, OPEN to the Service).
a node IP or single node FQDN — needless SPOF.
Test evidence
Two full rotations, each: job completed in ~4s → OpenBao patched → ESO synced → Reloader restarted the exporter → pve_up{id="node/jupiter"} 1.0 (and neptune/saturn) with the new token.
The live CronJob is already suspend: false; this commit makes git match so Flux does not revert it.
Follow-up to #306, which landed **suspended** pending two things. Both are now done and verified, so this enables the schedule.
## The privsep caveat is resolved
#306 warned that if `pve-exporter@pve!monitoring` used `privsep=1` with its own ACL, DELETE+re-create would silently drop the exporter's access. Checked on the cluster:
| Check | Result |
|---|---|
| token privsep | `0` |
| ACL | `/ → PVEAuditor` on the **user** |
| token-level ACL | none |
So re-creating with `privsep=0` inherits the user's PVEAuditor and there is nothing to re-grant.
## Bootstrap credential: least-privilege, not root
#306 suggested `root@pam!<id>` as an option. Avoided. PVE rejects per-user ACL paths (`/access/users/<id>` fails `check_path`) and authorises the token endpoints via a `userid-group` check, so the tightest available scope is a group containing only the target user:
```
role SecretRotator (User.Modify)
user secret-rotator@pve
ACL /access/groups/secret-rotated → SecretRotator # only member: pve-exporter@pve
```
Verified against the live API: it can mint/delete `pve-exporter` tokens, sees **empty** VM and storage lists, `/access/users` returns only `pve-exporter@pve`, and minting a `root@pam` token returns **403**.
## api-url: the in-cluster Service
Set to `https://pve.apps.svc.cluster.local:8006`, backed by all three PVE nodes, so one node being down is tolerated.
Two endpoints that do **not** work, documented in the header so they aren't retried:
- `https://pve.renner.uno` — correct externally, but pods cannot hairpin to their own cluster's ingress LB. The job hangs (confirmed: `nc` from the openbao ns is BLOCKED to the LB, OPEN to the Service).
- a node IP or single node FQDN — needless SPOF.
## Test evidence
Two full rotations, each: job completed in ~4s → OpenBao patched → ESO synced → Reloader restarted the exporter → `pve_up{id="node/jupiter"} 1.0` (and neptune/saturn) with the new token.
The live CronJob is already `suspend: false`; this commit makes git match so Flux does not revert it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
#306 landed suspended pending a privsep check and a bootstrap credential.
Both are now done and the job is verified, so enable the schedule.
privsep caveat RESOLVED: pve-exporter@pve!monitoring is privsep=0 and its
ACL (/ -> PVEAuditor) is on the USER, not the token. DELETE+re-create with
privsep=0 therefore loses no permission and needs no ACL re-grant.
Bootstrap credential is a dedicated least-privilege PVE identity rather than
root@pam. PVE rejects per-user ACL paths and authorises the token endpoints
via a userid-group check, so the tightest available scope is a group whose
only member is the target user:
role SecretRotator (User.Modify)
user secret-rotator@pve
ACL /access/groups/secret-rotated -> SecretRotator
Verified it can mint/delete pve-exporter tokens, sees no VMs or storage,
and gets 403 minting a root@pam token.
api-url is the in-cluster Service (all three nodes back it). The external
name pve.renner.uno cannot be used from a pod: pods cannot hairpin to their
own cluster's ingress LB, which hangs the job. A node IP or single node
FQDN would be a needless SPOF.
Tested: two full rotations; OpenBao patched, ESO synced, Reloader restarted
the exporter, pve_up returned 1.0 for all three nodes both times.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Follow-up to #306, which landed suspended pending two things. Both are now done and verified, so this enables the schedule.
The privsep caveat is resolved
#306 warned that if
pve-exporter@pve!monitoringusedprivsep=1with its own ACL, DELETE+re-create would silently drop the exporter's access. Checked on the cluster:0/ → PVEAuditoron the userSo re-creating with
privsep=0inherits the user's PVEAuditor and there is nothing to re-grant.Bootstrap credential: least-privilege, not root
#306 suggested
root@pam!<id>as an option. Avoided. PVE rejects per-user ACL paths (/access/users/<id>failscheck_path) and authorises the token endpoints via auserid-groupcheck, so the tightest available scope is a group containing only the target user:Verified against the live API: it can mint/delete
pve-exportertokens, sees empty VM and storage lists,/access/usersreturns onlypve-exporter@pve, and minting aroot@pamtoken returns 403.api-url: the in-cluster Service
Set to
https://pve.apps.svc.cluster.local:8006, backed by all three PVE nodes, so one node being down is tolerated.Two endpoints that do not work, documented in the header so they aren't retried:
https://pve.renner.uno— correct externally, but pods cannot hairpin to their own cluster's ingress LB. The job hangs (confirmed:ncfrom the openbao ns is BLOCKED to the LB, OPEN to the Service).Test evidence
Two full rotations, each: job completed in ~4s → OpenBao patched → ESO synced → Reloader restarted the exporter →
pve_up{id="node/jupiter"} 1.0(and neptune/saturn) with the new token.The live CronJob is already
suspend: false; this commit makes git match so Flux does not revert it.🤖 Generated with Claude Code