hscontrol: gate proxy header trust on trusted_proxies
chi middleware.RealIP was mounted unconditionally on both the
public router and the noise router, so any client could send
X-Real-IP or X-Forwarded-For and have the spoofed value land in
r.RemoteAddr and the access-log remote= field.
Add a top-level trusted_proxies config option (list of CIDRs) and
replace middleware.RealIP with a gated middleware that:
- honours True-Client-IP / X-Real-IP / X-Forwarded-For only when
r.RemoteAddr is inside one of the configured prefixes;
- strips those three headers from every request whose peer is
not trusted, so downstream handlers cannot read them.
X-Forwarded-For is parsed via realclientip-go's
RightmostTrustedRangeStrategy so a prepended value cannot win in a
proxy chain. trustedProxies() rejects 0.0.0.0/0 and ::/0 at config
load.
Empty trusted_proxies (the default) skips the mount entirely;
r.RemoteAddr is the directly-connecting TCP peer.
This commit is contained in:
parent
1f48ebb376
commit
c6c29c05e5
6 changed files with 511 additions and 2 deletions
|
|
@ -99,6 +99,10 @@ type Headscale struct {
|
|||
|
||||
DERPServer *derpServer.DERPServer
|
||||
|
||||
// realIPMiddleware is nil when cfg.TrustedProxies is empty; the
|
||||
// router skips the mount and r.RemoteAddr stays as the TCP peer.
|
||||
realIPMiddleware func(http.Handler) http.Handler
|
||||
|
||||
// Things that generate changes
|
||||
extraRecordMan *dns.ExtraRecordsMan
|
||||
authProvider AuthProvider
|
||||
|
|
@ -140,6 +144,13 @@ func NewHeadscale(cfg *types.Config) (*Headscale, error) {
|
|||
state: s,
|
||||
}
|
||||
|
||||
if len(cfg.TrustedProxies) > 0 {
|
||||
app.realIPMiddleware, err = trustedProxyRealIP(cfg.TrustedProxies)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building trusted_proxies middleware: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize ephemeral garbage collector
|
||||
ephemeralGC := db.NewEphemeralGarbageCollector(func(ni types.NodeID) {
|
||||
node, ok := app.state.GetNodeByID(ni)
|
||||
|
|
@ -512,7 +523,11 @@ func (h *Headscale) createRouter(grpcMux *grpcRuntime.ServeMux) *chi.Mux {
|
|||
},
|
||||
}))
|
||||
r.Use(middleware.RequestID)
|
||||
r.Use(middleware.RealIP)
|
||||
|
||||
if h.realIPMiddleware != nil {
|
||||
r.Use(h.realIPMiddleware)
|
||||
}
|
||||
|
||||
r.Use(middleware.RequestLogger(&zerologRequestLogger{}))
|
||||
r.Use(middleware.Recoverer)
|
||||
r.Use(securityHeaders)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue