Fix identified issues with server consolidation
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
# Caddyfile — Phase 9 §9.2 proposed form.
|
||||
#
|
||||
# Three changes vs. /etc/caddy/Caddyfile (2026-05-24):
|
||||
# 1. @static matcher now explicitly excludes /uploads/* — without this, an
|
||||
# uploaded *.jpg matched @static before @gated and slipped past the
|
||||
# forward_auth gate, hitting the SPA build root and returning a public 404.
|
||||
# 2. The security_headers snippet no longer sets Access-Control-Allow-* —
|
||||
# the upstreams' shared/cors/policy.js is the single source of truth for
|
||||
# CORS responses (Phase 6.6).
|
||||
# 3. New @cors_preflight handler punts OPTIONS preflights past forward_auth
|
||||
# so the upstream's CORS middleware can answer them (preflights have no
|
||||
# Authorization header, so they 401'd at the gate previously).
|
||||
#
|
||||
# Apply via the staged-cutover convention in Deviation #8:
|
||||
# scp this file to netcup:/home/matt/Caddyfile.new
|
||||
# curl --silent -X POST -H "Content-Type: text/caddyfile" \
|
||||
# --data-binary @/home/matt/Caddyfile.new http://localhost:2020/load
|
||||
# # ...smoke-test, then persist:
|
||||
# sudo cp /etc/caddy/Caddyfile /etc/caddy/Caddyfile.bak.YYYY-MM-DD
|
||||
# sudo cp /home/matt/Caddyfile.new /etc/caddy/Caddyfile
|
||||
{
|
||||
admin :2020
|
||||
}
|
||||
(security_headers) {
|
||||
header {
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
# Phase 9 §9.2: CORS headers removed. Upstreams set ACAO conditionally
|
||||
# via shared/cors/policy.js; Caddy stamping `*` here was overriding it.
|
||||
}
|
||||
}
|
||||
files.acot.site {
|
||||
reverse_proxy localhost:8060
|
||||
}
|
||||
pbx.acot.site {
|
||||
@ws path /ws
|
||||
handle @ws {
|
||||
reverse_proxy 127.0.0.1:8088
|
||||
}
|
||||
handle {
|
||||
reverse_proxy 127.0.0.1:8080 {
|
||||
header_up Host {host}
|
||||
header_down Location http://127.0.0.1:8080 https://pbx.acot.site
|
||||
header_down Location http://pbx.acot.site:8080 https://pbx.acot.site
|
||||
}
|
||||
}
|
||||
}
|
||||
turn.acot.site {
|
||||
respond 404
|
||||
}
|
||||
freescout.acot.site {
|
||||
root * /var/www/freescout/public
|
||||
encode gzip
|
||||
php_fastcgi unix//run/php/php8.3-fpm.sock
|
||||
file_server
|
||||
# Deny access to dotfiles
|
||||
@dotfiles path */.*
|
||||
respond @dotfiles 403
|
||||
}
|
||||
phone.acot.site {
|
||||
reverse_proxy 127.0.0.1:3020
|
||||
encode gzip
|
||||
}
|
||||
crafty.acot.site {
|
||||
reverse_proxy localhost:8443 {
|
||||
header_up X-Forwarded-Proto https
|
||||
header_up X-Forwarded-Port 443
|
||||
header_up Host {host}
|
||||
transport http {
|
||||
tls_insecure_skip_verify
|
||||
}
|
||||
}
|
||||
}
|
||||
cronicle.acot.site {
|
||||
reverse_proxy localhost:3100 {
|
||||
header_up X-Forwarded-Proto https
|
||||
}
|
||||
}
|
||||
inventory.acot.site, acot.site {
|
||||
redir https://tools.acherryontop.com{uri} permanent
|
||||
}
|
||||
tools.acherryontop.com {
|
||||
import security_headers
|
||||
|
||||
# Public: login endpoint
|
||||
handle /auth-inv/* {
|
||||
uri strip_prefix /auth-inv
|
||||
reverse_proxy localhost:3011
|
||||
}
|
||||
|
||||
# Phase 9 §9.2 — CORS preflight bypass.
|
||||
# Browsers send OPTIONS preflights without Authorization, so forward_auth
|
||||
# 401s them. Route preflights straight to the upstream which runs
|
||||
# shared/cors/policy.js and answers correctly. Must come before @static
|
||||
# and @gated so OPTIONS to *.jpg paths under /uploads/* also work if any
|
||||
# frontend ever XHRs an upload URL.
|
||||
@cors_preflight {
|
||||
method OPTIONS
|
||||
header Access-Control-Request-Method *
|
||||
}
|
||||
handle @cors_preflight {
|
||||
handle /api/klaviyo/* {
|
||||
reverse_proxy localhost:3015
|
||||
}
|
||||
handle /api/meta/* {
|
||||
reverse_proxy localhost:3015
|
||||
}
|
||||
handle /api/dashboard-analytics/* {
|
||||
reverse_proxy localhost:3015
|
||||
}
|
||||
handle /api/typeform/* {
|
||||
reverse_proxy localhost:3015
|
||||
}
|
||||
handle /api/acot/* {
|
||||
reverse_proxy localhost:3012
|
||||
}
|
||||
handle /chat-api/* {
|
||||
uri strip_prefix /chat-api
|
||||
reverse_proxy localhost:3014
|
||||
}
|
||||
handle /api/* {
|
||||
reverse_proxy localhost:3010
|
||||
}
|
||||
}
|
||||
|
||||
# Public: static frontend assets (long-cache).
|
||||
# Phase 9 §9.2: `not path /uploads/*` ensures uploaded images never get
|
||||
# served from the SPA build root — they must go through @gated below.
|
||||
@static {
|
||||
path *.js *.css *.png *.jpg *.jpeg *.gif *.ico *.svg *.woff *.woff2
|
||||
not path /uploads/*
|
||||
}
|
||||
handle @static {
|
||||
header Cache-Control "public, max-age=2592000"
|
||||
root * /var/www/inventory/frontend/build
|
||||
file_server
|
||||
}
|
||||
|
||||
# ----- Authenticated zone -----
|
||||
# Phase 6.1: forward_auth subrequest to auth-server:/verify. 2xx → proceeds.
|
||||
# 401/403 → Caddy returns auth-server response to client; backend never sees it.
|
||||
@gated path /api/* /chat-api/* /uploads/*
|
||||
handle @gated {
|
||||
forward_auth localhost:3011 {
|
||||
uri /verify
|
||||
copy_headers Authorization
|
||||
}
|
||||
# Phase 6.7: /uploads/* now behind the gate (was a public file_server before).
|
||||
# Phase 9 §9.2 closes the static-matcher bypass that made this ineffective.
|
||||
handle /uploads/* {
|
||||
root * /var/www/inventory
|
||||
file_server
|
||||
}
|
||||
# Phase 4: merged dashboard-server (klaviyo + meta + google + typeform).
|
||||
handle /api/klaviyo/* {
|
||||
reverse_proxy localhost:3015
|
||||
}
|
||||
handle /api/meta/* {
|
||||
reverse_proxy localhost:3015
|
||||
}
|
||||
handle /api/dashboard-analytics/* {
|
||||
reverse_proxy localhost:3015
|
||||
}
|
||||
handle /api/typeform/* {
|
||||
reverse_proxy localhost:3015
|
||||
}
|
||||
# ACOT
|
||||
handle /api/acot/* {
|
||||
reverse_proxy localhost:3012
|
||||
}
|
||||
# Chat (Phase 9 §9.1 — chat-server now has its own authenticate() too)
|
||||
handle /chat-api/* {
|
||||
uri strip_prefix /chat-api
|
||||
reverse_proxy localhost:3014
|
||||
}
|
||||
# Catch-all: inventory-server
|
||||
handle /api/* {
|
||||
reverse_proxy localhost:3010
|
||||
}
|
||||
}
|
||||
|
||||
# Out-of-band probes (unauthenticated)
|
||||
handle /health {
|
||||
reverse_proxy localhost:3010
|
||||
}
|
||||
|
||||
# SPA fallback (public assets)
|
||||
handle {
|
||||
root * /var/www/inventory/frontend/build
|
||||
try_files {path} /index.html
|
||||
file_server
|
||||
encode gzip
|
||||
}
|
||||
handle_errors {
|
||||
respond "{err.status_code} {err.status_text}"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user