feat(infra): provision the public rob.fyi vhost + LE cert in infra-setup
infra-setup only handled the internal (newsfeed.internal) cert and vhost and just printed a generic "set it up yourself" note for the WAN name. Now it also, on the web host: - issues the Let's Encrypt cert for rob.fyi idempotently (certbot, Cloudflare DNS-01, ECDSA, --keep-until-expiring), skipping cleanly if the CF token or certbot is absent; - installs the certbot renew deploy-hook (reload nginx); - installs the public vhost, gated on the cert existing so a missing cert can't break `nginx -t` for all of nginx (external-tls.md §4). PUBLIC_FQDN/CERT_EMAIL/CF_CREDS are infra-truth vars at the top; set PUBLIC_FQDN empty to skip. Cloudflare A record + OPNsense :443 forward remain manual and are called out in the closing output. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016fKZzDpvjiJ9eYbPGgJvUP
This commit is contained in:
@@ -21,6 +21,9 @@ API_HOST="slartibartfast.kosherinata.internal" # newsfeed-api + newsfeed-worke
|
||||
WEB_HOST="oolon.kosherinata.internal" # nginx: SPA + API reverse proxy
|
||||
API_PORT="8081"
|
||||
CERT_NAME="newsfeed" # dot-free; serves ${CERT_NAME}.internal
|
||||
PUBLIC_FQDN="rob.fyi" # WAN name (Let's Encrypt); "" to skip
|
||||
CERT_EMAIL="ops@rob.fyi" # certbot registration email
|
||||
CF_CREDS="/root/.certbot-internal" # Cloudflare API token on the web host
|
||||
RUNNER_PUBKEY="${HOME}/.ssh/id_gitea_ci.pub" # runner key distributed to gitea_ci
|
||||
PROVISIONER_PW="${HOME}/.step/secrets/provisioner"
|
||||
ROOT_CA="/etc/pki/ca-trust/source/anchors/root-internal.pem"
|
||||
@@ -163,8 +166,66 @@ VHOST
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
"
|
||||
info "[$host] to publish on the WAN, set server_name + a Let's Encrypt cert in"
|
||||
info " asset/nginx/newsfeed.public.conf and symlink it into sites-enabled."
|
||||
|
||||
provision_public_vhost "$host"
|
||||
}
|
||||
|
||||
# Issue the public Let's Encrypt cert for $PUBLIC_FQDN (idempotent) and install the WAN
|
||||
# vhost, gated on the cert existing (external-tls.md). Skips cleanly if PUBLIC_FQDN is
|
||||
# unset or the Cloudflare credential isn't on the host.
|
||||
provision_public_vhost() {
|
||||
local host="$1"
|
||||
[[ -n "$PUBLIC_FQDN" ]] || { info "[$host] PUBLIC_FQDN unset; skipping WAN vhost"; return 0; }
|
||||
|
||||
if [[ "$DRY_RUN" == 1 ]]; then info "[$host] would issue LE cert + install ${PUBLIC_FQDN} vhost"; return 0; fi
|
||||
|
||||
info "[$host] issuing Let's Encrypt cert for ${PUBLIC_FQDN} (certbot, Cloudflare DNS-01)"
|
||||
# --keep-until-expiring makes this a no-op when the cert is still valid, so it is safe
|
||||
# to run unconditionally. DNS-01 needs no inbound :80 and works even before the WAN A
|
||||
# record / OPNsense forward exist. Requires the Cloudflare token at $CF_CREDS.
|
||||
run_remote "$host" "
|
||||
if [ ! -f '$CF_CREDS' ]; then
|
||||
echo 'WARN: $CF_CREDS missing; cannot issue ${PUBLIC_FQDN} cert' >&2
|
||||
exit 0
|
||||
fi
|
||||
command -v certbot >/dev/null || { echo 'WARN: certbot not installed' >&2; exit 0; }
|
||||
certbot certonly \
|
||||
-m '$CERT_EMAIL' --agree-tos --no-eff-email --noninteractive \
|
||||
--cert-name '$PUBLIC_FQDN' \
|
||||
--key-type ecdsa \
|
||||
--dns-cloudflare \
|
||||
--dns-cloudflare-credentials '$CF_CREDS' \
|
||||
--dns-cloudflare-propagation-seconds 60 \
|
||||
--keep-until-expiring \
|
||||
-d '$PUBLIC_FQDN'
|
||||
# nginx reload after future renewals (external-tls.md §3), once per host.
|
||||
install -d -m 0755 /etc/letsencrypt/renewal-hooks/deploy
|
||||
cat > /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh <<'HOOK'
|
||||
#!/bin/sh
|
||||
systemctl reload nginx 2>/dev/null || true
|
||||
HOOK
|
||||
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
|
||||
"
|
||||
|
||||
info "[$host] installing WAN vhost (${PUBLIC_FQDN})"
|
||||
local vhost; vhost="$(cat "$REPO_ROOT/asset/nginx/newsfeed.public.conf")"
|
||||
# Gate the vhost on the cert existing — nginx -t fails on a missing ssl_certificate and
|
||||
# would block ALL of nginx from reloading (external-tls.md §4).
|
||||
run_remote "$host" "
|
||||
if ! test -d /etc/letsencrypt/live/${PUBLIC_FQDN}; then
|
||||
echo 'NOTE: ${PUBLIC_FQDN} cert not present; skipping WAN vhost install' >&2
|
||||
exit 0
|
||||
fi
|
||||
cat > /etc/nginx/sites-available/newsfeed.public.conf <<'VHOST'
|
||||
${vhost}
|
||||
VHOST
|
||||
ln -sfn ../sites-available/newsfeed.public.conf /etc/nginx/sites-enabled/newsfeed.public.conf
|
||||
nginx -t
|
||||
systemctl reload nginx
|
||||
"
|
||||
info "[$host] ${PUBLIC_FQDN} served. Ensure the Cloudflare A record ${PUBLIC_FQDN} ->"
|
||||
info " kosherinata WAN IP exists (unproxied) and OPNsense forwards :443 -> oolon"
|
||||
info " (reverse-proxies.md §2)."
|
||||
}
|
||||
|
||||
# Mint the first internal cert (idempotent) and enable renewal (internal-tls.md §4).
|
||||
|
||||
Reference in New Issue
Block a user