Skip to content

Commit fae2478

Browse files
committed
fix(setup): two-phase nginx config to break certbot chicken-and-egg
setup-broker-host.sh wrote a full :80+:443 nginx site up front, but the :443 block referenced LE cert files that don't exist yet. nginx then refused to start, and `certbot --nginx` aborted at its preflight `nginx -t` check — operators saw: cannot load certificate "/etc/letsencrypt/live/<host>/fullchain.pem": BIO_new_file() failed ... No such file or directory Switch to a two-phase config: • Phase A (no cert): :80-only with the ACME challenge location. • Phase B (cert exists): adds the :443 ssl block with proxy_pass. The script detects the cert file at /etc/letsencrypt/live/$ISSUER_HOST/ and writes the right config; re-running after `certbot certonly --webroot` flips A → B automatically. The post-run summary now points at `certbot certonly --webroot` (which works while nginx is up on :80) instead of the broken `--nginx` flow.
1 parent 4592e48 commit fae2478

1 file changed

Lines changed: 72 additions & 13 deletions

File tree

scripts/setup-broker-host.sh

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -497,13 +497,21 @@ WantedBy=multi-user.target
497497
EOF
498498

499499
# ─── 6. nginx (optional) ──────────────────────────────────────────────────────
500-
if [[ "$WITH_NGINX" == "yes" ]]; then
501-
if ! have nginx; then
502-
log "Installing nginx"
503-
"${PM_INSTALL[@]}" nginx
504-
fi
505-
log "Writing nginx site for $ISSUER_HOST"
506-
sudo tee /etc/nginx/sites-available/agentkeys-broker >/dev/null <<EOF
500+
# Two-phase nginx config to avoid the certbot ↔ nginx chicken-and-egg:
501+
# nginx will not start if its config references LE cert files that don't
502+
# exist yet, but `certbot --nginx` runs `nginx -t` before issuing — so
503+
# the first run must produce a config nginx can load *without* a cert.
504+
#
505+
# Phase A (no cert yet): :80-only with the ACME challenge location.
506+
# Operator issues the cert via webroot mode.
507+
# Phase B (cert exists): adds the :443 server block with proxy_pass.
508+
#
509+
# Re-running this script after issuance flips A → B automatically.
510+
write_nginx_site() {
511+
local cert_path="/etc/letsencrypt/live/$ISSUER_HOST/fullchain.pem"
512+
if sudo test -f "$cert_path"; then
513+
log "Writing nginx site for $ISSUER_HOST (HTTPS — LE cert detected)"
514+
sudo tee /etc/nginx/sites-available/agentkeys-broker >/dev/null <<EOF
507515
server {
508516
listen 80;
509517
server_name $ISSUER_HOST;
@@ -515,7 +523,6 @@ server {
515523
listen 443 ssl http2;
516524
server_name $ISSUER_HOST;
517525
518-
# certbot will fill these in when you run \`sudo certbot --nginx\` (Step 9).
519526
ssl_certificate /etc/letsencrypt/live/$ISSUER_HOST/fullchain.pem;
520527
ssl_certificate_key /etc/letsencrypt/live/$ISSUER_HOST/privkey.pem;
521528
ssl_protocols TLSv1.2 TLSv1.3;
@@ -530,10 +537,43 @@ server {
530537
}
531538
}
532539
EOF
540+
else
541+
log "Writing nginx site for $ISSUER_HOST (HTTP-only — no LE cert yet)"
542+
log "After issuing a cert, re-run this script to flip on TLS."
543+
sudo tee /etc/nginx/sites-available/agentkeys-broker >/dev/null <<EOF
544+
# HTTP-only initial config. To issue the Let's Encrypt cert:
545+
# sudo certbot certonly --webroot -w /var/www/certbot -d $ISSUER_HOST \\
546+
# --agree-tos -m <ops@your.org> --non-interactive
547+
# then re-run scripts/setup-broker-host.sh to flip on the :443 block.
548+
server {
549+
listen 80;
550+
server_name $ISSUER_HOST;
551+
location /.well-known/acme-challenge/ { root /var/www/certbot; }
552+
location / {
553+
return 503 "TLS cert not yet issued — see setup-broker-host.sh\n";
554+
default_type text/plain;
555+
}
556+
}
557+
EOF
558+
fi
559+
}
560+
561+
if [[ "$WITH_NGINX" == "yes" ]]; then
562+
if ! have nginx; then
563+
log "Installing nginx"
564+
"${PM_INSTALL[@]}" nginx
565+
fi
566+
sudo install -d -m 0755 /var/www/certbot
567+
write_nginx_site
533568
if [[ -d /etc/nginx/sites-enabled ]]; then
534569
sudo ln -sf /etc/nginx/sites-available/agentkeys-broker /etc/nginx/sites-enabled/
570+
sudo rm -f /etc/nginx/sites-enabled/default
571+
fi
572+
if sudo nginx -t; then
573+
sudo systemctl reload nginx 2>/dev/null || sudo systemctl restart nginx
574+
else
575+
warn "nginx -t failed — leaving service in current state. Inspect /etc/nginx/sites-available/agentkeys-broker."
535576
fi
536-
sudo install -d -m 0755 /var/www/certbot
537577
fi
538578

539579
# ─── 7. certbot (optional) ────────────────────────────────────────────────────
@@ -617,12 +657,31 @@ cat <<EOF
617657
EOF
618658

619659
if [[ "$WITH_NGINX" == "yes" ]]; then
620-
cat <<EOF
621-
TLS:
622-
sudo certbot --nginx -d $ISSUER_HOST --agree-tos -m <ops@your.org>
623-
sudo nginx -t && sudo systemctl reload nginx
660+
if sudo test -f "/etc/letsencrypt/live/$ISSUER_HOST/fullchain.pem"; then
661+
cat <<EOF
662+
TLS: cert already issued — nginx is serving HTTPS.
663+
sudo certbot renew --dry-run # verify auto-renewal is wired
624664
625665
EOF
666+
else
667+
cat <<EOF
668+
TLS: nginx is HTTP-only until the cert is issued.
669+
1. Confirm DNS resolves to this host's public IP:
670+
dig +short $ISSUER_HOST @1.1.1.1
671+
2. Confirm port 80 is reachable from anywhere (security group + host firewall).
672+
3. Issue the cert via webroot mode (works while nginx serves :80):
673+
sudo certbot certonly --webroot -w /var/www/certbot -d $ISSUER_HOST \\
674+
--agree-tos -m <ops@your.org> --non-interactive
675+
4. Re-run this script to flip on the :443 block:
676+
bash scripts/setup-broker-host.sh
677+
5. Verify renewal:
678+
sudo certbot renew --dry-run
679+
680+
Note: do NOT use \`certbot --nginx\` for the first issuance — its preflight
681+
\`nginx -t\` will fail because the :443 ssl block doesn't exist until step 4.
682+
683+
EOF
684+
fi
626685
fi
627686

628687
cat <<EOF

0 commit comments

Comments
 (0)