Skip to content

Encryption

How do I configure chezmoi to encrypt files but only request a passphrase the first time chezmoi init is run?

The following steps use age for encryption.

This can be achieved with the following process:

  1. Generate an age private key.
  2. Encrypt the private key with a passphrase.
  3. Configure chezmoi to decrypt the private key if needed.
  4. Configure chezmoi to use the private key.
  5. Add encrypted files.

First, change to chezmoi's root directory:

$ chezmoi cd ~

Generate an age private key encrypted with a passphrase in the file key.txt.age with the command:

$ age-keygen | age --armor --passphrase > key.txt.age
Public key: age193wd0hfuhtjfsunlq3c83s8m93pde442dkcn7lmj3lspeekm9g7stwutrl
Enter passphrase (leave empty to autogenerate a secure one):
Confirm passphrase:

Use a strong passphrase and make a note of the public key (age193wd0hfuhtjfsunlq3c83s8m93pde442dkcn7lmj3lspeekm9g7stwutrl in this case).

Add key.txt.age to .chezmoiignore so that chezmoi does not try to create it:

$ echo key.txt.age >> .chezmoiignore

Configure chezmoi to decrypt the passphrase-encrypted private key if needed:

$ cat > run_once_before_decrypt-private-key.sh.tmpl <<EOF
#!/bin/sh

if [ ! -f "${HOME}/.config/chezmoi/key.txt" ]; then
    mkdir -p "${HOME}/.config/chezmoi"
    chezmoi age decrypt --output "${HOME}/.config/chezmoi/key.txt" --passphrase "{{ .chezmoi.sourceDir }}/key.txt.age"
    chmod 600 "${HOME}/.config/chezmoi/key.txt"
fi
EOF

Specify the public and private keys for encryption. age.recipient must be your public key from above. Make sure encryption is added at the beginning, before any other sections.

~/.config/chezmoi/chezmoi.toml
encryption = "age"
[age]
    identity = "~/.config/chezmoi/key.txt"
    recipient = "age193wd0hfuhtjfsunlq3c83s8m93pde442dkcn7lmj3lspeekm9g7stwutrl"

Run chezmoi init --apply to generate the chezmoi's config file and decrypt the private key:

$ chezmoi init --apply
Enter passphrase:

At this stage everything is configured and git status should report:

$ git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
    .chezmoi.toml.tmpl
    .chezmoiignore
    key.txt.age
    run_once_before_decrypt-private-key.sh.tmpl

nothing added to commit but untracked files present (use "git add" to track)

If you're happy with the changes you can commit them. All four files should be committed.

Add files that you want to encrypt using the --encrypt argument to chezmoi add, for example:

$ chezmoi add --encrypt ~/.ssh/id_rsa

When you run chezmoi init on a new machine you will be prompted to enter your passphrase once to decrypt key.txt.age. Your decrypted private key will be stored in ~/.config/chezmoi/key.txt.

How to re-encrypt encrypted files

To rotate from an expired GPG key to its replacement, or change from GPG to age encryption, the following steps can be used:

  1. Make sure you have applied all encrypted files (e.g. chezmoi apply decrypts files and places them in their destinations).
  2. Update chezmoi configuration to use the new encryption method (examples: gpg, age, age with one-time passphrase).
  3. Remove all encrypted files from the state via chezmoi forget or chezmoi unmanage.
  4. Add them back with chezmoi add --encrypt.

Example: Migrate from GPG to age

Update chezmoi configuration to use age encryption (with chezmoi edit-config or manually editing the corresponding template):

- encryption = "gpg"
- [gpg]
-     recipient = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ encryption = "age"
+ [age]
+     recipient = "age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+     identity = "/home/user/key.txt"

Re-encrypt the files with a script like this:

for encrypted_file in $(chezmoi managed --include encrypted --path-style absolute)
do
  # optionally, add --force to avoid prompts
  chezmoi forget "$encrypted_file"

  # strip the .asc extension
  decrypted_file="${encrypted_file%.asc}"

  chezmoi add --encrypt "$decrypted_file"
done