From 96214654d0680e3b564c5ec6f6035bae154b6f80 Mon Sep 17 00:00:00 2001 From: S Date: Mon, 2 Mar 2026 22:06:27 -0600 Subject: [PATCH] feat: add recommended Caddyfile and update usage guide for production configuration --- setup/nginx-to-caddy/CUTOVER_CHECKLIST.md | 1 + setup/nginx-to-caddy/Caddyfile.recommended | 130 +++++++++++++++++++++ setup/nginx-to-caddy/README.md | 3 + setup/nginx-to-caddy/USAGE_GUIDE.md | 26 ++++- setup/nginx-to-caddy/validate_caddy.sh | 20 ++-- 5 files changed, 168 insertions(+), 12 deletions(-) create mode 100644 setup/nginx-to-caddy/Caddyfile.recommended diff --git a/setup/nginx-to-caddy/CUTOVER_CHECKLIST.md b/setup/nginx-to-caddy/CUTOVER_CHECKLIST.md index 422ca38..3a4ecce 100644 --- a/setup/nginx-to-caddy/CUTOVER_CHECKLIST.md +++ b/setup/nginx-to-caddy/CUTOVER_CHECKLIST.md @@ -3,6 +3,7 @@ ## Pre-cutover - [ ] `nginx -T` snapshot captured (`output/nginx-full.conf`) - [ ] Generated Caddyfile reviewed +- [ ] `Caddyfile.recommended` reviewed/adapted for your domains - [ ] `conversion-warnings.txt` reviewed and resolved for canary site - [ ] `validate_caddy.sh` passes - [ ] DNS TTL lowered for canary domain diff --git a/setup/nginx-to-caddy/Caddyfile.recommended b/setup/nginx-to-caddy/Caddyfile.recommended new file mode 100644 index 0000000..62f359e --- /dev/null +++ b/setup/nginx-to-caddy/Caddyfile.recommended @@ -0,0 +1,130 @@ +# Recommended Caddy baseline for the current homelab reverse-proxy estate. +# Source upstreams were derived from setup/nginx-to-caddy/oldconfig/*.conf. +# +# If your public suffix changes (for example sintheus.com -> privacyindesign.com), +# update the hostnames below before deployment. +{ + # DNS-01 certificates through Cloudflare. + # Requires CF_API_TOKEN in Caddy runtime environment. + acme_dns cloudflare {env.CF_API_TOKEN} + + # Trust private-range proxy hops in LAN environments. + servers { + trusted_proxies static private_ranges + protocols h1 h2 h3 + } +} + +(common_security) { + encode zstd gzip + + header { + Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" + X-Content-Type-Options "nosniff" + X-Frame-Options "SAMEORIGIN" + Referrer-Policy "strict-origin-when-cross-origin" + -Server + } +} + +(proxy_headers) { + # Keep Nginx parity for backends that consume Host and X-Real-IP. + header_up Host {host} + header_up X-Real-IP {remote_host} +} + +(proxy_streaming) { + import proxy_headers + # Flush immediately for streaming/log-tail/websocket-heavy UIs. + flush_interval -1 +} + +ai.sintheus.com { + import common_security + + request_body { + max_size 50MB + } + + reverse_proxy http://192.168.1.82:8181 { + import proxy_streaming + } +} + +photos.sintheus.com { + import common_security + + request_body { + max_size 50GB + } + + reverse_proxy http://192.168.1.222:2283 { + import proxy_headers + } +} + +fin.sintheus.com { + import common_security + + reverse_proxy http://192.168.1.233:8096 { + import proxy_streaming + } +} + +disk.sintheus.com { + import common_security + + request_body { + max_size 20GB + } + + reverse_proxy http://192.168.1.52:80 { + import proxy_headers + } +} + +pi.sintheus.com { + import common_security + + reverse_proxy http://192.168.1.4:80 { + import proxy_headers + } +} + +plex.sintheus.com { + import common_security + + reverse_proxy http://192.168.1.111:32400 { + import proxy_streaming + } +} + +sync.sintheus.com { + import common_security + + reverse_proxy http://192.168.1.119:8384 { + import proxy_headers + } +} + +syno.sintheus.com { + import common_security + + reverse_proxy https://100.108.182.16:5001 { + import proxy_headers + transport http { + tls_insecure_skip_verify + } + } +} + +tower.sintheus.com { + import common_security + + reverse_proxy https://192.168.1.82:443 { + import proxy_headers + transport http { + tls_insecure_skip_verify + } + } +} diff --git a/setup/nginx-to-caddy/README.md b/setup/nginx-to-caddy/README.md index 3dd6282..5731393 100644 --- a/setup/nginx-to-caddy/README.md +++ b/setup/nginx-to-caddy/README.md @@ -13,6 +13,8 @@ This module is intentionally conservative: - SSH into a host and collect `nginx -T`, `/etc/nginx` tarball, and a quick inventory summary. - `nginx_to_caddy.sh` - Convert basic Nginx server blocks into a generated Caddyfile. +- `Caddyfile.recommended` + - Hardened baseline config (security headers, sensible body limits, streaming behavior). - `validate_caddy.sh` - Run `caddy fmt`, `caddy adapt`, and `caddy validate` on the generated Caddyfile. @@ -24,6 +26,7 @@ cd setup/nginx-to-caddy ./extract_nginx_inventory.sh --host= --user= --port=22 --yes ./nginx_to_caddy.sh --input=./output/nginx-full.conf --output=./output/Caddyfile.generated --tls-mode=cloudflare --yes ./validate_caddy.sh --config=./output/Caddyfile.generated --docker +./validate_caddy.sh --config=./Caddyfile.recommended --docker ``` ## Conversion Scope diff --git a/setup/nginx-to-caddy/USAGE_GUIDE.md b/setup/nginx-to-caddy/USAGE_GUIDE.md index b1ad316..a9dee59 100644 --- a/setup/nginx-to-caddy/USAGE_GUIDE.md +++ b/setup/nginx-to-caddy/USAGE_GUIDE.md @@ -51,7 +51,23 @@ If local `caddy` is installed: ./validate_caddy.sh --config=./output/Caddyfile.generated ``` -## 4) Canary migration (recommended) +## 4) Use the recommended baseline + +This toolkit now includes a hardened baseline at: +- `setup/nginx-to-caddy/Caddyfile.recommended` + +Use it when you want a production-style config instead of a raw 1:1 conversion. +You can either: +1. use it directly (if hostnames/upstreams already match your environment), or +2. copy its common snippets and service patterns into your live Caddyfile. + +Validate it before deployment: + +```bash +./validate_caddy.sh --config=./Caddyfile.recommended --docker +``` + +## 5) Canary migration (recommended) Migrate one low-risk subdomain first: 1. Copy only one site block from generated Caddyfile to your live Caddy config. @@ -62,7 +78,7 @@ Migrate one low-risk subdomain first: - API/websocket calls work 4. Keep Nginx serving all other subdomains. -## 5) Full migration after canary success +## 6) Full migration after canary success When the canary is stable: 1. Add remaining site blocks. @@ -70,14 +86,14 @@ When the canary is stable: 3. Keep Nginx config snapshots for rollback. 4. Decommission Nginx only after monitoring period. -## 6) Rollback plan +## 7) Rollback plan If a site fails after cutover: 1. Repoint affected DNS entry back to Nginx endpoint. 2. Restore previous Nginx server block. 3. Investigate conversion warnings for that block. -## 7) Domain/TLS note for your current setup +## 8) Domain/TLS note for your current setup You confirmed the domain is `privacyindesign.com`. @@ -86,7 +102,7 @@ If you use `TLS_MODE=cloudflare` with Caddy, ensure: - Cloudflare token has DNS edit on the same zone. - DNS records point to the Caddy ingress path you intend (direct or via edge proxy). -## 8) Suggested next step for Phase 8 +## 9) Suggested next step for Phase 8 Given your current repo config: - keep Phase 8 Caddy focused on `source.privacyindesign.com` diff --git a/setup/nginx-to-caddy/validate_caddy.sh b/setup/nginx-to-caddy/validate_caddy.sh index 0022f7e..bb4b02d 100755 --- a/setup/nginx-to-caddy/validate_caddy.sh +++ b/setup/nginx-to-caddy/validate_caddy.sh @@ -49,35 +49,41 @@ fi CONFIG_FILE="$(cd "$(dirname "$CONFIG_FILE")" && pwd)/$(basename "$CONFIG_FILE")" +docker_env_args=() + if [[ "$USE_DOCKER" == "true" ]]; then require_cmd docker - docker_env_args=() if [[ -n "${CF_API_TOKEN:-}" ]]; then docker_env_args+=( -e "CF_API_TOKEN=${CF_API_TOKEN}" ) elif [[ -n "${CLOUDFLARE_API_TOKEN:-}" ]]; then docker_env_args+=( -e "CF_API_TOKEN=${CLOUDFLARE_API_TOKEN}" ) fi + run_docker_caddy() { + if [[ "${#docker_env_args[@]}" -gt 0 ]]; then + docker run --rm "${docker_env_args[@]}" "$@" + else + docker run --rm "$@" + fi + } + if [[ "$FORMAT_FILE" == "true" ]]; then log_info "Formatting Caddyfile with Docker..." - docker run --rm \ - "${docker_env_args[@]}" \ + run_docker_caddy \ -v "$CONFIG_FILE:/etc/caddy/Caddyfile" \ "$CADDY_IMAGE" caddy fmt --overwrite /etc/caddy/Caddyfile fi if [[ "$DO_ADAPT" == "true" ]]; then log_info "Adapting Caddyfile (Docker)..." - docker run --rm \ - "${docker_env_args[@]}" \ + run_docker_caddy \ -v "$CONFIG_FILE:/etc/caddy/Caddyfile:ro" \ "$CADDY_IMAGE" caddy adapt --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null fi if [[ "$DO_VALIDATE" == "true" ]]; then log_info "Validating Caddyfile (Docker)..." - docker run --rm \ - "${docker_env_args[@]}" \ + run_docker_caddy \ -v "$CONFIG_FILE:/etc/caddy/Caddyfile:ro" \ "$CADDY_IMAGE" caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile fi