Hardening checklist¶
Before going productive, walk through this list. Everything is optional, but skip few of them. Vesana's baseline stance is outbound-only: the server has no outbound connection to monitored machines — agents and collectors always connect from the inside out. That structurally reduces the attack surface; the points below harden the server itself.
Authentication¶
- [ ] Mandatory 2FA for super admin and tenant admin — available methods: TOTP (authenticator app such as Aegis/Authy/Google Authenticator), or WebAuthn (hardware key/passkey). Backup codes are mandatory when enabling.
- [ ] Password complexity in admin settings to high
- [ ] JWT lifetime capped at 8–24 h (shorter = more frequent login, safer)
- [ ] Default admin account renamed or disabled — don't leave
admin@example.com - [ ] Login rate limit verified — default 10 req/min/IP for login and 2FA verify, 3 req/min for 2FA resend
Tokens¶
- [ ] Distribute agent tokens minimally — only one per host
- [ ] Collector API keys with long prefix so leaks reveal which tenant
- [ ] Personal access tokens with short lifetime (e.g. 90 days) and targeted scope
Encryption¶
- [ ]
FIELD_ENCRYPTION_KEYin password manager + printed safe-stored - [ ] Backup of key separate from DB backup
- [ ] Only 1–2 people should know the key
TLS¶
- [ ] Real cert (Let's Encrypt or own CA), not self-signed in production
- [ ] HSTS active (default yes)
- [ ] TLS 1.2+ only in nginx config
- [ ] A/A+ at SSL Labs
- [ ] Auto-renewal (Let's Encrypt) or calendar reminder before expiry
Network¶
- [ ] Outbound whitelist on the server: only allowed targets (registry, licence portal, browser push services, SMTP)
- [ ] Inbound 80/443 only — all other ports closed except possibly SSH and NSCA. If agents report in from foreign networks: enable the agent gateway and expose ONLY the gateway port publicly instead (UI port internal/VPN)
- [ ] SSH key-only, no password
- [ ] Fail2ban or similar on SSH brute-force
- [ ] Firewall rules documented and versioned
API replicas and locking¶
- [ ] With multiple API replicas: distributed locks via Redis working (watch system tab)
- [ ] Redis auth active (
REDIS_PASSWORDset — default yes, verify)
Container isolation¶
- [ ] Confirm non-root — API, worker, receiver, and AI-service containers run as non-root (
USER app, UID 1000). Only deliberate exception: the updater container stays root because it needs to write to the Docker socket to run updates. - [ ] Docker socket not additionally mounted into other containers
- [ ] Frontend image (nginx) runs with the built-in nginx user — no adjustment needed
Logging and monitoring¶
- [ ] Audit log active and retention configured
- [ ] Log tailing external if compliance-relevant (SIEM)
- [ ] System health email alarm to super admins
- [ ] Failed-login alerts via an event-log/script check + alert rule
Updates¶
- [ ] GUI updater works (test with small update before first live update)
- [ ] Backup before each update runs (default yes, verify)
- [ ] Update window communicated (status page / wiki)
Backup¶
- [ ] Backup sidecar active
- [ ] Off-site copy (at least weekly)
- [ ] Restore drill at least once per quarter
- [ ]
.envbackup separate
RBAC¶
Vesana is deny by default: every API endpoint requires an explicit permission — without a matching permission you get 403, never an implicit "allowed anyway". Four base roles (Super Admin, Admin, Operator, Viewer) cover the standard cases; custom roles allow granular permission composition beyond that. tenant_access separately controls which tenants a user even sees (one tenant, several, or all as super admin) — every database query is additionally auto-filtered by tenant.
- [ ] Permission inventory — no user with more than necessary
- [ ] Default admin account not for daily use
- [ ] Auditor role for external auditors (read-only permissions such as
audit_log.view_tenant) - [ ] Check
tenant_access— cross-tenant read only where genuinely needed (MSP roles)
AI¶
- [ ] With cloud provider: be clear what goes to the API
- [ ] AI permissions sparingly — not every user needs
ai.query - [ ] AI visibility toggle in user pref explained (privacy wish)
Phone / push¶
- [ ] HTTPS is mandatory — without a secure context no browser allows installation or push
- [ ] Back up the push keys (VAPID) — they belong to the instance and are part of the backup; after a rebuild without backup every device has to re-enable push
Custom code (scripts, profiles)¶
Script-capable check types (agent_script, custom, ssh_script, ssh_custom) can run arbitrary code on monitored machines — the most sensitive point in the whole system, and hardened accordingly:
- Apply scope per user: who's allowed to create/change script checks can be restricted granularly — by tenant, by tag, or on individual hosts. Without an explicit setting the role default applies: Admin may apply scripts on all accessible tenants, any other role may not (super admins are never restricted). A stored setting replaces the default — including "None" for an admin.
- Reach is asked, not assumed: before saving a script body you see how many checks depend on it and choose "for all", "as own script" (copy) or "only this device". Nobody changes a script shared by many hosts by accident.
- Sandboxed Active Collector: scripts run with restricted capabilities, not full root rights. Server-internal addresses (loopback, Docker networks) are blocked for checks of non-privileged users, and per tenant you can allow whether network checks or custom plugins may run on the server (Admin → Agents & Collectors → Active checker, from v1.9.437).
Checklist:
- [ ]
script.createnot for viewer/operator - [ ] Apply scope per user meaningfully restricted (not a blanket "all tenants")
- [ ] Custom scripts tested on test machine before rollout
- [ ] Profile imports from unknown source carefully
- [ ] Policies rolling out scripts — super admin only (enforced by the system, not configurable)
Security updates¶
- [ ] CVE feeds watched (Postgres, Redis, FastAPI, React)
- [ ] Docker images pulled regularly (GUI updater does this)
Operations docs¶
- [ ] Runbooks in wiki — what to do on XYZ
- [ ] Contact list for escalations (in wiki)
- [ ] Status page for external communication
- [ ] Storage of
FIELD_ENCRYPTION_KEYdocumented (who has access)
Compliance-specific¶
| Standard | Special note |
|---|---|
| ISO 27001 | Audit log + 2FA + backup drill suffice for most audits |
| GDPR | Clear: no personal data is monitored (devices). Login data yes → DPA with vendor |
| HIPAA | Disable cloud AI or pick provider with BAA |
| PCI | Vesana is out of scope unless in card-data path — but can help with monitoring |