Introduces granular permission scopes for API tokens (docker:report, docker:manage, host-agent:report, monitoring:read/write, settings:read/write) allowing tokens to be restricted to minimum required access. Legacy tokens default to full access until scopes are explicitly configured. Adds standalone host agent for monitoring Linux, macOS, and Windows servers outside Proxmox/Docker estates. New Servers workspace in UI displays uptime, OS metadata, and capacity metrics from enrolled agents. Includes comprehensive token management UI overhaul with scope presets, inline editing, and visual scope indicators.
7.6 KiB
API Token Scope Design Brief
Objective
Introduce scoped API tokens so administrators can grant the minimum necessary permissions to each integration (Docker agent, host agent, future platform agents, automation scripts, etc.). This replaces today’s “all-or-nothing” tokens and provides safer rotation/revocation paths.
Security Rationale
- Agent and automation tokens are often deployed on hosts or third-party services we do not fully trust. If one leaks today, the attacker inherits full administrator powers (issuing new tokens, mutating settings, triggering installs/updates, etc.).
- Constraining tokens to the minimal scope limits the blast radius: a compromised reporting agent can only submit telemetry, not reconfigure Pulse or other integrations.
- Customers operating in regulated or multi-team environments increasingly ask for auditable least-privilege controls. Scopes give us the primitives to surface warnings on over-privileged tokens and eventually add rotation workflows.
- The feature still has to earn adoption, so pair the technical work with UI nudges and reporting that highlight “Full access” tokens and encourage admins to narrow permissions.
Requirements Overview
-
Token Model Changes
- Each API token record stores a list of scopes (strings) or a bitmask. Recommended canonical strings:
monitoring:readmonitoring:writedocker:reportdocker:managehost-agent:reportsettings:readsettings:write*(legacy full-access sentinel; backend accepts for migration/edit flows but the UI should not expose it)
- Existing tokens must remain valid. Treat missing scopes as
["*"](full access) until the admin edits them.
- Each API token record stores a list of scopes (strings) or a bitmask. Recommended canonical strings:
-
Persistence & Migration
- Extend the token persistence layer (currently BoltDB JSON) to include
scopes: []string. - On startup, detect tokens without the new field and default to
["*"]. - Expose the complete scope list when returning token metadata (internal API used by Settings UI).
- Extend the token persistence layer (currently BoltDB JSON) to include
-
Middleware Enforcement
-
Add a helper
RequireScope(scope string)that checks the request’s token record forscopeor*. -
Apply the helper according to the table below:
Endpoint (or group) HTTP verbs Required scope(s) Notes /api/agents/docker/reportPOSTdocker:reportDocker agent heartbeat payloads /api/agents/docker/commands/*POSTdocker:manage(optional)If we expose command ack/management over tokens /api/agents/docker/hosts/*DELETE,PUT,POSTdocker:manageAdmin actions for Docker hosts /api/agents/host/reportPOSThost-agent:reportHost agent reporting /api/stateGETmonitoring:readGeneral state polling (if token-authenticated) /api/alerts/*GETmonitoring:readAlerts reading APIs /api/alerts/*(mutations)POST,PUT,DELETEmonitoring:writeAcknowledge, silence, etc. /api/settings/*GETsettings:readSettings reads via API /api/settings/*POST,PUT,DELETE,PATCHsettings:writeAny settings mutation /api/security/tokens*all verbs n/a (session only) Leave browser-session only; do not allow API tokens yet /api/install/*,/api/updates/*(mutations)POST,PUTsettings:writeSensitive operational endpoints (More endpoints can be added as required; start with the rows above and expand during implementation.)
-
Maintain compatibility for admin sessions (browser login) which continue to bypass token checks.
-
-
Token Generation API
- Update
POST /api/security/tokensto accept ascopesarray. - Validation rules:
- Reject unknown scope identifiers (except the
*sentinel described above). - If the array is omitted (legacy callers), default to
['*'](full access) to preserve backward compatibility. - If the array is provided but empty, reject with a 400 (“select at least one scope or delete the token”).
- Reject mixed arrays that contain both
'*'and explicit scopes; if the UI submits such a payload, return a 400 with guidance (“either all scopes or full access”).
- Reject unknown scope identifiers (except the
- Include the scope list in the response payload so the UI can display it.
- Update
-
UI/UX Adjustments
- Settings → Security panel:
- When generating or editing a token, show a multi-select with friendly labels (“Docker agent reporting”, “Host agent reporting”, etc.).
- Display the scope summary in the token list (e.g. badges).
- For legacy tokens (implicit
*), show “Full access” and allow editing to reduce scope.
- Docker Agents / Host Agents screens:
- When requesting a token:
- Docker: pre-select both
docker:report(always) anddocker:manageif the user needs lifecycle commands (hide manage behind a toggle if desired). - Host agent: pre-select
host-agent:report.
- Docker: pre-select both
- Warn if the stored token lacks the required scope (fallback to showing
<api-token>placeholder).
- When requesting a token:
- Settings → Security panel:
-
Testing
- Unit tests covering:
- Scope parsing/migration
- Middleware checks (token with/without required scope)
- Integration tests for agent endpoints verifying 403 on missing scope.
- Unit tests covering:
-
Documentation
- Update
README.mdand relevant docs (e.g.docs/CONFIGURATION.md,docs/HOST_AGENT.md, Docker docs) to explain scoped tokens. - Provide an upgrade note for existing installations (“legacy tokens default to full access; edit to restrict scope”).
- Update
Implementation Notes
- Use constants for scope strings to avoid typos throughout the codebase.
- Token middleware already retrieves
APITokenRecord; that struct should grow aScopes []stringfield with helper methods (HasScope). - For future extensibility, keep the scope checks granular but simple (string equality) rather than regex matching.
- Ensure the Settings UI gracefully handles lack of admin privileges (disable scope selection, show hint).
- Update agent commands (Docker/Host) to mention required scope in their description.
- Guardrails: the backend should never auto-insert
*once a scoped token exists, and any admin edit that clears all scopes should surface a clear “delete token or assign scopes” decision.
Acceptance Criteria
- Scoped tokens persisted and surfaced via API.
- Middleware rejects tokens missing required scope.
- UI can create, edit, and display scoped tokens; agent panels auto-fill only when valid.
- Documentation updated; existing tokens remain functional without manual migration.
Once implemented delete this doc.