Emergency‑Response Plan for Personal Hardware
Introduction
When a determined adversary stands at your front door, ready to snatch your computer or launch a sophisticated cyber‑attack, the outcome hinges on whether you have a pre‑written emergency‑response plan. A well‑crafted “kill‑switch” that instantly isolates the device, rotates all cryptographic material, and dumps fresh secrets to an encrypted offline medium can turn a potentially catastrophic seizure into a manageable, auditable event. This article walks you through the why, the how, and the best‑practice safeguards you need to survive both opportunistic thieves and state‑level actors.
Why a Kill‑Switch Matters
- Immediate revocation of credentials – Old SSH, GPG, VPN, and API keys stop working the moment you replace them.
- Limiting the attack window – Key rotation takes seconds; an attacker must act faster than you can.
- Preventing lateral movement – Updated public keys on remote servers block any hop the intruder might try.
- Mitigating replay attacks – Fresh keys have new fingerprints, rendering captured handshakes useless.
- Zero‑trust hygiene – Rotation forces you to audit where each key is used, exposing hidden copies.
- Forensic baseline – Timestamped key changes separate “pre‑attack” activity from “post‑attack” activity.
Together, these benefits create a defensive depth that even a well‑funded adversary will struggle to overcome.
Preemptive Hardening (Before the Door Opens)
Areas to consider:
- Full‑Disk Encryption – LUKS + strong PBKDF2/Argon2, TPM‑bound key — Makes raw‑disk reads meaningless without the passphrase.
- Secure Boot & Measured Boot – Enable UEFI Secure Boot, enroll your own keys, log PCR values — Stops malicious firmware from silently capturing passwords.
- Hardware Power‑Cut Kill‑Switch – USB‑controlled relay or discrete power‑off button – Cuts power instantly, erasing RAM where keys may linger.
- Cold‑Boot Resistance – Zero RAM on suspend/shutdown, disable hibernation — Reduces the chance of a RAM dump yielding encryption keys.
- Tamper‑Evident Enclosure – Tamper‑proof screws, intrusion sensor logged to syslog – – Gives you proof of physical meddling for later analysis.
- Default‑Deny Firewall – Outbound
DROP
policy, whitelist only needed services — Guarantees no data leaves the device unless you explicitly allow it. - Key Management Discipline – Short‑lived session keys, master keys stored offline – – Limits the amount of high‑value material that could be stolen.
- Encrypted Offline Backup – LUKS container on a dedicated USB kept in a safe — Provides a clean restore point that the attacker cannot easily find.
The “At‑the‑Door” Playbook
Quick‑Action Trigger
Option A
Dedicated hardware button – A discreet USB‑relay or Raspberry Pi HAT wired to a physical button runs the kill‑switch script.
Option B
Keyboard shortcut – Map a rare combo (e.g., Ctrl+Alt+Shift+Esc
) to systemd-run --on-active=1 /usr/local/sbin/door_kill_switch
.
Full‑Script (Linux)
#!/usr/bin/env bash
#
# kill‑switch.sh – Isolate the machine, rotate credentials,
# export them to an encrypted USB, then clean up.
#
# Usage:
# ./kill-switch.sh # normal operation (requires sudo)
# ./kill-switch.sh –test # dry‑run – generate keys only, no system changes
#
# Optional shortcut: press Ctrl‑Alt‑K while the terminal window has focus
# (binds the same “–test” behavior via a trap).
set -euo pipefail # strict error handling
IFS=$’\n\t’ # sane field splitting
# Configuration (edit these if your environment differs)
USB_LABEL=”KILL_USB” # label of the prepared LUKS USB stick
CRYPT_NAME=”kill_usb” # name for the LUKS mapper
MNT_POINT=”/mnt/killswitch” # mount point for the opened volume
TEST_MODE=false # will be toggled by –test or shortcut
# Helper functions
log() { printf ‘[%s] %s\n’ “$(date ‘+%H:%M:%S’)” “$*”; }
die() { log “ERROR: $*” >&2; exit 1; }
# Detect the optional “test” flag
while (( $# )); do
case “$1” in
–test) TEST_MODE=true; shift ;;
*) break ;;
esac
done
# Bind Ctrl‑Alt‑K to trigger test mode (works in most modern terminals)
if ! $TEST_MODE; then
trap ‘TEST_MODE=true; log “Test mode activated via keystroke”;’ INT
fi
# 1️⃣ Network isolation
if ! $TEST_MODE; then
log “🔒 1️⃣ Isolating network…”
# Bring every interface down (ignore errors, e.g. lo)
for dev in $(ls /sys/class/net/); do
sudo ip link set “$dev” down 2>/dev/null || true
done
sudo ip route flush table main
sudo iptables -P OUTPUT DROP
sudo iptables -P FORWARD DROP
if command -v nft >/dev/null; then
sudo nft add rule ip filter output drop
fi
else
log “[TEST] Skipping network isolation.”
fi
# Drop caches (only in real mode)
if ! $TEST_MODE; then
log “🧹 2️⃣ Dropping page‑cache, dentries, and inodes…”
sync
echo 3 | sudo tee /proc/sys/vm/drop_caches > /dev/null
else
log “[TEST] Skipping cache drop.”
fi
# Credential rotation & export to encrypted USB
log “🔑 3️⃣ Preparing encrypted USB…”
# Resolve the USB device by its label
USB_DEV=”/dev/disk/by-label/${USB_LABEL}”
[[ -e “$USB_DEV” ]] || die “USB device with label ‘${USB_LABEL}’ not found at $USB_DEV”
# Open the LUKS container (asks for passphrase)
if ! $TEST_MODE; then
sudo cryptsetup open “$USB_DEV” “$CRYPT_NAME”
else
log “[TEST] Opening LUKS container (no passphrase prompt in test mode)…”
sudo cryptsetup open “$USB_DEV” “$CRYPT_NAME” <<<“$TEST_MODE” fi # Ensure mount point exists and mount the decrypted volume sudo mkdir -p “$MNT_POINT” sudo mount “/dev/mapper/${CRYPT_NAME}” “$MNT_POINT” # SSH key rotation log ” ↳ Rotating SSH keys…” SSH_DIR=”${HOME}/.ssh” NEW_SSH=”${SSH_DIR}/id_ed25519_new” # Generate a fresh Ed25519 key (empty passphrase) ssh-keygen -t ed25519 -f “$NEW_SSH” -N “” -C “$(hostname)-$(date +%F)” >/dev/null
# Archive any existing keys
mkdir -p “${SSH_DIR}/archived”
mv “${SSH_DIR}/id_ed25519″* “${SSH_DIR}/archived/” 2>/dev/null || true
# Point the standard filenames at the new key
ln -sf “$NEW_SSH” “${SSH_DIR}/id_ed25519”
ln -sf “${NEW_SSH}.pub” “${SSH_DIR}/id_ed25519.pub”
# Back‑up to USB
mkdir -p “${MNT_POINT}/ssh”
cp “${SSH_DIR}/id_ed25519″* “${MNT_POINT}/ssh/”
# GPG key generation
log ” ↳ Generating GPG key…”
NEW_GPG_FPR=$(gpg –batch –quick-generate-key \
“User <user@example.com>” ed25519 sign,encrypt,auth 2y \
–pinentry-mode loopback –passphrase ” 2>/dev/null)
# Export keys to the USB (ASCII‑armored)
gpg –armor –export-secret-keys “$NEW_GPG_FPR” > “${MNT_POINT}/gpg_secret.asc”
gpg –armor –export “$NEW_GPG_FPR” > “${MNT_POINT}/gpg_public.asc”
# WireGuard key rotation
log ” ↳ Rotating WireGuard keys…”
WG_PRIV=$(wg genkey)
WG_PUB=$(printf “%s” “$WG_PRIV” | wg pubkey)
# Store on USB
printf “%s\n” “$WG_PRIV” > “${MNT_POINT}/wg_private.key”
printf “%s\n” “$WG_PUB” > “${MNT_POINT}/wg_public.key”
# Update the host config *only* in real mode
if ! $TEST_MODE; then
sudo sed -i “s|^PrivateKey = .*|PrivateKey = $WG_PRIV|” /etc/wireguard/wg0.conf
sudo systemctl restart wg-quick@wg0
else
log “[TEST] Skipping wg0.conf update and service restart.”
fi
# Miscellaneous secrets (API token, DB password)
log ” ↳ Creating misc secrets…”
TMP_SECRETS=”$(mktemp /tmp/tmp_secrets.XXXXXX.yaml)”
cat < “$TMP_SECRETS”
api_token: “$(openssl rand -hex 16)”
db_password: “$(openssl rand -base64 32)”
EOF
# Encrypt with the *first* public key in the keyring (or the newly created one)
RECIPIENT=$(gpg –list-keys –with-colons |
awk -F: ‘/^pub/ {print $10; exit}’)
[[ -n “$RECIPIENT” ]] || die “No GPG public key found for encryption”
gpg –yes –batch –output “${MNT_POINT}/secrets.yaml.gpg” \
–encrypt –recipient “$RECIPIENT” “$TMP_SECRETS”
rm -f “$TMP_SECRETS”
# 4️⃣ Finalize – unmount & close USB
log “✅ 4️⃣ Finalizing…”
sync
sudo umount “$MNT_POINT”
sudo cryptsetup close “$CRYPT_NAME”
log “=== AT‑THE‑DOOR KILL SWITCH COMPLETE ===”