diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f76bead..1274443 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -91,8 +91,8 @@ client sends a command string (e.g., `deploy-example.com`), and `deploy.sh` parses it to determine the action: - `deploy-` — deploy a specific app -- `deploy-nginx` — deploy nginx (with cert handling) -- `cert-` — issue a cert only +- `deploy-nginx` — deploy nginx (fails if certs are missing) +- `cert-` — issue a cert only (via `certbot --webroot`, zero downtime) - `provision` — run `configure.sh` ## SSL certificate handling @@ -101,16 +101,9 @@ Cert names are derived from nginx conf filenames (e.g., `example.com.conf` → cert `example.com`). All `server_name` values in the conf are included in the cert as SANs. -**New domain (nginx not yet running):** -1. `deploy.sh` detects missing cert files -2. Stops nginx temporarily -3. Issues cert via `certbot --standalone` -4. Starts nginx -5. On error, cleans up the temp conf file (trap) - -**New domain (nginx already running):** -1. Issues cert via `certbot --webroot` using the ACME challenge directory -2. Zero downtime +**New domain:** issue cert before deploying nginx config: +1. Run `app.sh cert ` (uses `certbot --webroot`, zero downtime) +2. Deploy nginx config — `deploy.sh` verifies certs exist, fails if missing **Renewal:** certbot timer/cron runs daily, deploy hook reloads nginx. diff --git a/README.md b/README.md index 3e73e1c..03495d0 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ nodes. scripts/ bootstrap.sh # First-time server setup (manual, uses Bitwarden) configure.sh # Idempotent server config (CI-safe) - deploy.sh # Deploy + cert issuance + provision (called via SSH) + deploy.sh # Deploy + provision (called via SSH) tools/ new-app.sh # Add a new static site (run from dev machine) new-service.sh # Add a new Docker service (run from dev machine) diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 523c100..1ba1a55 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -92,33 +92,15 @@ else fi if [ "$APP" = "nginx" ]; then - # Issue certs for any new domains (brief nginx downtime while certbot binds port 80) - missing_certs=false + # Fail loudly if any certs are missing — issue them first with: app.sh cert for conf in /opt/hantim/docker/nginx/conf.d/*.*.conf; do cert_name=$(basename "$conf" .conf) if [ ! -d "/etc/letsencrypt/live/$cert_name" ]; then - missing_certs=true - break + echo "ERROR: Missing certificate for $cert_name" + echo " Run: app.sh cert $cert_name" + exit 1 fi done - if [ "$missing_certs" = true ]; then - docker stop nginx 2>/dev/null || true - trap 'docker start nginx 2>/dev/null || true' EXIT - for conf in /opt/hantim/docker/nginx/conf.d/*.*.conf; do - cert_name=$(basename "$conf" .conf) - if [ ! -d "/etc/letsencrypt/live/$cert_name" ]; then - domains=$(grep -oP 'server_name\s+\K[^;]+' "$conf" | tr ' ' '\n' | sort -u) - domain_args="" - for d in $domains; do - domain_args="$domain_args -d $d" - done - echo "Issuing cert for $cert_name..." - certbot certonly --standalone --non-interactive --agree-tos \ - --register-unsafely-without-email --cert-name "$cert_name" $domain_args - fi - done - trap - EXIT - fi # Test config before applying — a bad config would take down all sites docker compose run --rm -T nginx nginx -t fi