[management] Handle empty trusted peer (#7589)

This commit is contained in:
Bethuel Mmbaga
2026-09-18 18:21:34 +03:00
committed by GitHub
parent f8c3e565f3
commit 7d8f4fa31c
4 changed files with 23 additions and 19 deletions
@@ -808,9 +808,8 @@ server:
# Trust X-Forwarded-* only from the Traefik container's static address. Both
# keys must stay in step with the ipv4_address pinned in docker-compose.yml:
# trustedPeers decides whether forwarded headers are read at all. Leaving it
# unset trusts nothing and records Traefik's own address as every peer's
# connection IP.
# trustedPeers restricts which sources may supply forwarded headers. Leaving
# it unset trusts all IPv4 and IPv6 sources.
reverseProxy:
trustedPeers:
- "${TRAEFIK_IP}/32"
+3 -3
View File
@@ -586,9 +586,9 @@ configure_reverse_proxy() {
TRUSTED_PEERS="${NETBIRD_TRUSTED_PEERS:-}"
if [[ -z "$TRUSTED_PEERS" ]]; then
echo "" > /dev/stderr
echo "Note: reverseProxy.trustedPeers is unset, so NetBird will use the address your" > /dev/stderr
echo "proxy connects from as each peer's connection IP. To record real client IPs," > /dev/stderr
echo "set NETBIRD_TRUSTED_PEERS to your proxy's address (e.g. 172.20.0.5/32) and re-run." > /dev/stderr
echo "Warning: reverseProxy.trustedPeers is unset, so all IPv4 and IPv6 sources" > /dev/stderr
echo "are trusted to provide forwarded client-IP headers. Set NETBIRD_TRUSTED_PEERS" > /dev/stderr
echo "to your proxy's address (e.g. 172.20.0.5/32) and re-run." > /dev/stderr
echo "" > /dev/stderr
fi
fi
+14 -10
View File
@@ -366,20 +366,24 @@ func streamInterceptor(
return handler(srv, wrapped)
}
// realIPOptions builds the real-IP middleware options from the reverse proxy config.
// realIPOptions builds the real-IP middleware options.
//
// TrustedPeers controls which transport peers are allowed to supply forwarded-IP
// headers. If empty, forwarded headers are ignored and the transport peer address
// is used directly. Operators terminating connections at a reverse proxy should
// configure TrustedPeers with that proxy's address or network.
// Empty TrustedPeers trusts all IPv4 and IPv6 sources. Configure TrustedPeers
// with the reverse proxy address or network.
//
// X-Forwarded-For is consulted first. X-Real-IP is read when X-Forwarded-For is
// absent or has no entries left after TrustedHTTPProxiesCount is applied.
// X-Forwarded-For takes precedence over X-Real-IP.
func realIPOptions(cfg nbconfig.ReverseProxy) []realip.Option {
if idx := slices.IndexFunc(cfg.TrustedPeers, func(p netip.Prefix) bool { return p.Bits() == 0 }); idx >= 0 {
trustedPeers := cfg.TrustedPeers
if len(trustedPeers) == 0 {
trustedPeers = []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
}
}
if idx := slices.IndexFunc(trustedPeers, func(p netip.Prefix) bool { return p.Bits() == 0 }); idx >= 0 {
log.WithContext(context.Background()).Warnf("TrustedPeers contains the default route %s, which trusts "+
"X-Forwarded-For from every client and allows connection IP spoofing. Set TrustedPeers to the address "+
"of your reverse proxy, or leave it empty to use the connection's source address.", cfg.TrustedPeers[idx])
"of your reverse proxy.", trustedPeers[idx])
}
if cfg.TrustedHTTPProxiesCount > 0 {
log.WithContext(context.Background()).Warn(
@@ -389,7 +393,7 @@ func realIPOptions(cfg nbconfig.ReverseProxy) []realip.Option {
}
return []realip.Option{
realip.WithTrustedPeers(cfg.TrustedPeers),
realip.WithTrustedPeers(trustedPeers),
realip.WithTrustedProxies(cfg.TrustedHTTPProxies),
realip.WithTrustedProxiesCount(cfg.TrustedHTTPProxiesCount),
realip.WithHeaders([]string{realip.XForwardedFor, realip.XRealIp}),
+4 -3
View File
@@ -9,13 +9,14 @@ import (
"time"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/realip"
nbconfig "github.com/netbirdio/netbird/management/internals/server/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
"google.golang.org/protobuf/types/known/emptypb"
nbconfig "github.com/netbirdio/netbird/management/internals/server/config"
)
const (
@@ -135,8 +136,8 @@ func assertRealIP(t *testing.T, cfg nbconfig.ReverseProxy, want string, kv ...st
})
}
func TestRealIPDefaultIgnoresClientForwardedHeaders(t *testing.T) {
assertRealIP(t, nbconfig.ReverseProxy{}, "127.0.0.1",
func TestRealIPDefaultTrustsForwardedHeaders(t *testing.T) {
assertRealIP(t, nbconfig.ReverseProxy{}, "203.0.113.44",
realip.XForwardedFor, "203.0.113.44",
realip.XRealIp, "203.0.113.44",
)