nginx-proxy (new role): - Shared nginx + certbot under /opt/nginx, replacing the copies that lived inside the Paperclip compose project - Apps publish via tasks_from: add_vhost (cert marker -> issue cert -> write vhost -> nginx -t -> reload); a vhost that fails nginx -t is rolled back - Certs issued one per domain via Cloudflare DNS challenge; domains registered as marker files in cert-domains/; optional LE staging - Stock nginx.conf plus conf.d (directory mounts, not single-file mounts); catch-all server drops unknown Host/SNI - Containers renamed to nginx and certbot hermes_docker (new role): - Official Hermes image, host networking, gateway and dashboard bound to loopback and published through nginx-proxy - Dashboard login via basic-auth provider, secrets from the vault - Main model set from hermes_model (fantasmic endpoint, key from vault); config.yaml overrides are merged from a 0600 staged file that is removed afterwards, so the key never appears in a command line - Data dir owner-only (HERMES_HOME_MODE=0700) paperclip-docker: - Remove nginx, certbot and docker-compose download tasks; deploy with community.docker.docker_compose_v2 (idempotent, remove_orphans) - Stop passing the Cloudflare token and certbot email to the Paperclip container via env_file - Bind Postgres to 127.0.0.1 (host networking exposed it on all interfaces); stop chowning pgdata, which the postgres image owns - Handler recreates the paperclip service so .env changes apply - Domain m-v.wdw-labs.cloud -> cranium-command.wdw-labs.cloud Playbooks: - Add day1_deploy_nginx_proxy.yml and day1_deploy_hermes_docker.yml - deploy_paperclip_stack.yml: run nginx-proxy before paperclip-docker, CLI-based container checks (no Docker SDK dependency) - Remove deploy-hermes.yml Host vars: add nginx_proxy_certbot_email and nginx_proxy_certbot_staging (staging stays on until the switch to real certs).
93 lines
2.8 KiB
Bash
93 lines
2.8 KiB
Bash
#!/bin/sh
|
|
# Issues certificates (one per domain) via the Cloudflare DNS challenge.
|
|
#
|
|
# certbot-init.sh -> every domain with a marker file in $DOMAINS_DIR
|
|
# certbot-init.sh a.example.com -> only the domains given as arguments
|
|
#
|
|
# Idempotent: domains that already have a certificate are skipped.
|
|
# Renewal is NOT handled here; `certbot renew` covers every existing lineage.
|
|
set -eu
|
|
|
|
DOMAINS_DIR="${DOMAINS_DIR:-/etc/cert-domains.d}"
|
|
EMAIL="${CERTBOT_EMAIL:-}"
|
|
TOKEN="${CLOUDFLARE_API_TOKEN:-}"
|
|
|
|
STAGING_ARG=""
|
|
if [ "${CERTBOT_STAGING:-0}" = "1" ]; then
|
|
STAGING_ARG="--server https://acme-staging-v02.api.letsencrypt.org/directory"
|
|
echo "NOTE: using the Let's Encrypt STAGING server (certificates will not be trusted)"
|
|
fi
|
|
|
|
if [ "$#" -gt 0 ]; then
|
|
DOMAINS="$*"
|
|
elif [ -d "$DOMAINS_DIR" ]; then
|
|
DOMAINS=$(ls -1 "$DOMAINS_DIR" | grep -E '^[A-Za-z0-9]([A-Za-z0-9.-]*[A-Za-z0-9])?\.[A-Za-z]{2,}$' || true)
|
|
else
|
|
DOMAINS=""
|
|
fi
|
|
DOMAINS=$(printf '%s\n' "$DOMAINS" | tr ', \t' '\n\n\n' | grep -v '^$' | sort -u || true)
|
|
|
|
if [ -z "$DOMAINS" ]; then
|
|
echo "No domains registered in $DOMAINS_DIR; nothing to do"
|
|
exit 0
|
|
fi
|
|
|
|
if [ -z "$EMAIL" ]; then
|
|
echo "ERROR: CERTBOT_EMAIL not set"
|
|
exit 1
|
|
fi
|
|
if [ -z "$TOKEN" ]; then
|
|
echo "ERROR: CLOUDFLARE_API_TOKEN not set"
|
|
exit 1
|
|
fi
|
|
|
|
# Temporary credentials file, shared by all requests and always cleaned up
|
|
CREDS_FILE=$(mktemp /tmp/cloudflare.XXXXXX)
|
|
trap 'rm -f "$CREDS_FILE"' EXIT
|
|
chmod 600 "$CREDS_FILE"
|
|
printf 'dns_cloudflare_api_token = %s\n' "$TOKEN" > "$CREDS_FILE"
|
|
|
|
FAILED=""
|
|
ISSUED=0
|
|
SKIPPED=0
|
|
|
|
for DOMAIN in $DOMAINS; do
|
|
CERT_PATH="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem"
|
|
|
|
if [ -f "$CERT_PATH" ]; then
|
|
echo "Certificate for $DOMAIN already exists at $CERT_PATH"
|
|
SKIPPED=$((SKIPPED + 1))
|
|
continue
|
|
fi
|
|
|
|
echo "Requesting certificate for $DOMAIN via Cloudflare DNS challenge..."
|
|
|
|
# --cert-name pins the lineage directory to the domain name so the
|
|
# live/<domain>/ path nginx expects never gets a -0001 suffix.
|
|
# STAGING_ARG is intentionally unquoted (it expands to two words).
|
|
# shellcheck disable=SC2086
|
|
if certbot certonly \
|
|
--dns-cloudflare \
|
|
--dns-cloudflare-credentials "$CREDS_FILE" \
|
|
--agree-tos \
|
|
--no-eff-email \
|
|
--email "$EMAIL" \
|
|
--non-interactive \
|
|
--cert-name "$DOMAIN" \
|
|
$STAGING_ARG \
|
|
-d "$DOMAIN" && [ -f "$CERT_PATH" ]; then
|
|
echo "Certificate obtained and verified for $DOMAIN at $CERT_PATH"
|
|
ISSUED=$((ISSUED + 1))
|
|
else
|
|
echo "ERROR: Failed to obtain certificate for $DOMAIN"
|
|
FAILED="$FAILED $DOMAIN"
|
|
fi
|
|
done
|
|
|
|
echo "Summary: $ISSUED issued, $SKIPPED already present"
|
|
|
|
if [ -n "$FAILED" ]; then
|
|
echo "ERROR: certificate request failed for:$FAILED"
|
|
exit 1
|
|
fi
|