macsync/deploy/deploy-edge.sh
Natalie 336f41c905
Some checks failed
Swift Build & Test / swift build + test (push) Has been cancelled
fix(deploy): macsync edge writes a conf.d snippet, not the whole Caddyfile
ct.prod is a SHARED DMZ (Prospector's apps.ftw.pw + macsync). The old edge
script overwrote /etc/caddy/Caddyfile wholesale, so it and Prospector's deploy
clobbered each other (an outage: a Prospector deploy dropped the macsync site
and repointed DNS). Now each service owns one /etc/caddy/conf.d/<svc>.caddy and
the main Caddyfile just `import conf.d/*.caddy`. deploy-edge.sh idempotently adds
the import, removes any legacy inline macsync block, writes conf.d/macsync.caddy,
validates, and hot-reloads — never touching other sites.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 09:06:14 -04:00

68 lines
3.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# Configure the macsync PUBLIC edge on ct.prod (the DMZ), the ONLY public
# listener for macsync. ct.prod's Caddy terminates TLS for macsync.ct.uvlava.com
# and reverse-proxies to the macsync server on the INTERNAL box (lime/ct.services)
# over the private store VPC — never the public leg. lime keeps ZERO public ports.
#
# internet --(443)--> Caddy on ct.prod --(VPC 10.20.0.5:3201)--> macsync server
#
# Idempotent + rebuild-safe: after terraform rebuilds ct.prod (which wipes the
# manual Caddy config), run this to restore the edge. DNS (macsync.ct.uvlava.com
# A -> ct.prod reserved IP) is managed separately in DO DNS / terraform.
set -euo pipefail
JUMP_HOST=quinn-vps # Iceland vps-0 (89.127.233.145)
CT_PROD_PUBLIC=144.126.248.192 # ct.prod reserved IP (reachable via the jump)
MACSYNC_VPC=10.20.0.5 # macsync server private IP (backend_private_ip)
MACSYNC_PORT=3201
EDGE_DOMAIN=macsync.ct.uvlava.com
SSH_KEY=~/.ssh/id_ed25519_1984
SSH="ssh -J $JUMP_HOST -i $SSH_KEY -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=20 root@$CT_PROD_PUBLIC"
die(){ echo "$*" >&2; exit 1; }
step(){ echo "$*"; }
step "checking reachability ($CT_PROD_PUBLIC via $JUMP_HOST)"
$SSH 'echo ok' >/dev/null || die "cannot reach ct.prod via the jump"
step "ensuring Caddy is installed"
$SSH 'command -v caddy >/dev/null 2>&1 || {
export DEBIAN_FRONTEND=noninteractive
apt-get install -y -qq debian-keyring debian-archive-keyring apt-transport-https curl gnupg >/dev/null 2>&1
curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/gpg.key" | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg 2>/dev/null
curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt" | tee /etc/apt/sources.list.d/caddy-stable.list >/dev/null
apt-get update -qq >/dev/null 2>&1 && apt-get install -y -qq caddy >/dev/null 2>&1
}; caddy version | head -1'
step "writing macsync edge as a conf.d snippet (ct.prod is a SHARED DMZ — never clobber other sites like Prospector's apps.ftw.pw)"
$SSH "bash -s '$EDGE_DOMAIN' '$MACSYNC_VPC' '$MACSYNC_PORT'" <<'REMOTE'
set -e
DOMAIN="$1"; UPSTREAM="$2"; PORT="$3"
mkdir -p /etc/caddy/conf.d
touch /etc/caddy/Caddyfile
# Ensure the main Caddyfile imports conf.d (idempotent; never rewrites existing
# sites). Each service on this shared DMZ owns exactly one conf.d/<svc>.caddy.
grep -q '^import conf.d/\*\.caddy' /etc/caddy/Caddyfile || printf '\nimport conf.d/*.caddy\n' >> /etc/caddy/Caddyfile
# Remove any legacy inline macsync block from the main Caddyfile so it isn't a
# duplicate of the snippet (older deploy-edge.sh wrote the whole Caddyfile).
if grep -q "^${DOMAIN} {" /etc/caddy/Caddyfile; then
awk -v d="${DOMAIN} {" 'BEGIN{skip=0} $0==d{skip=1;next} skip&&$0=="}"{skip=0;next} !skip{print}' /etc/caddy/Caddyfile > /etc/caddy/Caddyfile.new && mv /etc/caddy/Caddyfile.new /etc/caddy/Caddyfile
fi
# macsync owns ONLY this snippet.
printf '# macsync public edge -> internal macsync server over the store VPC (lime).\n%s {\n\treverse_proxy %s:%s\n}\n' "$DOMAIN" "$UPSTREAM" "$PORT" > /etc/caddy/conf.d/macsync.caddy
caddy validate --config /etc/caddy/Caddyfile
systemctl enable caddy >/dev/null 2>&1
systemctl reload caddy 2>/dev/null || systemctl restart caddy
REMOTE
step "verifying edge"
sleep 3
code=$(curl -s -o /dev/null -w '%{http_code}' -m10 "https://$EDGE_DOMAIN/health" 2>/dev/null || echo 000)
[ "$code" = "200" ] || die "edge health returned $code (DNS may still be propagating, or the macsync server / VPC firewall is down)"
echo ""
echo "✓ macsync DMZ edge live on ct.prod"
echo " public: https://$EDGE_DOMAIN/health [$code]"
echo " path: internet -> ct.prod Caddy -> $MACSYNC_VPC:$MACSYNC_PORT (VPC, private)"
echo " NOTE: ct.prod cloud firewall (ct-prod-fw, terraform) must allow 80/443;"
echo " the macsync box firewall must allow $MACSYNC_PORT from the VPC (10.20.0.0/16)."