Watch
0
0
Fork
You've already forked hyperhive
0
hyperhive/nix/host-modules/lib/atomic-write-secret.nix
atlas c6a778f666 lint: tighten atomic-write-secret.nix's header comment; fix vale contractions in persistence.md
The header comment grew to 39 lines across two audit-driven rounds,
over the 30-line comment-block-lint max. Trimmed to 30: merged the
value-as-argument rationale with its /proc/cmdline justification into
one paragraph, cut the usage example to one call instead of two, and
condensed the args paragraph — no content dropped, just restatement.

persistence.md's first-boot-migration marker paragraph used "it is"
and "there is not" — vale's Microsoft.Contractions rule (this repo's
config) wants the contracted forms, and "is not" also collides with
"is nothing" as a literal substring, which is what actually tripped
the error. Reworded to "it's" / "there's nothing", no meaning change.

Lint-only: no script logic changed, gates re-run below are all lint
checks (no module-eval, no cargo).

Refs #4723
2026-09-26 21:50:03 +02:00

60 lines
2.8 KiB
Nix

# Shared shell step for a systemd oneshot that lands a freshly-fetched
# secret on disk: never `> path` the live file directly, because a reader
# racing the write can open it between the truncate and the write, or
# between the write and a `chmod` that follows it, and see an empty file
# or one at the wrong mode. `mktemp` always creates its file at 0600
# regardless of umask, so the temp file is private for its whole life; only
# the `chmod`/`chown`/`mv -f` sequence below ever makes the target mode and
# owner visible, and only once the content is already final.
#
# The value is a function ARGUMENT, not piped in on stdin: a producer that
# exits non-zero partway through a pipe still leaves `cat` a clean EOF, so
# a truncated read is indistinguishable from an intentionally short one,
# and the partial content would still land at the live path via `mv`. A
# caller that computes the value with a command captures it into a
# variable first (`value=$(cmd)`, which fails under `set -e` before this
# function is ever called) and passes the finished value in — never handed
# to an external program, so it never appears in a process's own
# `/proc/<pid>/cmdline`, and written with `printf`, a shell builtin.
#
# Pure function — NOT a NixOS module. Call it from a module's `let`:
#
# atomicWriteSecret = import ./lib/atomic-write-secret.nix { };
# script = ''
# ${atomicWriteSecret}
# atomic_write_secret 0600 "" "$path" "$secret"
# '';
#
# Args: mode, an owner for `chown` ("" to keep mktemp's root:root), target
# path, value — written with a trailing newline like every call site's
# prior `printf '%s\n'`. Requires `coreutils` on the caller's `path`.
{ }:
''
atomic_write_secret() {
local mode="$1" owner="$2" target="$3" value="$4" tmp
tmp="$(mktemp "$(dirname -- "$target")/.$(basename -- "$target").XXXXXX")"
# The write happens in a subshell so its own EXIT trap cleans up `$tmp`
# on failure (a `RETURN` trap does not fire when `set -e` aborts the
# function mid-body) without touching an EXIT trap the calling script's
# own `script` may already have — subshell traps are local to the
# subshell. A named handler, not an inline trap string, so `local rc=$?`
# is a normal function-local assignment shellcheck can see: it only
# removes `$tmp` when `$?` is nonzero — on the ordinary path the
# subshell also exits successfully, and `$tmp` has to survive that to
# reach the `mv` below.
(
_atomic_write_secret_cleanup() {
local rc=$?
[ "$rc" -eq 0 ] || rm -f "$tmp"
exit "$rc"
}
trap _atomic_write_secret_cleanup EXIT
printf '%s\n' "$value" > "$tmp"
chmod "$mode" "$tmp"
if [ -n "$owner" ]; then
chown "$owner" "$tmp"
fi
)
mv -f "$tmp" "$target"
}
''