Jump to
↵select↑↓navigateescclose

Development

Secrets

Keep a project's credentials as age-encrypted files in the repository, decrypt them per command in the dev shell, check who can read them, and let the server read the same ciphertext.

Updated
Tags
  • secrets
  • agenix
  • age
On this page

A project's credentials live in .age files encrypted with age, committed next to the code. The machinery is python-devops-nix's secrets toolkit, shared with frappe-nix: agenix for the files and agenix-shell for the dev shell.

Declare who may read them

At the top level of the project flake, beside perSystem:

odoo-nix.secrets = {
  dir = ./secrets;
  recipients.you = "ssh-ed25519 AAAA… you@host";
  hostRecipients.myserver = "ssh-ed25519 AAAA… root@myserver";  # the production host
};

Declaring recipients turns the module on. There is no secrets.nix to maintain: the agenix rules file is generated from recipients, and hostRecipients are added to every secret the deployment reads. A new project's secrets/README.md says the same.

The .age files are meant to be committed. A flake's source tree is exactly its git-tracked files, so an untracked one is invisible to the build and the shell reports it missing.

The commands

In the dev shell:

CommandWhat it does
setup-backup-accessPrompts for the object-store values, tests them against the bucket, and writes secrets/backup-access.age.
edit-secret <NAME>Creates or edits any other secret.
rekey-secretsRe-encrypts every file after recipients or hostRecipients change.
check-secretsVerifies that every .age file is encrypted to exactly the declared keys.

Secrets are decrypted per command, never at shell entry, so credentials are not in your shell's environment. odoo db restore re-runs itself under a wrapper that decrypts the file (one key prompt). The private keys tried are ~/.ssh/id_ed25519 and ~/.ssh/id_rsa (identityPaths). Decryption needs the shell loaded with --no-pure-eval, which the scaffolded .envrc already does.

The backup-access secret

backupAccess is the one secret odoo-nix declares itself: secrets/backup-access.age, the object-store credentials for database backups, as KEY=value lines that are also valid systemd EnvironmentFile= syntax:

BACKUPS_URL=https://s3.us-east-005.backblazeb2.com
BACKUPS_ACCESS_KEY=…
BACKUPS_SECRET_KEY=…
BACKUPS_BUCKET=my-backups
BACKUPS_PREFIX=odoo

BACKUPS_PREFIX is optional. Unlike the toolkit's default, it is encrypted to hostRecipients by default (backupAccess.hosts), because the server reads the same file to write its backups. See Restore a production backup.

Declare any other secret under extra.<NAME>, with a format of env, raw or json. See Secrets options.

On the server

The project flake exports every declared file as lib.secrets.files.<VAR>, so a host decrypts the same ciphertext:

age.secrets.odoo-backup.file = inputs.myproject.lib.secrets.files.odoo_backup_access;
services.odoo-nix.environmentFiles = [ config.age.secrets.odoo-backup.path ];

The host's key must be in hostRecipients. lib.odooSecrets is the old name of lib.secrets, kept through 1.x and removed in 2.0.0.