Files
proxy/ops/nginx_conf/proxy.conf
T
wmantly 6092468901 Add per-host reverse-proxy controls (rate limit, cache, headers, IP ACL)
Every proxied request flows through one shared OpenResty location whose
behavior is chosen at request time from the host's Redis hash. Add per-host
controls as new Host fields enforced in Lua rather than static nginx config
(which can't key off a per-request variable):

- Rate limiting: per-client-IP token bucket via resty.limit.req
  (ratelimit_enabled/rate/burst), backed by a new `ratelimit` shared dict.
- Response caching: opt-in per host via a global proxy_cache zone gated by
  $skip_cache (respcache_enabled). Off by default; upstream Cache-Control
  still honored.
- Custom/security headers: req_headers (upstream) + resp_headers (client) and
  hsts_enabled, applied in access/header_filter phases.
- IP allow/deny CIDR lists via resty.ipmatcher (deny wins; non-empty allow is
  default-deny).

New ops/nginx_conf/hostfeatures.lua holds the enforcement; proxy.conf's
access_by_lua string becomes a block that calls it, plus a header_filter block.
nodejs/utils/host_features.js is the pure, unit-tested normalize/validate layer
(header/CIDR parsing, range clamping, injection-safe values) applied in
routes/host.js and mirrored by the hosts.ejs edit form. install.sh gains the
ipmatcher rock, the cache dir, and the hostfeatures.lua symlink.

Per-host cache TTL is intentionally deferred (global default only) — see the
plan's limitations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 22:10:55 -04:00

91 lines
2.3 KiB
Plaintext

map $http_upgrade $connection_upgrade {
default Upgrade;
'' close;
}
server {
listen 80;
include autossl.conf;
set_real_ip_from 192.168.1.0/24;
real_ip_header X-Real-IP;
real_ip_recursive on;
location / {
set $target '';
set $target_scheme 'http';
set $target_port '';
set $header_host $host;
set $skip_cache 1;
access_by_lua_block {
local targetInfo = require "targetinfo"
local hostfeatures = require "hostfeatures"
local host = ngx.var.host
local uri = ngx.var.uri
local scheme = ngx.var.scheme
local res = targetInfo.get(ngx, host, ngx.ctx.targetInfo)
if scheme == "http" then
if res["forcessl"] == "true" then
return ngx.redirect("https://"..host..uri, 301)
end
end
if res["targetssl"] == "true" then
ngx.var.target_scheme = "https"
end
if res["host-pass-though"] == "false" then
ngx.var.header_host = res["ip"]
end
ngx.var.target = res["ip"]
ngx.var.target_port = res["targetPort"]
-- Per-host controls: IP allow/deny, rate limit, upstream headers, and
-- the $skip_cache gate. May ngx.exit() (403/429).
hostfeatures.access(ngx, res)
}
header_filter_by_lua_block {
require("hostfeatures").header(ngx)
}
resolver 192.168.1.1 ipv6=off; #8.8.4.4; # use Google's open DNS server
proxy_http_version 1.1;
proxy_pass_request_headers on;
# Response cache. Opt-in per host: $skip_cache is 0 only when the Host
# record sets respcache_enabled. Global default TTL lives in nginx.conf;
# upstream Cache-Control (private/no-store) is still honored.
proxy_cache proxycache;
proxy_cache_key $scheme$host$request_uri;
proxy_cache_bypass $skip_cache;
proxy_no_cache $skip_cache;
proxy_pass $target_scheme://$target:$target_port;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_ssl_session_reuse on;
proxy_intercept_errors off;
proxy_set_header Host $header_host;
add_header X-Target-Host $target;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $target_scheme;
proxy_set_header Referer $target_scheme://$header_host;
proxy_set_header Accept-Language $http_accept_language;
proxy_set_header User-Agent $http_user_agent;
sub_filter $target $host;
sub_filter_once off;
}
}