refactor: switch nginx to Caddy with automatic Let's Encrypt
Build Admin / Build image (push) Successful in 13s
Build Admin / Build image (push) Successful in 13s
Nginx required manually managed certs on port 8443; TLS was effectively
unused. Caddy issues and renews certificates for DOMAIN on its own.
- Caddyfile replaces nginx.conf + locations.conf; upstreams are one
`import api <prefix> <host> <path-prefix>` line each
- routes wrapped in `route {}` so guards run before handle_path
- access control (IP allowlist, X-Proxy-Token) generated from env into
matchers.caddy / guard.caddy by the entrypoint, as before
- listeners: 443 (+udp, HTTP/3) and 80 for ACME on DOMAIN, plus the
unchanged plain-HTTP 8082 -> 8080 path for existing clients
- caddy_data volume added: losing it re-issues certs and burns LE limits
- scripts/migrate-host-nginx.sh backs up and retires the host nginx
- deploy.sh points at the real prod layout (/srv/elevenlabs, compose v1)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
15 files changed
+289
-466
No files matched your search
+42
-72
@@ -1,87 +1,57 @@
|
||||
#!/bin/sh
|
||||
# proxy-vm/docker-entrypoint.sh
|
||||
# Generates nginx config fragments from environment variables at container start.
|
||||
# elevenlabs.io/docker-entrypoint.sh
|
||||
# Generates Caddy access-control fragments from environment variables at container start.
|
||||
set -e
|
||||
|
||||
CONF_DIR="/etc/nginx/conf.d"
|
||||
mkdir -p "${CONF_DIR}"
|
||||
CONF_DIR="/etc/caddy"
|
||||
MATCHERS="${CONF_DIR}/matchers.caddy"
|
||||
GUARD="${CONF_DIR}/guard.caddy"
|
||||
|
||||
# --- 1. Generate IP allowlist (geo block) ---
|
||||
ALLOWLIST_FILE="${CONF_DIR}/allowlist.conf"
|
||||
: > "${MATCHERS}"
|
||||
: > "${GUARD}"
|
||||
|
||||
if [ -z "${ALLOWED_CIDR}" ]; then
|
||||
cat > "${ALLOWLIST_FILE}" <<'GEO'
|
||||
geo $allowed_ip {
|
||||
default 1;
|
||||
}
|
||||
GEO
|
||||
echo "[entrypoint] ALLOWED_CIDR is empty — allowing all IPs (dev mode)"
|
||||
else
|
||||
{
|
||||
echo 'geo $allowed_ip {'
|
||||
echo ' default 0;'
|
||||
echo "${ALLOWED_CIDR}" | tr ',' '\n' | while read -r cidr; do
|
||||
cidr=$(echo "${cidr}" | xargs)
|
||||
[ -n "${cidr}" ] && echo " ${cidr} 1;"
|
||||
done
|
||||
echo '}'
|
||||
} > "${ALLOWLIST_FILE}"
|
||||
echo "[entrypoint] IP allowlist configured: ${ALLOWED_CIDR}"
|
||||
# --- 0. Required settings ---
|
||||
if [ -z "${DOMAIN}" ]; then
|
||||
echo "[entrypoint] FATAL: DOMAIN is not set (e.g. proxy.example.com)" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${ACME_EMAIL}" ]; then
|
||||
echo "[entrypoint] FATAL: ACME_EMAIL is not set — Let's Encrypt account needs it" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- 2. Generate token auth (map block) ---
|
||||
AUTH_FILE="${CONF_DIR}/auth.conf"
|
||||
# --- 1. IP allowlist ---
|
||||
if [ -z "${ALLOWED_CIDR}" ]; then
|
||||
echo "[entrypoint] ALLOWED_CIDR is empty — allowing all IPs (dev mode)"
|
||||
else
|
||||
CIDR_LIST=$(echo "${ALLOWED_CIDR}" | tr ',' ' ' | tr -s ' ')
|
||||
cat >> "${MATCHERS}" <<MATCH
|
||||
@denied_ip not remote_ip ${CIDR_LIST}
|
||||
MATCH
|
||||
cat >> "${GUARD}" <<GUARDRULE
|
||||
respond @denied_ip \`{"error":"ip_not_allowed"}\` 403 {
|
||||
close
|
||||
}
|
||||
GUARDRULE
|
||||
echo "[entrypoint] IP allowlist configured: ${CIDR_LIST}"
|
||||
fi
|
||||
|
||||
# --- 2. Token auth ---
|
||||
if [ -z "${PROXY_SECRET}" ]; then
|
||||
echo "[entrypoint] PROXY_SECRET is not set — token auth disabled (open mode)"
|
||||
cat > "${AUTH_FILE}" <<'MAP'
|
||||
map $http_x_proxy_token $auth_ok {
|
||||
default 1;
|
||||
}
|
||||
MAP
|
||||
else
|
||||
cat > "${AUTH_FILE}" <<MAP
|
||||
map \$http_x_proxy_token \$auth_ok {
|
||||
default 0;
|
||||
"${PROXY_SECRET}" 1;
|
||||
cat >> "${MATCHERS}" <<MATCH
|
||||
@bad_token not header X-Proxy-Token "${PROXY_SECRET}"
|
||||
MATCH
|
||||
cat >> "${GUARD}" <<GUARDRULE
|
||||
respond @bad_token \`{"error":"invalid_token"}\` 403 {
|
||||
close
|
||||
}
|
||||
MAP
|
||||
GUARDRULE
|
||||
echo "[entrypoint] Token auth configured"
|
||||
fi
|
||||
|
||||
# --- 3. Copy locations.conf ---
|
||||
cp /etc/nginx/locations.conf "${CONF_DIR}/locations.conf"
|
||||
echo "[entrypoint] Locations config copied"
|
||||
|
||||
# --- 4. Generate HTTPS server block (if certs exist) ---
|
||||
HTTPS_FILE="${CONF_DIR}/https_server.conf"
|
||||
SSL_CERT="/etc/nginx/ssl/tls.crt"
|
||||
SSL_KEY="/etc/nginx/ssl/tls.key"
|
||||
|
||||
if [ -f "${SSL_CERT}" ] && [ -f "${SSL_KEY}" ]; then
|
||||
cat > "${HTTPS_FILE}" <<HTTPS
|
||||
server {
|
||||
listen 8443 ssl;
|
||||
server_name _;
|
||||
|
||||
ssl_certificate ${SSL_CERT};
|
||||
ssl_certificate_key ${SSL_KEY};
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
|
||||
include /etc/nginx/conf.d/locations.conf;
|
||||
}
|
||||
HTTPS
|
||||
echo "[entrypoint] HTTPS enabled on port 8443 (cert: ${SSL_CERT})"
|
||||
else
|
||||
# Empty file so nginx include does not fail
|
||||
: > "${HTTPS_FILE}"
|
||||
echo "[entrypoint] No TLS certs found at ${SSL_CERT} — HTTPS disabled"
|
||||
fi
|
||||
|
||||
# --- 5. Start nginx ---
|
||||
echo "[entrypoint] Starting nginx..."
|
||||
exec nginx -g 'daemon off;'
|
||||
# --- 3. Validate and start ---
|
||||
caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
echo "[entrypoint] Starting caddy (domain: ${DOMAIN})..."
|
||||
exec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
Reference in new issue
Block a user