secrets

encrypted values in the project

A secret is a value that the project keeps encrypted. Only winch secret decrypt prints it.

Format

A secret is encrypted with age for the recipients in winch.yaml. The !secret tag marks it, followed by the ciphertext in base64 on one line.

Where a secret stands

A !secret stands only in a variable value: in the vars of an inventory, and in the defaults of winch.yaml and of recipes, at any depth of a list or a map. --set cannot give a secret.

Commands

winch secret initmake an age key, add it to recipients
winch secret generate [<path>]make a random password as a secret: write it at the path, or print it
winch secret encrypt [<path>]encrypt a value from stdin or a prompt: write it at the path, or print it
winch secret decrypt [<path>]print the plaintext of the secret at the path, or of a secret on stdin
winch secret rekeyencrypt all secrets for the current recipients

winch secret generate and winch secret encrypt need the recipients, but no age key. generate never prints the password.

winch secret encrypt reads a pipe on stdin. At a terminal, it asks for one line twice and does not show what you type. When the two values differ, it writes nothing.

winch secret decrypt needs the age key. It prints the plaintext and nothing else. It reads a !secret value on stdin, with or without the tag. A path to a map or to a value that is not a !secret is an error.

Paths

A path names a place in the project, with keys joined by ".". It works the same for an inventory or a recipe in its own file and as a key in winch.yaml.

vars.xthe default of x in winch.yaml
recipes.mail.vars.mail_xthe default of mail_x in the recipe mail
groups.all.vars.x, hosts.web1.vars.xthe inventory at the top of winch.yaml
inventories.prod.groups.all.vars.xthe inventory prod
vars.users.alicethe key alice in the map variable users
vars.dkim_keys["acme.com"]the key acme.com in the map variable dkim_keys
  • The path ends at a variable value or below it in a map. A path cannot name a list item.
  • A key below the variable name that holds a dot or "[" stands in brackets and double quotes.
  • generate and encrypt replace the value at the path, and the output says replaced vars.x in winch.yaml. They add a missing path with the maps it needs, and the output says added vars.x in winch.yaml.
  • The inventory, the recipe, the host, and the group must exist. The group all always exists.
  • For generate and encrypt, something in the project must read the variable. A path that gives a variable a second default is an error.
  • The commands do not load the whole project, so errors in other places do not stop them.
  • generate and encrypt keep the comments and layout of the file. A flow map on the path, such as {a: 1}, is an error. They write through a symbolic link to the target.

The age key

winch secret init makes a key at ~/.config/winch/age.key, or at the path in WINCH_AGE_KEY. It makes a key only when no key is there. With WINCH_AGE_IDENTITY_CMD, it makes no key and adds the public key of the command's key.

winch reads the age key from the output of the command in WINCH_AGE_IDENTITY_CMD, such as op read op://infra/winch/key. Without it, winch reads the file in WINCH_AGE_KEY, or else ~/.config/winch/age.key. winch validate needs no age key.

A new member

A new member runs winch secret init and sends the change for review. The new key can read the secrets after a member who can read them runs winch secret rekey.

Output

A variable is secret when its value is a !secret, or when its default reads a secret variable. The plan, diffs, errors, logs, and JSON show (secret) in place of a secret, also a part of a value that comes from a secret. A filter replaces each secret value in all text that winch prints, also in output from the host. The filter also hides private keys, age keys, passwords in URLs, and well-known tokens.

Secrets in commands

A secret in a command does not show in the process list of the host. A program that the command starts with the secret as an argument, such as psql -c, does show it.

Warnings and errors

  • winch warns about text in project files that looks like a private key or a token, and is not a !secret.
  • winch warns about a file step whose content reads a secret and whose mode is missing or lets others read it. Inside a range, only the fields of the element that the body reads count, such as .name.
  • A !secret in a certbot hook is an error: certbot stores its hooks in a file that other users can read. A hook that reads a secret through match() fails the host.

Example

# inventories/production.yaml
hosts:
  db1.acme.com: {}
groups:
  all:
    vars:
      db_password: !secret YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB...

# winch.yaml
name: acme
recipients:
  - age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p
steps:
  - file:
      path: /etc/app/db.env
      content: !tpl "DB_PASSWORD={{ .vars.db_password }}\n"
      mode: "0600"