diff --git a/.env.example b/.env.example index b398a7662..8ba3be7d0 100644 --- a/.env.example +++ b/.env.example @@ -361,11 +361,17 @@ API_TOKEN_HMAC_KEY="REPLACE-WITH-OUTPUT-OF-openssl-rand-hex-32" # Auto-deploy webhook (v1.4.15 — phase C2) # ----------------------------------------------------------------------------- # Coolify (or any CI) pings POST /api/internal/deploy-webhook after a deploy -# attempt with success/failure. We verify the request via this shared secret -# carried in the `X-Deploy-Webhook-Secret` header (timing-safe compare). -# Generate with `openssl rand -hex 32`. Configure the SAME value in Coolify's -# Notifications → Webhook custom-headers panel. See docs/audit/v1415-auto-deploy.md. +# attempt with success/failure. A request authenticates with this shared +# secret in the `X-Deploy-Webhook-Secret` header (timing-safe compare; what +# Coolify's stock sender does, with no replay protection), or by signing: +# `X-Deploy-Webhook-Signature: sha256=`, the HMAC-SHA256 under this +# secret of the `X-Deploy-Webhook-Timestamp` value, a dot and the raw body, +# the timestamp within 5 minutes. Generate with `openssl rand -hex 32`. +# Configure the SAME value in Coolify's Notifications → Webhook +# custom-headers panel. See docs/ops/deploy.md. # DEPLOY_WEBHOOK_SECRET="" +# "true" accepts signed requests only; leave unset for Coolify's stock sender. +# DEPLOY_WEBHOOK_REQUIRE_TIMESTAMP="" # ----------------------------------------------------------------------------- diff --git a/docker-compose.yml b/docker-compose.yml index 957177d69..c73d135f1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -299,16 +299,18 @@ x-healthlog-env: &healthlog_env OIDC_SCOPES: "${OIDC_SCOPES:-}" OIDC_BUTTON_LABEL: "${OIDC_BUTTON_LABEL:-}" OIDC_ONLY: "${OIDC_ONLY:-}" - # Deploy-result webhook (optional). DEPLOY_WEBHOOK_SECRET authenticates - # POST /api/internal/deploy-webhook; DEPLOY_LOGS_URL is the - # deployment-dashboard URL (e.g. the Coolify UI) linked in - # deploy-failure notifications. Listed here so operator values in - # .env reach the container (the `environment:` block is a whitelist). + # Deploy-result webhook (optional). POST /api/internal/deploy-webhook + # authenticates either with DEPLOY_WEBHOOK_SECRET in the + # X-Deploy-Webhook-Secret header (what Coolify's stock sender can do; no + # replay protection) or with X-Deploy-Webhook-Signature: sha256=." under the secret>, the timestamp + # within 5 minutes. DEPLOY_WEBHOOK_REQUIRE_TIMESTAMP=true accepts signed + # requests only. DEPLOY_LOGS_URL is the deployment-dashboard URL (e.g. the + # Coolify UI) linked in deploy-failure notifications. Listed here so + # operator values in .env reach the container (the `environment:` block is + # a whitelist). See docs/ops/deploy.md. DEPLOY_WEBHOOK_SECRET: "${DEPLOY_WEBHOOK_SECRET:-}" DEPLOY_LOGS_URL: "${DEPLOY_LOGS_URL:-}" - # Optional replay hardening: when "true", the deploy webhook - # requires a fresh X-Deploy-Webhook-Timestamp header (±5 min). - # See docs/ops/deploy.md. DEPLOY_WEBHOOK_REQUIRE_TIMESTAMP: "${DEPLOY_WEBHOOK_REQUIRE_TIMESTAMP:-}" # TLS leaf SPKI-change alarm baseline (optional). The native client # pins the served TLS leaf; this is the known-good pin set the monitor diff --git a/scripts/env-manifest.json b/scripts/env-manifest.json index 9d7f38d9c..75339c7fd 100644 --- a/scripts/env-manifest.json +++ b/scripts/env-manifest.json @@ -260,11 +260,16 @@ "variables": [ { "name": "DEPLOY_WEBHOOK_SECRET", - "purpose": "Shared secret for the X-Deploy-Webhook-Secret header on POST /api/internal/deploy-webhook. 64 hex chars." + "purpose": "Shared secret of POST /api/internal/deploy-webhook. 64 hex chars. A request authenticates by sending it in the X-Deploy-Webhook-Secret header (Coolify's stock sender; no replay protection), or by signing: X-Deploy-Webhook-Signature: sha256=, the timestamp within 5 minutes." }, { "name": "DEPLOY_LOGS_URL", "purpose": "URL of the deployment dashboard (e.g. the Coolify UI) linked in deploy-failure notifications. Optional; when unset the notification omits the link." + }, + { + "name": "DEPLOY_WEBHOOK_REQUIRE_TIMESTAMP", + "purpose": "\"true\" (or \"1\") accepts only signed deploy-webhook requests (X-Deploy-Webhook-Signature with a fresh X-Deploy-Webhook-Timestamp); the secret header alone is refused with 401. Leave unset for Coolify's stock sender, which can only send the secret header.", + "example": "true" } ] },