Field encryption¶
Credentials and other secrets are stored in the database encrypted with AES-256-GCM. They exist as plaintext only briefly in memory — for example when the server delivers a collector its check configuration or runs a check itself.
What is encrypted¶
| Area | Values |
|---|---|
| Hosts | SNMP community, SNMPv3 auth and privacy passwords, SSH password, SSH key, password or token secret of the API account |
| Checks | password, key and community fields in the check configuration, script values declared as password |
| Credential sets | all secret fields (SNMP, SSH) |
| Discovery | SNMP communities and SNMPv3 passwords of scans and collector defaults, default community |
| Notification channels | tokens, passwords and webhook addresses |
| E-mail delivery | SMTP password |
| Login | TOTP secrets, SSO client secrets, LDAP bind password |
| AI | API keys of cloud providers |
| Certificate | private key, ACME account key, DNS credentials, EAB key |
| Push | private key of the push key pair (VAPID) |
Key¶
FIELD_ENCRYPTION_KEY is a base64url-encoded 32-byte key. The setup script generates it with:
Only the FIELD_ENCRYPTION_KEY=… line in the .env next to the compose file
takes effect (default install: /opt/vesana/.env) — Docker Compose injects it
into the containers from there. The setup script sets the .env to mode 600
and the secrets/ directory to 700 (files inside 600); running the
installer again repairs permissions that are too open.
secrets/field_encryption_key is only a copy
The setup script also writes the key to secrets/field_encryption_key. That
file is read by nothing: no compose file mounts it, no service opens it.
Changing only that file during a migration changes nothing — the instance
keeps running with the old key.
Flow¶
sequenceDiagram
participant U as User
participant API
participant DB
participant C as Collector
U->>API: Create host with SNMP community
API->>API: encrypt with FIELD_ENCRYPTION_KEY
API->>DB: store (nonce + ciphertext + tag)
Note over DB: Plaintext nowhere stored
C->>API: Fetch check configuration (HTTPS, API key)
API->>DB: Read host
API->>API: decrypt
API->>C: Configuration with credentials
Note over C: Plaintext in memory only
Masking in the API¶
API responses never return encrypted fields in plaintext; they show ***. Even super admins can only set stored credentials, not read them. Saving a form with *** (or an empty password field) keeps the stored value.
Unencrypted legacy values¶
Values from very old installations may still be stored unencrypted. They keep being read and are encrypted on the next save.
Key loss¶
Lost key = data lost
Without FIELD_ENCRYPTION_KEY, encrypted fields stay unreadable forever. There is no recovery mechanism.
Consequence: backing up the key is as important as the database backup. Recommendation in Backup & restore. After a restore, Vesana checks whether the encrypted values can be read with the current key and warns if not.
Changing the key¶
Vesana does not support changing the key — neither in the UI nor with a tool. A new FIELD_ENCRYPTION_KEY makes every previously encrypted value unreadable: credentials on hosts, checks, channels and settings would have to be entered again, and authenticator apps set up again. So don't change the key; store it safely.
Encryption in backups¶
Database dumps contain the encrypted values (not the plaintext, not the key). Restoring into an instance with a different FIELD_ENCRYPTION_KEY leaves the values unreadable — always restore with the same key.
So a .env backup is mandatory along with the database backup, but not in the same place:
- Database backup: volume / off-site copy
.env: password manager / encrypted separate storage
Next¶
- Backup & restore — how key and backups are handled together
- Hardening checklist