Commit Graph

4 Commits

Author SHA1 Message Date
bolkedebruin
de31bfe8a0 Restrict the rdpgw-auth socket to its own UID by default (#190)
The auth daemon's gRPC socket was world-writable and accepted any
local UID that could connect to it. On a multi-tenant host any user
on the box could speak the gRPC API and run an arbitrary username/
password through PAM -- effectively an unauthenticated PAM oracle.

Create the socket with mode 0660 (Umask(0117)) and gate Accept on
SO_PEERCRED: only the daemon's own UID is allowed by default, plus
any operator-supplied --allow-uid / --allow-gid. Privilege-separated
deployments (rdpgw and rdpgw-auth as different users) need to list
the gateway's UID, or share a group; the existing path otherwise
would have been permissive.

The peer-credentials check is Linux-only; the non-Linux build keeps
the listener as-is and logs a warning, since rdpgw-auth itself
requires libpam and is effectively Linux-only in practice.
2026-04-30 18:59:48 +02:00
bolkedebruin
13323f56cb Honor X-Forwarded-For only from a trusted-proxy CIDR (#189)
EnrichContext used to copy the first X-Forwarded-For entry into the
request identity unconditionally. The resulting AttrClientIp drives
client-IP comparisons later in the gateway-access flow, and a direct
caller could set XFF to anything they liked.

Add a small package-level allow-list:

* InitTrustedProxies(cidrs) parses operator-supplied CIDRs once at
  startup. A bad CIDR is fatal, an empty list disables XFF entirely.
* EnrichContext takes the client IP from r.RemoteAddr (host portion)
  and only swaps in the first X-Forwarded-For entry when r.RemoteAddr
  itself sits in a trusted-proxy CIDR. AttrProxies is set from the
  remaining XFF entries on the same condition.

Wire Server.TrustedProxies through configuration.go to web.
2026-04-30 18:47:46 +02:00
bolkedebruin
449cd1e2fe 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.
2026-04-30 18:42:24 +02:00
Bolke de Bruin
6b32631434 Finalize rdp templating 2023-05-15 10:43:38 +02:00