write secret and key files atomically with their final mode
Each of these wrote its file in place (or set the mode after the write), so a write cut short by a kill or a full disk left a partial file behind, and some secrets existed for a moment at umask mode: - swarm-authelia/swarm-grafana/hive-forge/swarm-bao generators: a partial file passed the `-s`/`-e` guard and was kept forever. They now capture the value first and land it with `atomic_write_secret`. - swarm-controller webhook secret: a truncated file failed the 64-hex check and the secret was regenerated, out of step with the forge hooks. It is now written 0600 to a sibling temp, fsynced, renamed. - hive-c0re htpasswd: the temp is now fsynced and set 0644 before the rename, like the sibling agents.conf writer, instead of relying on c0re's umask. - hivectl wg init: the private key is created 0400 in a temp and renamed, instead of written at umask mode and chmod'ed after. Closes #5068
This commit is contained in:
parent
1726ad3bad
commit
01b8387ab8
7 changed files with 156 additions and 16 deletions
|
|
@ -65,6 +65,7 @@ let
|
|||
|
||||
baoCfg = config.services.hyperhive.swarm.bao;
|
||||
storeRetry = import ../lib/store-retry.nix { };
|
||||
atomicWriteSecret = import ../lib/atomic-write-secret.nix { };
|
||||
havePublisherIdentity =
|
||||
deployCfg.bao.forgePublisherClientCertFile != null
|
||||
&& deployCfg.bao.forgePublisherClientKeyFile != null;
|
||||
|
|
@ -916,6 +917,8 @@ in
|
|||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
${atomicWriteSecret}
|
||||
|
||||
# Idempotent by query, same reasoning as the SSO unit: assert
|
||||
# the effect, not the command's exit code. `--admin`: the
|
||||
# controller creates repos in the agents org on the swarm's
|
||||
|
|
@ -982,8 +985,7 @@ in
|
|||
echo "no token-shaped word in forgejo's generate-access-token output" >&2
|
||||
exit 1
|
||||
fi
|
||||
umask 0177
|
||||
printf '%s' "$token" > "$token_path"
|
||||
atomic_write_secret 0600 "" "$token_path" "$token"
|
||||
echo "minted forge access token for ${swarmControllerForgeUser}"
|
||||
fi
|
||||
'';
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ let
|
|||
unitName = lib.removeSuffix ".service" cfg.unit;
|
||||
instance = lib.removePrefix "authelia-" unitName;
|
||||
stateDir = "/var/lib/${unitName}";
|
||||
atomicWriteSecret = import ./lib/atomic-write-secret.nix { };
|
||||
|
||||
caTrust = import ./lib/hive-ca-trust.nix {
|
||||
inherit lib gatewayCfg;
|
||||
|
|
@ -837,6 +838,8 @@ in
|
|||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
${atomicWriteSecret}
|
||||
|
||||
# Each is generated once and never rotated here: the
|
||||
# session and storage keys are load-bearing for data
|
||||
# already written (sessions, the encrypted store), so
|
||||
|
|
@ -844,7 +847,8 @@ in
|
|||
for f in ${lib.concatStringsSep " " randomKeys}; do
|
||||
p=${lib.escapeShellArg stateDir}/"$f".key
|
||||
if [ ! -s "$p" ]; then
|
||||
head -c 64 /dev/urandom | od -An -tx1 | tr -d ' \n' > "$p"
|
||||
value=$(head -c 64 /dev/urandom | od -An -tx1 | tr -d ' \n')
|
||||
atomic_write_secret 0600 "" "$p" "$value"
|
||||
echo "generated $p"
|
||||
fi
|
||||
chmod 0600 "$p"
|
||||
|
|
|
|||
|
|
@ -3526,8 +3526,8 @@ in
|
|||
# expression, which would render it world-readable into the store.
|
||||
for p in so-pin user-pin; do
|
||||
if [ ! -e ${tokenStoreDir}/$p ]; then
|
||||
( umask 077; openssl rand -hex 16 > ${tokenStoreDir}/$p )
|
||||
chmod 0400 ${tokenStoreDir}/$p
|
||||
pin=$(openssl rand -hex 16)
|
||||
atomic_write_secret 0400 "" ${tokenStoreDir}/$p "$pin"
|
||||
fi
|
||||
done
|
||||
|
||||
|
|
|
|||
|
|
@ -735,10 +735,13 @@ in
|
|||
];
|
||||
script = ''
|
||||
set -euo pipefail
|
||||
|
||||
${atomicWriteSecret}
|
||||
|
||||
key=${lib.escapeShellArg secretKeyPath}
|
||||
if [ ! -s "$key" ]; then
|
||||
umask 077
|
||||
openssl rand -hex 32 > "$key"
|
||||
value=$(openssl rand -hex 32)
|
||||
atomic_write_secret 0600 "" "$key" "$value"
|
||||
echo "generated Grafana's datasource encryption key"
|
||||
fi
|
||||
'';
|
||||
|
|
|
|||
Loading…
Reference in a new issue