Skip to content

Encryption at Rest

Info

New in Atfinity 17.

Atfinity encrypts the credentials of your configured integrations before it writes them to the database. This covers the admin parameters of every integration, such as API keys, client secrets and passwords, the private keys Atfinity generates for an integration, and the passphrases of uploaded certificates. Someone with a copy of the database, for example from a backup, cannot read them without the key.

The key is not stored in the database. Atfinity reads it from the environment variable DATA_ENCRYPTION_KEYS of the API and worker containers. When the variable is not set, Atfinity derives the key from SECRET_KEY instead, so an installation keeps working without any change, but a lost or regenerated SECRET_KEY then also loses every stored credential. Set DATA_ENCRYPTION_KEYS on every installation that holds real credentials.

Setting the key

Generate a key inside the API container, which ships the library that produces it:

python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

On Kubernetes that is kubectl exec deploy/<release>-api -c api -- python -c ..., on Docker Compose docker exec api python -c ....

On Kubernetes, store it in a secret you create yourself and reference that secret as encryption.keysSecretName in the Helm values. The helm chart readme describes the exact commands. Do not add the key to the values file, which ends up in the release history.

On Docker Compose, set DATA_ENCRYPTION_KEYS in the .env file of the CORE server, next to SECRET_KEY.

Restart the API and worker containers after setting or changing the variable. Existing credentials stay readable, since the key derived from SECRET_KEY remains a fallback for decryption, and are moved to the new key by the rotate_encryption_keys command.

Warning

Keep the key together with your disaster recovery material, separate from the database backup. A database restored without the key it was written under cannot decrypt any stored credential, and Atfinity cannot recover them for you.

Rotating the key

DATA_ENCRYPTION_KEYS accepts several keys separated by commas, newest first. Atfinity encrypts with the first key and decrypts with any key in the list. That is what lets you rotate a key while the installation stays in service:

  1. Generate a new key and set the variable to <new key>,<old key>.
  2. Restart the API and worker containers. From now on new values are written with the new key and existing values are still read with the old one.
  3. Run rotate_encryption_keys. It re-encrypts every stored value with the first key and prints how many it rotated, how many were already current and how many it could not decrypt. Repeat it until both rotated and failed are zero.
  4. Set the variable to <new key> only and restart the containers again.

Both restarts can ride a regular release, so a rotation needs no dedicated maintenance window. Remove a key from the list only after step 3 reports zero. A value still encrypted with a removed key cannot be read any more, and the command reports it as failed.

Rotate when the key may have been exposed or when someone who had access to it leaves, and on a fixed schedule otherwise so that the procedure stays exercised. Rotating the key does not help when a credential itself was exposed: replace that credential at the provider and enter the new one in the integration.

Moving data between installations

An archive from zip_export holds the credentials encrypted with the keys of the installation that wrote it. To import it elsewhere, add that installation's key to the target's DATA_ENCRYPTION_KEYS after the target's own key, import, run rotate_encryption_keys, then remove the added key again. An installation that never set DATA_ENCRYPTION_KEYS has no key to hand over, so set one there and run rotate_encryption_keys before exporting.