diff --git a/Makefile b/Makefile index 57ddb64..6304463 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,9 @@ SECOND_DOMAIN ?= ruakb.online SECOND_WWW_DOMAIN ?= www.ruakb.online LETSENCRYPT_EMAIL ?= admin@ruakb.ru AUTO_CERT_INIT ?= 0 +SKIP_LOCAL_SMOKE ?= 0 +LOCAL_SMOKE_BASE_URL ?= https://127.0.0.1 +LOCAL_SMOKE_CANDIDATES ?= $(LOCAL_SMOKE_BASE_URL),https://localhost,http://127.0.0.1,http://localhost CONFIRM_TOKEN ?= ROTATE-PROD-SECRETS CERTBOT_DOMAINS = -d "$(DOMAIN)" -d "$(WWW_DOMAIN)" $(if $(strip $(SECOND_DOMAIN)),-d "$(SECOND_DOMAIN)") $(if $(strip $(SECOND_WWW_DOMAIN)),-d "$(SECOND_WWW_DOMAIN)") @@ -52,6 +55,9 @@ help: @echo " SECOND_DOMAIN=$(SECOND_DOMAIN)" @echo " SECOND_WWW_DOMAIN=$(SECOND_WWW_DOMAIN)" @echo " AUTO_CERT_INIT=$(AUTO_CERT_INIT)" + @echo " SKIP_LOCAL_SMOKE=$(SKIP_LOCAL_SMOKE)" + @echo " LOCAL_SMOKE_BASE_URL=$(LOCAL_SMOKE_BASE_URL)" + @echo " LOCAL_SMOKE_CANDIDATES=$(LOCAL_SMOKE_CANDIDATES)" local-up: $(LOCAL_COMPOSE) up -d --build @@ -119,6 +125,9 @@ prod-security-audit: check-cert-files SECOND_WWW_DOMAIN="$(SECOND_WWW_DOMAIN)" \ LETSENCRYPT_EMAIL="$(LETSENCRYPT_EMAIL)" \ AUTO_CERT_INIT="$(AUTO_CERT_INIT)" \ + SKIP_LOCAL_SMOKE="$(SKIP_LOCAL_SMOKE)" \ + LOCAL_SMOKE_BASE_URL="$(LOCAL_SMOKE_BASE_URL)" \ + LOCAL_SMOKE_CANDIDATES="$(LOCAL_SMOKE_CANDIDATES)" \ ./scripts/ops/prod_security_audit.sh rotate-encryption-kid: diff --git a/README.md b/README.md index ca48c0e..5c30ba5 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,22 @@ Optional: auto-bootstrap Let's Encrypt certs if HTTPS health is failing: make prod-security-audit AUTO_CERT_INIT=1 DOMAIN=ruakb.ru WWW_DOMAIN=www.ruakb.ru SECOND_DOMAIN=ruakb.online SECOND_WWW_DOMAIN=www.ruakb.online LETSENCRYPT_EMAIL=you@example.com ``` +If localhost loopback probes are not desired on production host: +```bash +make prod-security-audit SKIP_LOCAL_SMOKE=1 DOMAIN=ruakb.ru WWW_DOMAIN=www.ruakb.ru SECOND_DOMAIN=ruakb.online SECOND_WWW_DOMAIN=www.ruakb.online LETSENCRYPT_EMAIL=you@example.com +``` + +By default local smoke probes now try multiple loopback endpoints: +- `https://127.0.0.1` +- `https://localhost` +- `http://127.0.0.1` +- `http://localhost` + +You can override: +```bash +make prod-security-audit LOCAL_SMOKE_CANDIDATES="https://127.0.0.1,http://127.0.0.1" +``` + ## Container health and alerting Docker Compose is configured with: - `restart: unless-stopped` for core services diff --git a/scripts/ops/check_chat_health.sh b/scripts/ops/check_chat_health.sh index 33a57ff..9c97b7f 100755 --- a/scripts/ops/check_chat_health.sh +++ b/scripts/ops/check_chat_health.sh @@ -8,7 +8,7 @@ EMAIL_HEALTH_URL="${BASE_URL%/}/email-health" check_http_200() { url="$1" - code="$(curl -L -sS -o /dev/null -w "%{http_code}" "$url" || true)" + code="$(curl -k -L -sS -o /dev/null -w "%{http_code}" "$url" || true)" [ "$code" = "200" ] } diff --git a/scripts/ops/prod_security_audit.sh b/scripts/ops/prod_security_audit.sh index 12ea0c8..c7fc7c5 100755 --- a/scripts/ops/prod_security_audit.sh +++ b/scripts/ops/prod_security_audit.sh @@ -10,6 +10,9 @@ SECOND_DOMAIN="${SECOND_DOMAIN:-ruakb.online}" SECOND_WWW_DOMAIN="${SECOND_WWW_DOMAIN:-www.ruakb.online}" LETSENCRYPT_EMAIL="${LETSENCRYPT_EMAIL:-admin@ruakb.ru}" AUTO_CERT_INIT="${AUTO_CERT_INIT:-0}" +SKIP_LOCAL_SMOKE="${SKIP_LOCAL_SMOKE:-0}" +LOCAL_SMOKE_BASE_URL="${LOCAL_SMOKE_BASE_URL:-https://127.0.0.1}" +LOCAL_SMOKE_CANDIDATES="${LOCAL_SMOKE_CANDIDATES:-${LOCAL_SMOKE_BASE_URL},https://localhost,http://127.0.0.1,http://localhost}" PROD_COMPOSE=(docker compose -f docker-compose.yml -f docker-compose.prod.nginx.yml) CERT_COMPOSE=(docker compose -f docker-compose.yml -f docker-compose.prod.nginx.yml -f docker-compose.prod.cert.yml) @@ -92,15 +95,34 @@ PY } run_local_smoke() { - log "Running local smoke checks via localhost" + if [[ "$SKIP_LOCAL_SMOKE" == "1" ]]; then + log "Skipping local smoke checks (SKIP_LOCAL_SMOKE=1)" + return 0 + fi + + log "Running local smoke checks (candidates: ${LOCAL_SMOKE_CANDIDATES})" local max_attempts="${LOCAL_SMOKE_MAX_ATTEMPTS:-24}" local sleep_seconds="${LOCAL_SMOKE_SLEEP_SECONDS:-5}" local attempt=1 + local candidate + local ok=0 while (( attempt <= max_attempts )); do - if ./scripts/ops/check_chat_health.sh http://localhost >/dev/null 2>&1 && \ - ./scripts/ops/security_smoke.sh http://localhost >/dev/null 2>&1; then - log "Local smoke checks passed (attempt ${attempt}/${max_attempts})" + ok=0 + IFS=',' read -r -a _urls <<< "$LOCAL_SMOKE_CANDIDATES" + for candidate in "${_urls[@]}"; do + candidate="$(echo "$candidate" | xargs)" + [[ -z "$candidate" ]] && continue + + if ./scripts/ops/check_chat_health.sh "$candidate" >/dev/null 2>&1 && \ + ./scripts/ops/security_smoke.sh "$candidate" >/dev/null 2>&1; then + log "Local smoke checks passed via ${candidate} (attempt ${attempt}/${max_attempts})" + ok=1 + break + fi + done + + if [[ "$ok" == "1" ]]; then return 0 fi @@ -109,7 +131,7 @@ run_local_smoke() { attempt=$((attempt + 1)) done - fail "Local smoke checks failed after ${max_attempts} attempts" + fail "Local smoke checks failed after ${max_attempts} attempts (candidates: ${LOCAL_SMOKE_CANDIDATES})" } run_domain_quick_health_wait() {