diff --git a/scripts/secubox-haproxy-regen-safe b/scripts/secubox-haproxy-regen-safe index 7387b8e1..94cd7ab9 100755 --- a/scripts/secubox-haproxy-regen-safe +++ b/scripts/secubox-haproxy-regen-safe @@ -1,55 +1,58 @@ #!/bin/bash -# secubox-haproxy-regen-safe — Safe haproxy.cfg regeneration with validate + atomic swap + rollback +# secubox-haproxy-regen-safe — Safe haproxy.cfg regeneration with validate + rollback # # Usage: secubox-haproxy-regen-safe [--no-reload] # # Flow: -# 1. Snapshot current /etc/haproxy/haproxy.cfg -# 2. Generate new cfg via haproxyctl regen (output to .new) -# 3. Validate new cfg with haproxy -c -f -# 4. On success: atomic swap + reload haproxy -# 5. On failure: keep current cfg, exit non-zero +# 1. Snapshot /etc/haproxy/haproxy.cfg +# 2. Run `haproxyctl generate` (writes new cfg in place) +# 3. Validate with `haproxy -c -f` — if invalid, restore snapshot +# 4. Reload haproxy on success (skip with --no-reload) # -# CyberMind — added 2026-05-12 after Session 155 haproxy regression incident. +# CyberMind — 2026-05-12 (post Session 156 regression). set -euo pipefail CFG=/etc/haproxy/haproxy.cfg TS=$(date +%s) SNAP="$CFG.bak.$TS-pre-regen" -NEW="$CFG.new.$TS" RELOAD=1 [[ "${1:-}" == "--no-reload" ]] && RELOAD=0 log() { echo "[$(date "+%F %T")] $*" >&2; } -[[ -x /usr/sbin/haproxyctl ]] || { log "FATAL: haproxyctl missing"; exit 2; } +[[ -x /usr/sbin/haproxyctl ]] || { log "FATAL: /usr/sbin/haproxyctl missing"; exit 2; } +[[ -r "$CFG" ]] || { log "FATAL: $CFG missing or unreadable"; exit 2; } -log "Snapshot current cfg -> $SNAP" -cp "$CFG" "$SNAP" +log "Snapshot $CFG → $SNAP" +cp -p "$CFG" "$SNAP" -log "Generate new cfg via haproxyctl" -# haproxyctl writes to $CFG directly; redirect by temporarily moving -cp "$CFG" "$NEW" -if ! /usr/sbin/haproxyctl regen 2>&1 | tail -5; then - log "regen failed; restoring snapshot" - mv "$SNAP" "$CFG" - rm -f "$NEW" +log "Run: haproxyctl generate" +if ! /usr/sbin/haproxyctl generate; then + log "haproxyctl generate failed; restoring snapshot" + cp -p "$SNAP" "$CFG" exit 3 fi -mv "$CFG" "$NEW" -mv "$SNAP" "$CFG" # restore current while we validate the new -log "Validate new cfg" -if haproxy -c -f "$NEW" >/dev/null 2>&1; then - log "validation OK" - mv "$NEW" "$CFG" - if [[ $RELOAD -eq 1 ]]; then - log "reload haproxy" - systemctl reload haproxy && log "reload OK" || { log "reload FAILED"; exit 4; } - fi - log "regen-safe complete" -else - log "validation FAILED; cfg untouched; broken candidate saved as $NEW.broken" - mv "$NEW" "$NEW.broken" +log "Validate new $CFG" +if ! haproxy -c -f "$CFG" >/dev/null 2>&1; then + log "validation FAILED; saving broken candidate and restoring snapshot" + cp -p "$CFG" "$CFG.broken.$TS" + cp -p "$SNAP" "$CFG" + log "broken candidate kept at $CFG.broken.$TS for forensics" exit 5 fi +log "validation OK" + +if [[ $RELOAD -eq 1 ]]; then + log "Reload haproxy" + if systemctl reload haproxy; then + log "reload OK" + else + log "reload failed; rolling back to snapshot" + cp -p "$SNAP" "$CFG" + systemctl reload haproxy && log "rollback reload OK" || log "rollback reload ALSO failed — manual intervention needed" + exit 4 + fi +fi + +log "regen-safe complete (snapshot kept: $SNAP)"