#!/usr/bin/env bash # # Attach one customer domain to this server and issue a TLS certificate. # Idempotent: re-running for an existing domain renews/repairs rather than duplicates. # Run as root, AFTER the domain's A/AAAA record already resolves to this server. # # bash add-domain.sh shop.example.com --email ops@example.com # bash add-domain.sh shop.example.com --email ops@example.com --with-www # # Why per-domain blocks exist at all: the application is multi-tenant off the # Host header and needs no per-domain root. Certificates are the exception — # certbot must match a concrete server_name, which `default_server _` is not. set -euo pipefail DOMAIN="${1:-}"; shift || true EMAIL="" WITH_WWW=0 while [[ $# -gt 0 ]]; do case "$1" in --email) EMAIL="$2"; shift 2 ;; --with-www) WITH_WWW=1; shift ;; *) echo "unknown argument: $1" >&2; exit 2 ;; esac done [[ $EUID -eq 0 ]] || { echo "must run as root" >&2; exit 1; } [[ -n "$DOMAIN" ]] || { echo "usage: add-domain.sh --email
[--with-www]" >&2; exit 2; } [[ -n "$EMAIL" ]] || { echo "--email is required (certbot expiry notices)" >&2; exit 2; } # Fail loudly rather than let certbot fail obscurely on an unpointed domain. echo "==> checking DNS for $DOMAIN" RESOLVED="$(getent hosts "$DOMAIN" | awk '{print $1}' | head -1 || true)" if [[ -z "$RESOLVED" ]]; then echo "ERROR: $DOMAIN does not resolve. Point its A record at this server first." >&2 exit 1 fi echo " resolves to $RESOLVED" NAMES="$DOMAIN" CERT_ARGS=(-d "$DOMAIN") if [[ $WITH_WWW -eq 1 ]]; then NAMES="$DOMAIN www.$DOMAIN" CERT_ARGS+=(-d "www.$DOMAIN") fi CONF="/etc/nginx/sites-available/tenant-$DOMAIN.conf" echo "==> nginx server block: $CONF" cat > "$CONF" < certificate" # --nginx rewrites the block above in place to add listen 443 + ssl directives # and an HTTP->HTTPS redirect. Re-running is a no-op when the cert is current. certbot --nginx "${CERT_ARGS[@]}" \ --non-interactive --agree-tos --email "$EMAIL" \ --redirect --keep-until-expiring nginx -t systemctl reload nginx echo "==> companion API domain(s)" CONFIGURE_API="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/configure-api-domain.sh" [[ -x "$CONFIGURE_API" ]] || { echo "ERROR: configure-api-domain.sh must be executable and next to add-domain.sh" >&2 exit 1 } "$CONFIGURE_API" --domain "$DOMAIN" --email "$EMAIL" if [[ $WITH_WWW -eq 1 ]]; then "$CONFIGURE_API" --domain "www.$DOMAIN" --email "$EMAIL" fi echo "==> renewal timer" systemctl enable --now certbot.timer systemctl status certbot.timer --no-pager | head -3 || true echo echo "done. verify:" echo " curl -I https://$DOMAIN/health" echo " certbot certificates | grep -A3 $DOMAIN"