gateway: move verifiedProxyTo's 42-line rationale comment to docs/gateway.md
The comment-block lint (added in 79dc8ca6) now trips on this block —
genuinely pre-existing, unrelated to that change, just newly caught.
Per the lint's own suggested remedy: relocated the full per-directive
reasoning plus both footguns (session-cache keying, the Host-header
clobber that can recurse a subrequest into itself) to a new
"Dialing another vhost by name" section in docs/gateway.md, and left
a short why + pointer comment in the source. No behavior change.
This commit is contained in:
parent
8a0ecb307b
commit
c4a573d91d
2 changed files with 52 additions and 41 deletions
|
|
@ -599,3 +599,47 @@ Since the gateway always terminates TLS (see [TLS modes](#tls-modes)
|
|||
above), an enabled HSTS header is always served over https — there is no
|
||||
TLS-less mode that could violate it.
|
||||
|
||||
## Dialing another vhost by name (`verifiedProxyTo`)
|
||||
|
||||
`vhost-lib.nix`'s `verifiedProxyTo` builds the `proxy_ssl_*` /
|
||||
`proxy_set_header` block a module uses to dial another service on this
|
||||
same gateway BY NAME over https, verified. One definition rather than a
|
||||
copy per module: nginx verifies nothing by default
|
||||
(`proxy_ssl_verify` is off), so a `proxy_pass https://…` without these
|
||||
lines is encrypted and unauthenticated. That failure is invisible — it
|
||||
works, and keeps working, against any certificate at all.
|
||||
|
||||
Every line earns its place, each confirmed against a real nginx with
|
||||
the opposite arm run as a control:
|
||||
|
||||
- `verify` + `depth` — the chain is leaf -> intermediate -> root.
|
||||
- `trusted_cert` — the bundle; nginx reads ALL certs in the file, which
|
||||
the bundle's own doc warns is not true of every consumer.
|
||||
- `ssl_name` — checks the HOSTNAME too. Without it a chain-only check
|
||||
accepts any certificate this CA ever signed, and for an internal CA
|
||||
that is every service on the hive.
|
||||
- `server_name on` — sends SNI, or the far end cannot pick a cert.
|
||||
|
||||
**⚠️ Session-cache footgun**: `proxy_ssl_session_reuse` is left at its
|
||||
default (on), deliberately — this is used on per-request auth
|
||||
subrequests, so the handshake it avoids is paid on every request. Worth
|
||||
knowing when testing though: the session cache is keyed by upstream
|
||||
address and NOT by trust config, so two locations pointing at one
|
||||
upstream with different trust do not verify independently.
|
||||
|
||||
**⚠️ Host-header clobber footgun**: `verifiedProxyTo` also pins `Host`
|
||||
(and reinstates the rest of nginx's `recommendedProxySettings` header
|
||||
set) to the target `name` rather than leaving it to be filled in later.
|
||||
`name` here resolves back to THIS gateway — every consumer dials another
|
||||
vhost on the same nginx, not a separate host — and nginx picks the vhost
|
||||
to answer an HTTPS request from the `Host` header, not from the TLS SNI
|
||||
that `proxy_ssl_name` sends. `recommendedProxySettings`'s own `Host
|
||||
$host` (the CALLER's host, not the target) is textually appended by
|
||||
nixpkgs AFTER a location's `extraConfig` — so it always wins over a
|
||||
`proxy_set_header Host` written in the location body, and the subrequest
|
||||
loops back into the ORIGINAL vhost instead of reaching the target,
|
||||
recursing on its own `auth_request` until nginx's subrequest-depth limit
|
||||
turns it into a plain 500. Every call site sets `recommendedProxySettings
|
||||
= false` on the location for exactly this reason — nixpkgs' version
|
||||
would still clobber this one.
|
||||
|
||||
|
|
|
|||
|
|
@ -87,47 +87,14 @@ in
|
|||
vhostTls;
|
||||
|
||||
# Dial another service on this hive BY NAME over https, verified.
|
||||
#
|
||||
# One definition rather than a copy per module: nginx verifies
|
||||
# nothing by default (`proxy_ssl_verify` is off), so a `proxy_pass
|
||||
# https://…` without these lines is encrypted and unauthenticated.
|
||||
# That failure is invisible — it works, and keeps working, against
|
||||
# any certificate at all.
|
||||
#
|
||||
# Every line earns its place, each confirmed against a real nginx
|
||||
# with the opposite arm run as a control:
|
||||
# verify + depth — the chain is leaf -> intermediate -> root
|
||||
# trusted_cert — the bundle; nginx reads ALL certs in the file,
|
||||
# which the bundle's own doc warns is not true of
|
||||
# every consumer
|
||||
# ssl_name — checks the HOSTNAME too. Without it a chain-only
|
||||
# check accepts any certificate this CA ever
|
||||
# signed, and for an internal CA that is every
|
||||
# service on the hive
|
||||
# server_name on — sends SNI, or the far end cannot pick a cert
|
||||
#
|
||||
# ⚠️ `proxy_ssl_session_reuse` is left at its default (on) and that is
|
||||
# deliberate: this is used on per-request auth subrequests, so the
|
||||
# handshake it avoids is paid on every request. Worth knowing when
|
||||
# testing though — the session cache is keyed by upstream address and
|
||||
# NOT by trust config, so two locations pointing at one upstream with
|
||||
# different trust do not verify independently.
|
||||
#
|
||||
# ⚠️ Also pins `Host` (and reinstates the rest of nginx's
|
||||
# `recommendedProxySettings` header set) to `name` rather than leaving
|
||||
# it to be filled in later. `name` here resolves back to THIS gateway
|
||||
# — every consumer dials another vhost on the same nginx, not a
|
||||
# separate host — and nginx picks the vhost to answer an HTTPS request
|
||||
# from the `Host` header, not from the TLS SNI that `proxy_ssl_name`
|
||||
# above sends. `recommendedProxySettings`'s own `Host $host` (the
|
||||
# CALLER's host, not the target) is textually appended by nixpkgs
|
||||
# AFTER a location's `extraConfig` — so it always wins over a
|
||||
# `proxy_set_header Host` written in the location body, and the
|
||||
# subrequest loops back into the ORIGINAL vhost instead of reaching
|
||||
# the target, recursing on its own `auth_request` until nginx's
|
||||
# subrequest-depth limit turns it into a plain 500. Every call site
|
||||
# sets `recommendedProxySettings = false` on the location for exactly
|
||||
# this reason — nixpkgs' version would still clobber this one.
|
||||
# nginx verifies nothing by default (`proxy_ssl_verify` is off), so a
|
||||
# `proxy_pass https://…` without this is encrypted but unauthenticated
|
||||
# — invisibly, it works and keeps working against any certificate at
|
||||
# all. Full reasoning for every directive here (plus two real
|
||||
# footguns — session-cache keying, and a `Host`-header clobber that
|
||||
# can recurse a subrequest into itself) is in docs/gateway.md's
|
||||
# "Dialing another vhost by name" section — read it before touching
|
||||
# this.
|
||||
verifiedProxyTo = name: ''
|
||||
proxy_ssl_verify on;
|
||||
proxy_ssl_verify_depth 3;
|
||||
|
|
|
|||
Loading…
Reference in a new issue