thermograph/infra/deploy/provision-agent-access.sh
Emi Griffith ae1d9bb534 Subtree-merge thermograph-infra (origin/main) into infra/
git-subtree-dir: infra
git-subtree-mainline: d6df04eab2
git-subtree-split: 99b4b3f78d
2026-07-22 22:01:11 -07:00

90 lines
4.1 KiB
Bash
Executable file

#!/usr/bin/env bash
# Provisions a dedicated, full-sudo login for an AI coding agent on this VPS.
# Idempotent — safe to re-run (e.g. to rotate the key or re-apply hardening
# after an OS upgrade). Run as root (or via an existing sudo-capable user):
#
# sudo bash /opt/thermograph/deploy/provision-agent-access.sh
#
# The agent's public key is embedded below (AGENT_PUBKEY) rather than passed
# as an argument, so this file alone is the whole bootstrap — copy the current
# one from the repo and run it; there's nothing else to fetch or fill in.
#
# What this does, and why it's a SEPARATE user rather than direct root login:
# - A distinct login name ("agent") gives a clean "who did this" audit trail
# that shared/root login destroys. It is still full-root-capable via
# passwordless sudo — this is a full-trust grant, just an attributable and
# independently revocable one.
# - Revocation is one line: delete AGENT_PUBKEY's line from
# /home/agent/.ssh/authorized_keys, or `deluser --remove-home agent` +
# `rm /etc/sudoers.d/agent`. Your own access is untouched either way.
#
# This script does NOT touch the app, Docker, Postgres, or Terraform-managed
# state — it only manages the "agent" login and sshd/auditd hardening.
set -euo pipefail
# --- fill this in when rotating the key; the current value is the one
# generated for this engagement --------------------------------------------
AGENT_PUBKEY="${AGENT_PUBKEY:-ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICUwrp/neW5UJVmsK9SRveghJ+ayX3r59qgkL9Zo0mqO claude-agent@thermograph-infra}"
AGENT_USER="${AGENT_USER:-agent}"
if [ "$(id -u)" -ne 0 ]; then
echo "Run as root (sudo bash $0)." >&2
exit 1
fi
echo "==> User: $AGENT_USER"
if ! id "$AGENT_USER" >/dev/null 2>&1; then
adduser --disabled-password --gecos "" "$AGENT_USER"
fi
echo "==> Passwordless sudo (full, by design — see header comment)"
echo "$AGENT_USER ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/"$AGENT_USER"
chmod 440 /etc/sudoers.d/"$AGENT_USER"
visudo -cf /etc/sudoers.d/"$AGENT_USER" # fail loudly on a syntax error rather than silently locking sudo
echo "==> Authorized key"
install -d -m 700 -o "$AGENT_USER" -g "$AGENT_USER" /home/"$AGENT_USER"/.ssh
# Replace wholesale rather than append, so re-running after a key rotation
# doesn't accumulate stale keys.
echo "$AGENT_PUBKEY" > /home/"$AGENT_USER"/.ssh/authorized_keys
chown "$AGENT_USER":"$AGENT_USER" /home/"$AGENT_USER"/.ssh/authorized_keys
chmod 600 /home/"$AGENT_USER"/.ssh/authorized_keys
echo "==> sshd hardening (fleet-wide, not just for this account)"
SSHD_DROPIN=/etc/ssh/sshd_config.d/99-agent-hardening.conf
cat > "$SSHD_DROPIN" <<'EOF'
# Managed by deploy/provision-agent-access.sh — key-only login, no passwords.
# Root itself still can't password in; PermitRootLogin prohibit-password
# leaves direct root key-login available for a human who already has a key
# there, without opening a password path for anyone.
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin prohibit-password
EOF
sshd -t # validate before reload; a bad config here can lock out the whole box
systemctl reload sshd 2>/dev/null || systemctl reload ssh 2>/dev/null || true
echo "==> auditd: log root-effective commands (survives shell history -c)"
if ! command -v auditctl >/dev/null 2>&1; then
apt-get update -y -q
apt-get install -y -q auditd audispd-plugins
fi
AUDIT_RULES=/etc/audit/rules.d/agent-root-cmds.rules
cat > "$AUDIT_RULES" <<'EOF'
# Every root-effective exec, tagged for `ausearch -k agentcmd`.
-a always,exit -F arch=b64 -S execve -F euid=0 -k agentcmd
-a always,exit -F arch=b32 -S execve -F euid=0 -k agentcmd
EOF
augenrules --load 2>/dev/null || true
systemctl enable --now auditd 2>/dev/null || true
echo
echo "Done. Verify from your own machine (not this box):"
echo " ssh -i <agent-private-key> $AGENT_USER@<this-host> sudo whoami # -> root"
echo " ssh -o PasswordAuthentication=no <any-user>@<this-host> # password path confirmed dead"
echo
echo "Revoke any time: delete the key line in"
echo " /home/$AGENT_USER/.ssh/authorized_keys"
echo "or remove the account entirely:"
echo " deluser --remove-home $AGENT_USER && rm -f /etc/sudoers.d/$AGENT_USER"