← All four scripts
lib.sh
Shared helpers: dry-run handling, idempotent writes, and the rollback timer the two risky scripts depend on. Sourced, never run directly.
Read this, do not pipe it. These scripts run as root. Copy the source into a file on your own machine, read it, and run it yourself. Nobody should curl a root shell script straight off a website, including this one.
#!/usr/bin/env bash
# Shared helpers for the harden-*.sh scripts.
# Sourced, never executed directly.
set -Eeuo pipefail
RED=$'\033[31m'; GRN=$'\033[32m'; YLW=$'\033[33m'; BLU=$'\033[34m'; DIM=$'\033[2m'; RST=$'\033[0m'
DRY_RUN="${DRY_RUN:-0}"
CHANGED=0
SKIPPED=0
FAILED=0
# Optional site-specific overrides (APP_USER, SANDBOX_SERVICES, AGENT_USERS, …).
# Put local values in harden.local.conf rather than editing the scripts, so the
# scripts stay generic. Untracked. An explicit if-block, not `[ -f ] && source`:
# under `set -e` a false test as the last statement makes `source lib.sh` fail.
if [ -f ./harden.local.conf ]; then
# shellcheck source=/dev/null
. ./harden.local.conf
fi
log() { printf '%s\n' "$*"; }
info() { printf '%s→%s %s\n' "$BLU" "$RST" "$*"; }
ok() { printf '%s✓%s %s\n' "$GRN" "$RST" "$*"; SKIPPED=$((SKIPPED+1)); }
did() { printf '%s✚%s %s\n' "$GRN" "$RST" "$*"; CHANGED=$((CHANGED+1)); }
warn() { printf '%s!%s %s\n' "$YLW" "$RST" "$*"; }
err() { printf '%s✗%s %s\n' "$RED" "$RST" "$*" >&2; FAILED=$((FAILED+1)); }
head1(){ printf '\n%s══ %s ══%s\n' "$BLU" "$*" "$RST"; }
# Fail loudly with the failing line, rather than silently continuing.
trap 'err "line $LINENO: command failed (exit $?)"' ERR
need_root() {
[ "$(id -u)" -eq 0 ] || { err "must run as root (use sudo)"; exit 1; }
}
# run <description> <command...> — honours DRY_RUN
run() {
local desc="$1"; shift
if [ "$DRY_RUN" = "1" ]; then
printf '%s[dry-run]%s %s %s(%s)%s\n' "$YLW" "$RST" "$desc" "$DIM" "$*" "$RST"
return 0
fi
if "$@"; then did "$desc"; else err "$desc"; return 1; fi
}
# Idempotently ensure "key value" exists in a sysctl-style file.
# ensure_kv <file> <key> <value> [separator]
ensure_kv() {
local file="$1" key="$2" val="$3" sep="${4:-=}"
local line="${key}${sep}${val}"
if [ -f "$file" ] && grep -qE "^[[:space:]]*${key}[[:space:]]*${sep}[[:space:]]*${val}[[:space:]]*$" "$file"; then
ok "$key already $val in $(basename "$file")"
return 0
fi
if [ "$DRY_RUN" = "1" ]; then
printf '%s[dry-run]%s would set %s in %s\n' "$YLW" "$RST" "$line" "$file"
return 0
fi
mkdir -p "$(dirname "$file")"
touch "$file"
# Replace an existing key line if present, else append.
if grep -qE "^[[:space:]]*#?[[:space:]]*${key}[[:space:]]*${sep}" "$file"; then
sed -i -E "s|^[[:space:]]*#?[[:space:]]*${key}[[:space:]]*${sep}.*|${line}|" "$file"
else
printf '%s\n' "$line" >> "$file"
fi
did "set $line in $(basename "$file")"
}
# Write a file only if the content differs. Keeps a .bak on first change.
# write_file <path> <mode> <<<"content"
write_file() {
local path="$1" mode="${2:-0644}" content
content="$(cat)"
if [ -f "$path" ] && [ "$(cat "$path")" = "$content" ]; then
ok "$(basename "$path") already correct"
return 0
fi
if [ "$DRY_RUN" = "1" ]; then
printf '%s[dry-run]%s would write %s (%s bytes)\n' "$YLW" "$RST" "$path" "${#content}"
return 0
fi
mkdir -p "$(dirname "$path")"
[ -f "$path" ] && [ ! -f "${path}.bak" ] && cp -a "$path" "${path}.bak"
printf '%s\n' "$content" > "$path"
chmod "$mode" "$path"
did "wrote $path"
}
pkg_installed() { dpkg -s "$1" >/dev/null 2>&1; }
ensure_pkg() {
local p
for p in "$@"; do
if pkg_installed "$p"; then ok "$p installed"
else run "install $p" apt-get install -y -q "$p" >/dev/null; fi
done
}
summary() {
printf '\n%s──────────────────────────────%s\n' "$DIM" "$RST"
printf 'changed: %s%d%s already-ok: %s%d%s failed: %s%d%s\n' \
"$GRN" "$CHANGED" "$RST" "$DIM" "$SKIPPED" "$RST" \
"$([ "$FAILED" -gt 0 ] && echo "$RED" || echo "$DIM")" "$FAILED" "$RST"
[ "$DRY_RUN" = "1" ] && printf '%sDRY RUN — nothing was changed%s\n' "$YLW" "$RST"
return 0
}
# ── Dead man's switch ────────────────────────────────────────────────────────
# Schedules an automatic undo. If you lock yourself out, the box heals itself.
# rollback_arm <unit-name> <minutes> <command...>
rollback_arm() {
local unit="$1" mins="$2"; shift 2
systemctl stop "${unit}.timer" 2>/dev/null || true
systemd-run --collect --on-active="${mins}min" --unit="$unit" "$@" >/dev/null
warn "ROLLBACK ARMED: '$*' runs in ${mins} min unless cancelled"
}
rollback_disarm() {
local unit="$1"
systemctl stop "${unit}.timer" 2>/dev/null || true
systemctl reset-failed "$unit" 2>/dev/null || true
did "rollback cancelled ($unit)"
}
# Blocks until the operator confirms from a SEPARATE session.
confirm_still_connected() {
local what="$1"
cat <<EOF
${YLW}┌─────────────────────────────────────────────────────────────┐
│ VERIFY NOW, FROM A SECOND TERMINAL │
│ │
│ ${what}
│ │
│ Do NOT use this session. Open a NEW one. │
│ If it fails, do nothing — the rollback fires automatically. │
└─────────────────────────────────────────────────────────────┘${RST}
EOF
local ans=""
read -r -p "Confirmed working from a NEW session? type 'yes': " ans
[ "$ans" = "yes" ]
}