Gate hostselection=any to public destinations and a port allow-list (#188)

The `roundrobin`, `signed`, and `unsigned` host-selection modes route
requests against an operator-curated `Server.Hosts` list. The `any`
mode does not -- it forwards to whatever `?host=` value the request
carries, which makes the gateway usable as a generic TCP relay
against whatever the gateway can reach (loopback, RFC1918, link-local,
the cloud metadata service, arbitrary high-numbered ports on public
hosts).

Add a small destination policy applied only in `any` mode:

* Reject hosts that resolve to loopback, RFC1918, IPv6 ULA,
  link-local, unspecified, or multicast addresses. Operators can opt
  back in with `Server.AllowPrivateDestinations: true`.
* Restrict the destination port to `Server.AllowedDestinationPorts`
  (default {3389}).

The other host-selection modes are unaffected -- the operator already
curates their hosts list.

The DestinationPolicy zero value is the secure default, so direct
&Handler{} constructions in tests still get the expected behavior.
DNS names are resolved at validation time and every returned address
is checked.
This commit is contained in:
bolkedebruin
2026-04-30 18:42:24 +02:00
committed by GitHub
parent 49fa170023
commit 449cd1e2fe
8 changed files with 260 additions and 10 deletions

View File

@@ -166,8 +166,18 @@ Server:
# - roundrobin, which selects a random host from the list (default)
# - signed, a listed host specified in the signed query parameter
# - unsigned, a listed host specified in the query parameter
# - any, insecurely allow any host specified in the query parameter
# - any, allow any host specified in the query parameter, gated by the
# AllowedDestinationPorts / AllowPrivateDestinations options below.
HostSelection: roundrobin
# When HostSelection: any, only these TCP ports may be forwarded to.
# Empty defaults to [3389]. Ignored for the curated host modes
# (roundrobin, signed, unsigned).
# AllowedDestinationPorts:
# - 3389
# When HostSelection: any, refuse to forward to loopback / RFC1918 /
# link-local / IPv6 ULA / unspecified / multicast destinations unless
# this is true. Default false. Has no effect on the curated host modes.
# AllowPrivateDestinations: false
# a random strings of at least 32 characters to secure cookies on the client
# make sure to share this across the different pods
SessionKey: thisisasessionkeyreplacethisjetzt