# Shared shell steps for a unit that keeps a secret it once generated on disk # in the swarm secret store instead. The store's value wins; a store holding # none is seeded from the old file, so the secret keeps its value; a new value # is minted only when there is no file either. # # `store_seeded` prints the value and never rotates one: the store is written # with `-cas=0`, so only a leaf that holds nothing takes a value, and a lost # race reads the winner's back. # # Pure function — NOT a NixOS module. Call it from a module's `let`: # # storeSeededSecret = import ./lib/store-seeded-secret.nix { }; # script = '' # ${storeSeededSecret} # store_init # store_login # key="$(store_seeded secret/swarm/services/foo/key /var/lib/foo/key mint_key)" || exit 1 # ''; # # Needs `bao` and `coreutils` on the caller's `path` and the `BAO_*` client # environment set. Takes the EXIT trap, to remove its scratch file. { }: '' # Bounded, so a store that drops packets cannot hold the caller past its # start timeout; one that refuses answers at once. store_bao() { timeout 15 bao "$@" } store_init() { STORE_ERR="$(mktemp)" STORE_UP=0 trap 'rm -f "$STORE_ERR"' EXIT } # Sets STORE_UP. A store that cannot be logged in to leaves every secret to # its file. store_login() { local token if token="$(store_bao login -method=cert -token-only 2>"$STORE_ERR")"; then BAO_TOKEN="$token" export BAO_TOKEN STORE_UP=1 else cat "$STORE_ERR" >&2 echo "could not log in to the swarm secret store; secrets keep the value of their files." >&2 fi } # . The value on stdout; status 1 when # neither the store nor the file can give one. store_seeded() { local leaf="$1" earlier="$2" value candidate shift 2 if [ "$STORE_UP" = 1 ]; then if value="$(store_bao kv get -field=value "$leaf" 2>"$STORE_ERR")" && [ -n "$value" ]; then printf '%s' "$value" return 0 fi if [ -s "$earlier" ]; then candidate="$(< "$earlier")" echo "storing the value of $earlier in $leaf." >&2 else candidate="$("$@")" echo "no earlier value; minting $leaf." >&2 fi if ! printf '%s' "$candidate" | store_bao kv put -cas=0 "$leaf" value=- >/dev/null 2>"$STORE_ERR"; then echo "could not create $leaf; reading it back in case another run created it." >&2 cat "$STORE_ERR" >&2 fi if value="$(store_bao kv get -field=value "$leaf" 2>"$STORE_ERR")" && [ -n "$value" ]; then printf '%s' "$value" return 0 fi cat "$STORE_ERR" >&2 echo "the store did not return $leaf." >&2 fi if [ -s "$earlier" ]; then echo "using the value of $earlier for $leaf." >&2 printf '%s' "$(< "$earlier")" return 0 fi echo "no value for $leaf in the store or at $earlier." >&2 return 1 } ''