luminare.infra_edge · v0.2.1
0019. Repository settings in root-only files, checked before they change, instead of Environment= lines in the unit
- Status: accepted
- Date: 2026-10-22
Context
A restic run needs the repository address, its password and often backend credentials (an S3
access key). Roles often write them as Environment= lines into the unit file (readable by every
local user) or pass them to Ansible tasks through environment:, which puts them on the remote
command line, where ps and verbose Ansible output show them. A wrong password in the inventory
must not break a backup that works, and a failing or unreachable backend must never lead to a
second repository being initialised.
Considered options
- Root-only files under
/etc/restic(envwith the repository and the credentials, every value shell-quoted;password;excludes), read by a wrapper/usr/local/sbin/restic-envfor every restic run; the new settings are first tried against the repository through a helper that gets them on standard input. Environment=andRESTIC_PASSWORDin the unit files,snapshots || initbefore each backup.- systemd credentials (
LoadCredentialEncrypted=).
Decision
Option 1. The services, the restore test, the role and an operator all run restic through the
wrapper, so there is one way to reach the repository, and it reads the settings as root. The
settings are written by tasks with no_log and no diff. Before they replace the old files, the
role runs files/repository_state.py with the new settings on standard input; it hands them to
restic as environment variables of the restic process (readable by root only) and reports
whether the repository exists, is missing, rejects the password or fails. Only “missing” leads to
restic init, and restic refuses to initialise a location that holds a repository. Option 2
shows the password to every local user and runs init after any failed snapshots, also a
network error. Option 3 needs a credential key per host and does not cover runs by hand.
Tested on 2026-10-22 in the Molecule scenario: a value with a quote, a dollar sign, backquotes, a
backslash and spaces reaches restic unchanged; a run with a wrong password stops with its
message and leaves the settings files, the repository’s configuration and its keys unchanged; the
password appears nowhere in the journal of the instance or in the output of a converge with
-vvv.
Consequences
- Good: no secret on a command line, in a unit file or in Ansible’s output.
- Good: a typo in the password or the credentials cannot stop a working backup.
- Bad: every run of the role opens the repository once, so an unreachable backend fails the play.
- Bad: the files hold the secrets in plain text for root; disk encryption of the host is out of scope.
docs/adr/0019-repository-settings-in-root-only-files-checked-before-they-change.md on GitHub
Licensed under Apache-2.0. Source: luminare-dev/ansible-collection-infra_edge at v0.2.1.