deploy --diff: never diff seeded secret files, redact secret values
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KqhjP7nud5gdBg25zCiRk9
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
# ./deploy.sh --check [conf] # validate conf + render all templates, no install
|
# ./deploy.sh --check [conf] # validate conf + render all templates, no install
|
||||||
# ./deploy.sh --diff [conf] # what would change in /etc
|
# ./deploy.sh --diff [conf] # what would change in /etc
|
||||||
# ./deploy.sh render <file> # preview a templated file on stdout
|
# ./deploy.sh render <file> # preview a templated file on stdout
|
||||||
|
# ./deploy.sh --redact # filter stdin, masking secret values (used by --diff; for tests)
|
||||||
# Idempotent. Netplan (wlan0 + Starlink NIC = NM-managed WANs; eth0 is a LAN
|
# Idempotent. Netplan (wlan0 + Starlink NIC = NM-managed WANs; eth0 is a LAN
|
||||||
# port on br0) is NOT deployed here — reference copy in ap/50-van-wan.yaml,
|
# port on br0) is NOT deployed here — reference copy in ap/50-van-wan.yaml,
|
||||||
# applied once manually (apply flaps uplinks). We do check it for drift below.
|
# applied once manually (apply flaps uplinks). We do check it for drift below.
|
||||||
@@ -86,6 +87,12 @@ install_rendered() { # install_rendered <src> <dst> [mode]
|
|||||||
rm -f "$tmp"
|
rm -f "$tmp"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
redact_secrets() { # stdin -> stdout; mask values of secret-looking keys, keep key + diff markers
|
||||||
|
sed -E \
|
||||||
|
-e 's/((pass(word|phrase)|secret|token)[A-Za-z_]*[[:space:]]*=).*/\1<redacted>/I' \
|
||||||
|
-e 's/("[A-Za-z_]*(pass(word|phrase)|secret|token|user)[A-Za-z_]*"[[:space:]]*:[[:space:]]*)"[^"]*"/\1"<redacted>"/I'
|
||||||
|
}
|
||||||
|
|
||||||
template_files() { # every repo file that carries an @TOKEN@ placeholder
|
template_files() { # every repo file that carries an @TOKEN@ placeholder
|
||||||
grep -rlE "$TOKEN_RE" --exclude-dir=.git --exclude-dir=__pycache__ --exclude-dir=.superpowers \
|
grep -rlE "$TOKEN_RE" --exclude-dir=.git --exclude-dir=__pycache__ --exclude-dir=.superpowers \
|
||||||
--exclude-dir=docs --exclude-dir=tests --exclude-dir=hosts \
|
--exclude-dir=docs --exclude-dir=tests --exclude-dir=hosts \
|
||||||
@@ -114,23 +121,29 @@ case "${1:-}" in
|
|||||||
echo "NEW $dst"
|
echo "NEW $dst"
|
||||||
elif ! diff -q <(render "$src") "$dst" >/dev/null; then
|
elif ! diff -q <(render "$src") "$dst" >/dev/null; then
|
||||||
echo "CHANGED $dst"
|
echo "CHANGED $dst"
|
||||||
diff -u "$dst" <(render "$src") | sed 's/^/ /' || true
|
diff -u "$dst" <(render "$src") | redact_secrets | sed 's/^/ /' || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
# Also lists plain `install` files and `rm -f` targets — these sections may be
|
# Also lists plain `install` files and `rm -f` targets — these sections may be
|
||||||
# flag-gated in the real run, but --diff reports them regardless of the flag.
|
# flag-gated in the real run, but --diff reports them regardless of the flag.
|
||||||
grep -E '^[[:space:]]*install -D -m[0-9]+ +[^" ]+ +/' deploy.sh | while read -r _ _ _ src dst _; do
|
grep -E '^[[:space:]]*install -D -m[0-9]+ +[^" ]+ +/' deploy.sh | while read -r _ _ mode src dst _; do
|
||||||
|
# seed-if-absent secret files (from *.example, or 0600): never diff or print
|
||||||
|
case "$src" in *.example) continue ;; esac
|
||||||
|
[ "$mode" = -m0600 ] && continue
|
||||||
if [ ! -e "$dst" ]; then
|
if [ ! -e "$dst" ]; then
|
||||||
echo "NEW $dst"
|
echo "NEW $dst"
|
||||||
elif ! cmp -s "$src" "$dst"; then
|
elif ! cmp -s "$src" "$dst"; then
|
||||||
echo "CHANGED $dst"
|
echo "CHANGED $dst"
|
||||||
diff -u "$dst" "$src" | sed 's/^/ /' || true
|
diff -u "$dst" "$src" | redact_secrets | sed 's/^/ /' || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
grep -E '^[[:space:]]*rm -f +/' deploy.sh | while read -r _ _ path _; do
|
grep -E '^[[:space:]]*rm -f +/' deploy.sh | while read -r _ _ path _; do
|
||||||
[ -e "$path" ] && echo "REMOVE $path"
|
[ -e "$path" ] && echo "REMOVE $path"
|
||||||
done || true
|
done || true
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
|
--redact) # ./deploy.sh --redact — stdin through redact_secrets (no config, no root)
|
||||||
|
redact_secrets
|
||||||
|
exit 0 ;;
|
||||||
render)
|
render)
|
||||||
[ -n "${2:-}" ] || { echo "Usage: $0 render <file>"; exit 1; }
|
[ -n "${2:-}" ] || { echo "Usage: $0 render <file>"; exit 1; }
|
||||||
load_conf "$VANLINK_CONF"
|
load_conf "$VANLINK_CONF"
|
||||||
|
|||||||
@@ -151,4 +151,22 @@ for c in hosts/*.conf; do
|
|||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# 10. --diff never touches seed-if-absent secret files, and redacts secret values
|
||||||
|
out=$(./deploy.sh --diff hosts/wan.conf 2>&1) || true
|
||||||
|
leak=$(echo "$out" | grep -E '/etc/van-gps/config\.json|/etc/van-li3/config\.json|/etc/van-battery/pushover\.json') || true
|
||||||
|
if [ -z "$leak" ]; then
|
||||||
|
ok "--diff ignores seeded secret files"
|
||||||
|
else
|
||||||
|
bad "--diff mentions seeded secret files"; echo "$leak" | sed 's/^/ /'
|
||||||
|
fi
|
||||||
|
red=$(printf '%s\n' 'wpa_passphrase=hunter2' '+ "password": "hunter2",' '- "token": "abc123"' ' "user": "u123"' | ./deploy.sh --redact 2>&1) || true
|
||||||
|
if echo "$red" | grep -qE 'hunter2|abc123|u123'; then
|
||||||
|
bad "--redact leaks a secret value"; echo "$red" | sed 's/^/ /'
|
||||||
|
elif echo "$red" | grep -q '^wpa_passphrase=' && echo "$red" | grep -q '^+ "password"' \
|
||||||
|
&& echo "$red" | grep -q '^- "token"' && echo "$red" | grep -q '^ "user"'; then
|
||||||
|
ok "--redact masks values, keeps keys and diff markers"
|
||||||
|
else
|
||||||
|
bad "--redact lost keys or diff markers"; echo "$red" | sed 's/^/ /'
|
||||||
|
fi
|
||||||
|
|
||||||
exit "$fail"
|
exit "$fail"
|
||||||
|
|||||||
Reference in New Issue
Block a user