fleet-manager: point acme DNS-01 propagation check at a public resolver
Split-horizon DNS made lego's propagation check query the container's internal resolver, which doesn't serve the public _acme-challenge TXT record, so the challenge timed out. Add ACME_RESOLVERS (comma-separated host:port, default 8.8.8.8:53) → one --dns.resolvers flag each. Documented in .env + DEPLOY.md.
This commit is contained in:
@@ -31,6 +31,10 @@ AWS_REGION=us-east-1
|
|||||||
# Optional but recommended: pin the Route 53 zone. Skips ListHostedZonesByName
|
# Optional but recommended: pin the Route 53 zone. Skips ListHostedZonesByName
|
||||||
# (so you can drop it from the IAM policy) and disambiguates public/private zones.
|
# (so you can drop it from the IAM policy) and disambiguates public/private zones.
|
||||||
AWS_HOSTED_ZONE_ID=
|
AWS_HOSTED_ZONE_ID=
|
||||||
|
# Public resolver(s) for the DNS-01 propagation check (comma-separated host:port).
|
||||||
|
# Needed because internal/split-horizon DNS won't resolve the public challenge
|
||||||
|
# record. Defaults to Google DNS; override if 8.8.8.8 is blocked (e.g. 1.1.1.1:53).
|
||||||
|
ACME_RESOLVERS=8.8.8.8:53
|
||||||
# ACME_STAGING=1 # uncomment for a dry-run vs LE staging (UNTRUSTED certs, no rate limit)
|
# ACME_STAGING=1 # uncomment for a dry-run vs LE staging (UNTRUSTED certs, no rate limit)
|
||||||
|
|
||||||
# --- agent plane (gRPC + mTLS, fleet CA) ---------------------------------------
|
# --- agent plane (gRPC + mTLS, fleet CA) ---------------------------------------
|
||||||
|
|||||||
@@ -151,6 +151,12 @@ A publicly-trusted cert also keeps the WebAuthn secure-context clean (no browser
|
|||||||
warnings). The side-car keeps renewing on its 12-hour loop; the manager reloads
|
warnings). The side-car keeps renewing on its 12-hour loop; the manager reloads
|
||||||
each new cert automatically.
|
each new cert automatically.
|
||||||
|
|
||||||
|
> **Split-horizon DNS:** the side-car defaults `ACME_RESOLVERS=8.8.8.8:53` so
|
||||||
|
> lego checks TXT propagation against **public** DNS. Without it, lego uses the
|
||||||
|
> container's internal resolver, which doesn't serve the public
|
||||||
|
> `_acme-challenge` record, and the challenge times out. Override if 8.8.8.8 is
|
||||||
|
> blocked (e.g. `1.1.1.1:53`, comma-separated for several).
|
||||||
|
|
||||||
> **Origin match:** the cert CN/SAN (`FLEET_MGR_RP_ID`) must equal the origin the
|
> **Origin match:** the cert CN/SAN (`FLEET_MGR_RP_ID`) must equal the origin the
|
||||||
> browser uses *and* `FLEET_MGR_RP_ORIGIN` — passkeys are origin-bound. The admin
|
> browser uses *and* `FLEET_MGR_RP_ORIGIN` — passkeys are origin-bound. The admin
|
||||||
> listener binds loopback (`127.0.0.1:9500`) by default; expose it as
|
> listener binds loopback (`127.0.0.1:9500`) by default; expose it as
|
||||||
|
|||||||
@@ -32,6 +32,17 @@ LEGO=$(command -v lego 2>/dev/null || true)
|
|||||||
[ -z "$LEGO" ] && [ -x /lego ] && LEGO=/lego
|
[ -z "$LEGO" ] && [ -x /lego ] && LEGO=/lego
|
||||||
[ -z "$LEGO" ] && { echo "acme: FATAL lego binary not found (looked on PATH and /lego)"; exit 1; }
|
[ -z "$LEGO" ] && { echo "acme: FATAL lego binary not found (looked on PATH and /lego)"; exit 1; }
|
||||||
|
|
||||||
|
# Force the DNS-01 propagation check against a PUBLIC resolver — the container's
|
||||||
|
# internal/split-horizon DNS doesn't serve the public _acme-challenge record, so
|
||||||
|
# lego's default (system resolver) never sees the TXT and times out. Accepts a
|
||||||
|
# comma-separated list of host:port; each becomes a --dns.resolvers flag.
|
||||||
|
RESOLVERS=""
|
||||||
|
if [ -n "${ACME_RESOLVERS:-}" ]; then
|
||||||
|
OLDIFS=$IFS; IFS=','
|
||||||
|
for r in $ACME_RESOLVERS; do RESOLVERS="$RESOLVERS --dns.resolvers $r"; done
|
||||||
|
IFS=$OLDIFS
|
||||||
|
fi
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
# First pass issues; later passes renew when near expiry.
|
# First pass issues; later passes renew when near expiry.
|
||||||
if [ -f "/le/certificates/$DOMAIN.crt" ]; then
|
if [ -f "/le/certificates/$DOMAIN.crt" ]; then
|
||||||
@@ -41,9 +52,9 @@ while true; do
|
|||||||
fi
|
fi
|
||||||
echo "acme: lego $* for $DOMAIN (Route 53 DNS-01)"
|
echo "acme: lego $* for $DOMAIN (Route 53 DNS-01)"
|
||||||
|
|
||||||
# shellcheck disable=SC2086 # $STAGING is intentionally two words when set
|
# shellcheck disable=SC2086 # $STAGING/$RESOLVERS intentionally word-split
|
||||||
if "$LEGO" --accept-tos --email "$EMAIL" --dns route53 --domains "$DOMAIN" \
|
if "$LEGO" --accept-tos --email "$EMAIL" --dns route53 --domains "$DOMAIN" \
|
||||||
--path /le $STAGING "$@"; then
|
--path /le $RESOLVERS $STAGING "$@"; then
|
||||||
install -m 644 "/le/certificates/$DOMAIN.crt" /out/fullchain.pem
|
install -m 644 "/le/certificates/$DOMAIN.crt" /out/fullchain.pem
|
||||||
install -m 600 "/le/certificates/$DOMAIN.key" /out/privkey.pem
|
install -m 600 "/le/certificates/$DOMAIN.key" /out/privkey.pem
|
||||||
chown "$UID_OUT:$UID_OUT" /out/fullchain.pem /out/privkey.pem 2>/dev/null \
|
chown "$UID_OUT:$UID_OUT" /out/fullchain.pem /out/privkey.pem 2>/dev/null \
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ services:
|
|||||||
ACME_DOMAIN: "${FLEET_MGR_RP_ID:?set FLEET_MGR_RP_ID in .env}" # = the UI hostname
|
ACME_DOMAIN: "${FLEET_MGR_RP_ID:?set FLEET_MGR_RP_ID in .env}" # = the UI hostname
|
||||||
ACME_EMAIL: "${ACME_EMAIL:?set ACME_EMAIL in .env}"
|
ACME_EMAIL: "${ACME_EMAIL:?set ACME_EMAIL in .env}"
|
||||||
ACME_STAGING: "${ACME_STAGING:-}" # set 1 for a dry-run
|
ACME_STAGING: "${ACME_STAGING:-}" # set 1 for a dry-run
|
||||||
|
# Public resolver(s) for the propagation check — the internal/split-horizon
|
||||||
|
# DNS doesn't serve the public _acme-challenge record. Comma-separated host:port.
|
||||||
|
ACME_RESOLVERS: "${ACME_RESOLVERS:-8.8.8.8:53}"
|
||||||
# AWS creds for the Route 53 DNS-01 challenge (or omit + use an instance role).
|
# AWS creds for the Route 53 DNS-01 challenge (or omit + use an instance role).
|
||||||
AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID:-}"
|
AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID:-}"
|
||||||
AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY:-}"
|
AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY:-}"
|
||||||
|
|||||||
Reference in New Issue
Block a user