#!/usr/bin/env bash # The wildcard TLS cert for the gateway. # # Usage: # ./certs.sh status # SANs issued vs SANs the services need # ./certs.sh verify # inspect the cert served on :443 # ./certs.sh renew # refuses: issues a real cert # ./certs.sh push # refuses: ships to a live gateway set -euo pipefail cd "$(dirname "$0")" source ./lib/config.sh source ./lib/estate.sh load_config status() { local issued; issued=$(estate_get "certs.issued" | python3 -c 'import json,sys try: print("\n".join(json.load(sys.stdin))) except Exception: pass') echo "issued SANs (estate/${ESTATE}.json: certs.issued):" echo "$issued" | sed 's/^/ /' echo echo "SANs the services NEED (derived from services[]):" estate_sans | sed 's/^/ /' echo local missing=0 s while IFS= read -r s; do [ -z "$s" ] && continue grep -qxF "$s" <<< "$issued" || { echo "MISSING: $s"; missing=1; } done < <(estate_sans) [ "$missing" = 0 ] && echo "the issued cert covers every derived name." echo echo "certbot image: $(eval echo "\$$CERTBOT_IMAGE_VAR") provider: $DNS_PROVIDER" } verify() { echo "would run:" echo " echo | openssl s_client -connect ${DOMAIN}:443 -servername ${DOMAIN} 2>/dev/null \\" echo " | openssl x509 -noout -dates -ext subjectAltName" echo echo "read-only against a live host — announce and approve first (§7)." } refuse() { echo "REFUSING: '$1' acts on a live cert and a live gateway." >&2 echo " renew issues a real Let's Encrypt cert (rate-limited)." >&2 echo " push rsyncs to the gateway and reloads nginx." >&2 echo " Neither runs without explicit approval." >&2 exit 1 } case "${1:-status}" in status) status ;; verify) verify ;; renew|push) refuse "$1" ;; *) echo "usage: $0 [status|verify|renew|push]" >&2; exit 1 ;; esac