Rotating the encryption key

Re-encrypted: the per-report key wrappers, confidential identity and contact, secure e-mail, TOTP secrets, identity-reveal reasons. Report content and attachments are not touched. Back up the database first.

  1. Set ENCRYPTION_KEY_PREVIOUS to the key in use: the old ENCRYPTION_KEY, or SECRET_KEY on an install that never set one.
  2. Set ENCRYPTION_KEY to a new value (openssl rand -hex 32) and recreate the container: docker compose up -d (a restart keeps the old environment).
  3. Run docker compose exec app python scripts/rotate_encryption_key.py. It writes nothing unless every value can be re-encrypted; otherwise it lists the rows by id. Safe to run twice.
  4. Empty ENCRYPTION_KEY_PREVIOUS and recreate the container again: docker compose up -d.

At every start, before migrating, the app checks one stored report key and one TOTP secret. If no key opens them, it refuses to start and writes nothing; the log line names ENCRYPTION_KEY_PREVIOUS. That happens, for example, when ENCRYPTION_KEY is set on an existing install without step 1, or removed again.

Helm: before step 3, wait until every pod runs with the new environment (kubectl rollout status). During the rolling update, old pods cannot read data that new pods write.

Keep the old key

Until step 3 has succeeded, removing it leaves data unreadable. Afterwards, keep it safe as long as any backup from before the rotation is kept. To restore such a backup, put the old key back into ENCRYPTION_KEY_PREVIOUS and run the script.

Edit this page on GitHub