Check peers before proxy and expand unit tests

This commit is contained in:
Philip Laine
2026-06-11 12:29:55 +02:00
parent ce126112e9
commit ce5986ccc2
5 changed files with 163 additions and 41 deletions
+11
View File
@@ -15,6 +15,8 @@ require (
github.com/DeRuina/timberjack v1.4.2 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/awnumar/memcall v0.4.0 // indirect
github.com/awnumar/memguard v0.23.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.38.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.31.6 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.10 // indirect
@@ -37,9 +39,11 @@ require (
github.com/coder/websocket v1.8.14 // indirect
github.com/coreos/go-iptables v0.7.0 // indirect
github.com/creack/pty v1.1.24 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gliderlabs/ssh v0.3.8 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect
@@ -53,8 +57,10 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/huin/goupnp v1.2.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kardianos/service v1.2.3-0.20240613133416-becf2eb62b83 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/koron/go-ssdp v0.0.4 // indirect
@@ -68,6 +74,7 @@ require (
github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/mdp/qrterminal/v3 v3.2.1 // indirect
github.com/mholt/acmez/v2 v2.0.1 // indirect
github.com/miekg/dns v1.1.72 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
@@ -93,6 +100,8 @@ require (
github.com/shoenig/go-m1cpu v0.2.1 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/things-go/go-socks5 v0.0.4 // indirect
github.com/ti-mo/conntrack v0.5.1 // indirect
github.com/ti-mo/netfilter v0.5.2 // indirect
@@ -112,6 +121,7 @@ require (
golang.org/x/net v0.53.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/term v0.42.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.43.0 // indirect
@@ -124,6 +134,7 @@ require (
google.golang.org/protobuf v1.36.11 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20260219192049-0f2374377e89 // indirect
rsc.io/qr v0.2.0 // indirect
)
replace github.com/dexidp/dex => github.com/netbirdio/dex v0.244.1-0.20260512110716-8d70ad8647c1
+22
View File
@@ -35,6 +35,10 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/awnumar/memcall v0.4.0 h1:B7hgZYdfH6Ot1Goaz8jGne/7i8xD4taZie/PNSFZ29g=
github.com/awnumar/memcall v0.4.0/go.mod h1:8xOx1YbfyuCg3Fy6TO8DK0kZUua3V42/goA5Ru47E8w=
github.com/awnumar/memguard v0.23.0 h1:sJ3a1/SWlcuKIQ7MV+R9p0Pvo9CWsMbGZvcZQtmc68A=
github.com/awnumar/memguard v0.23.0/go.mod h1:olVofBrsPdITtJ2HgxQKrEYEMyIBAIciVG4wNnZhW9M=
github.com/aws/aws-sdk-go-v2 v1.38.3 h1:B6cV4oxnMs45fql4yRH+/Po/YU+597zgWqvDpYMturk=
github.com/aws/aws-sdk-go-v2 v1.38.3/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
@@ -104,6 +108,7 @@ github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d4
github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -165,6 +170,8 @@ github.com/go-webauthn/webauthn v0.16.4 h1:R9jqR/cYZa7hRquFF7Za/8qoH/K/TIs1/Q/4C
github.com/go-webauthn/webauthn v0.16.4/go.mod h1:SU2ljAgToTV/YLPI0C05QS4qn+e04WpB5g1RMfcZfS4=
github.com/go-webauthn/x v0.2.3 h1:8oArS+Rc1SWFLXhE17KZNx258Z4kUSyaDgsSncCO5RA=
github.com/go-webauthn/x v0.2.3/go.mod h1:tM04GF3V6VYq79AZMl7vbj4q6pz9r7L2criWRzbWhPk=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -231,6 +238,8 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY=
github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
@@ -254,6 +263,8 @@ github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7X
github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/kardianos/service v1.2.3-0.20240613133416-becf2eb62b83 h1:q+kfN+Jh0AAUp1QPSBPxPEQrS5rkFr6b5qilQ6pEfk8=
github.com/kardianos/service v1.2.3-0.20240613133416-becf2eb62b83/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
@@ -298,6 +309,8 @@ github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42 h1:A1Cq6Ysb0GM0
github.com/mdlayher/netlink v1.7.3-0.20250113171957-fbb4dce95f42/go.mod h1:BB4YCPDOzfy7FniQ/lxuYQ3dgmM2cZumHbK8RpTjN2o=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/mdp/qrterminal/v3 v3.2.1 h1:6+yQjiiOsSuXT5n9/m60E54vdgFsw0zhADHhHLrFet4=
github.com/mdp/qrterminal/v3 v3.2.1/go.mod h1:jOTmXvnBsMy5xqLniO0R++Jmjs2sTm9dFSuQ5kpz/SU=
github.com/mholt/acmez/v2 v2.0.1 h1:3/3N0u1pLjMK4sNEAFSI+bcvzbPhRpY383sy1kLHJ6k=
github.com/mholt/acmez/v2 v2.0.1/go.mod h1:fX4c9r5jYwMyMsC+7tkYRxHibkOTgta5DIFGoe67e1U=
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
@@ -426,6 +439,7 @@ github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russellhaering/goxmldsig v1.6.0 h1:8fdWXEPh2k/NZNQBPFNoVfS3JmzS4ZprY/sAOpKQLks=
github.com/russellhaering/goxmldsig v1.6.0/go.mod h1:TrnaquDcYxWXfJrOjeMBTX4mLBeYAqaHEyUeWPxZlBM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
@@ -446,6 +460,10 @@ github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EE
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -531,6 +549,7 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
goauthentik.io/api/v3 v3.2023051.3 h1:NebAhD/TeTWNo/9X3/Uj+rM5fG1HaiLOlKTNLQv9Qq4=
goauthentik.io/api/v3 v3.2023051.3/go.mod h1:nYECml4jGbp/541hj8GcylKQG1gVBsKppHy4+7G8u4U=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -585,6 +604,7 @@ golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -686,3 +706,5 @@ gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
gvisor.dev/gvisor v0.0.0-20260219192049-0f2374377e89 h1:mGJaeA61P8dEHTqdvAgc70ZIV3QoUoJcXCRyyjO26OA=
gvisor.dev/gvisor v0.0.0-20260219192049-0f2374377e89/go.mod h1:QkHjoMIBaYtpVufgwv3keYAbln78mBoCuShZrPrer1Q=
rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=
rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=
+52 -26
View File
@@ -25,6 +25,17 @@ import (
"github.com/netbirdio/netbird/shared/management/http/api"
)
const (
AcceptHeader = "Accept"
AcceptEncodingHeader = "Accept-Encoding"
ContentLengthHeader = "Content-Length"
ContentTypeHeader = "Content-Type"
UserAgentHeader = "User-Agent"
AuthorizationHeader = "Authorization"
ImpersonateUserHeader = "Impersonate-User"
ImpersonateGroupHeader = "Impersonate-Group"
)
type PeerLister interface {
List(ctx context.Context, opts ...netbird.PeersListOption) ([]api.Peer, error)
}
@@ -38,15 +49,8 @@ func Server(embedClient *embed.Client, peerLister PeerLister, kubeAPIServerURL *
if err != nil {
return nil, err
}
handler := proxyHandler(peerLister, kubeAPIServerURL, certPool, bearerToken)
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = &tls.Config{
RootCAs: certPool,
}
proxy := &httputil.ReverseProxy{
Transport: transport,
Rewrite: rewriteHandler(peerLister, kubeAPIServerURL, bearerToken),
}
stat, err := embedClient.Status()
if err != nil {
return nil, err
@@ -60,21 +64,23 @@ func Server(embedClient *embed.Client, peerLister PeerLister, kubeAPIServerURL *
Certificates: []tls.Certificate{proxyCert},
MinVersion: tls.VersionTLS12,
},
Handler: proxy,
Handler: handler,
ReadHeaderTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}
return &srv, nil
}
func rewriteHandler(peerLister PeerLister, kubeAPIServerURL *url.URL, bearerToken string) func(*httputil.ProxyRequest) {
return func(pr *httputil.ProxyRequest) {
func proxyHandler(peerLister PeerLister, kubeAPIServerURL *url.URL, certPool *x509.CertPool, bearerToken string) http.HandlerFunc {
type peerCtxKey struct{}
rewrite := func(pr *httputil.ProxyRequest) {
allowedHeaders := map[string]any{
"Accept": nil,
"Accept-Encoding": nil,
"Content-Length": nil,
"Content-Type": nil,
"User-Agent": nil,
AcceptHeader: nil,
AcceptEncodingHeader: nil,
ContentLengthHeader: nil,
ContentTypeHeader: nil,
UserAgentHeader: nil,
}
for k := range pr.Out.Header {
if _, ok := allowedHeaders[k]; !ok {
@@ -82,27 +88,47 @@ func rewriteHandler(peerLister PeerLister, kubeAPIServerURL *url.URL, bearerToke
}
}
remoteIP, _, err := net.SplitHostPort(pr.In.RemoteAddr)
if err != nil {
peer, ok := pr.In.Context().Value(peerCtxKey{}).(api.Peer)
if !ok {
return
}
listCtx, listCancel := context.WithTimeout(pr.In.Context(), 10*time.Second)
pr.Out.Header.Set(ImpersonateUserHeader, peer.UserId)
for _, group := range peer.Groups {
pr.Out.Header.Add(ImpersonateGroupHeader, group.Name)
}
pr.Out.Header.Set(AuthorizationHeader, "Bearer "+bearerToken)
pr.SetURL(kubeAPIServerURL)
}
transport := http.DefaultTransport.(*http.Transport).Clone()
transport.TLSClientConfig = &tls.Config{
RootCAs: certPool,
MinVersion: tls.VersionTLS12,
}
proxy := &httputil.ReverseProxy{
Transport: transport,
Rewrite: rewrite,
}
return func(rw http.ResponseWriter, req *http.Request) {
remoteIP, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
return
}
listCtx, listCancel := context.WithTimeout(req.Context(), 10*time.Second)
defer listCancel()
peers, err := peerLister.List(listCtx, netbird.PeerIPFilter(remoteIP))
if err != nil {
rw.WriteHeader(http.StatusInternalServerError)
return
}
if len(peers) != 1 {
rw.WriteHeader(http.StatusUnauthorized)
return
}
peer := peers[0]
pr.Out.Header.Set("Impersonate-User", peer.UserId)
for _, group := range peer.Groups {
pr.Out.Header.Add("Impersonate-Group", group.Name)
}
pr.Out.Header.Set("Authorization", "Bearer "+bearerToken)
pr.SetURL(kubeAPIServerURL)
peerCtx := context.WithValue(req.Context(), peerCtxKey{}, peers[0])
proxy.ServeHTTP(rw, req.WithContext(peerCtx))
}
}
+75 -13
View File
@@ -6,10 +6,11 @@ import (
"context"
"crypto/x509"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/http/httputil"
"net/url"
"strings"
"testing"
"github.com/go-openapi/testify/v2/require"
@@ -34,14 +35,14 @@ func (p *mockPeerLister) List(ctx context.Context, opts ...netbird.PeersListOpti
if ip != "" {
peer, ok := p.peers[ip]
if !ok {
return nil, fmt.Errorf("peer with ip %s could not be found", ip)
return nil, nil
}
return []api.Peer{peer}, nil
}
return nil, nil
}
func TestRewriteHandler(t *testing.T) {
func TestProxyHandler(t *testing.T) {
t.Parallel()
peerLister := &mockPeerLister{
@@ -50,27 +51,88 @@ func TestRewriteHandler(t *testing.T) {
UserId: "foo",
Groups: []api.GroupMinimum{
{
Name: "bar",
Name: "group1",
},
{
Name: "group2",
},
},
},
},
}
serverURL, err := url.Parse("https://internal.name")
require.NoError(t, err)
bearerToken := "foobar"
handler := rewriteHandler(peerLister, serverURL, bearerToken)
srv := httptest.NewTLSServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
token, _ := strings.CutPrefix(req.Header.Get("Authorization"), "Bearer ")
if token != bearerToken {
rw.WriteHeader(http.StatusUnauthorized)
return
}
if req.URL.Path != "/version" {
rw.WriteHeader(http.StatusNotFound)
return
}
body := fmt.Sprintf("%s %s %s", req.Header[AuthorizationHeader], req.Header[ImpersonateUserHeader], req.Header[ImpersonateGroupHeader])
// nolint: errcheck
rw.Write([]byte(body))
}))
t.Cleanup(func() {
srv.Close()
})
certPool := srv.Client().Transport.(*http.Transport).TLSClientConfig.RootCAs
kubeAPIServerURL, err := url.Parse(srv.URL)
require.NoError(t, err)
tests := []struct {
name string
remoteAddr string
headers map[string]string
expectedStatus int
expectedBody string
}{
{
name: "valid peer",
headers: nil,
expectedStatus: http.StatusOK,
expectedBody: "[Bearer foobar] [foo] [group1 group2]",
},
{
name: "valid peer with bearer token",
headers: map[string]string{
AuthorizationHeader: "Bearer testtest",
},
expectedStatus: http.StatusOK,
expectedBody: "[Bearer foobar] [foo] [group1 group2]",
},
{
name: "no peer found",
remoteAddr: "192.0.2.2:123",
headers: nil,
expectedStatus: http.StatusUnauthorized,
expectedBody: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/version", nil)
pr := &httputil.ProxyRequest{
In: req,
Out: req.Clone(t.Context()),
if tt.remoteAddr != "" {
req.RemoteAddr = tt.remoteAddr
}
handler(pr)
for k, v := range tt.headers {
req.Header.Add(k, v)
}
rec := httptest.NewRecorder()
handler := proxyHandler(peerLister, kubeAPIServerURL, certPool, bearerToken)
handler(rec, req)
b, err := io.ReadAll(rec.Result().Body)
require.NoError(t, err)
require.EqualT(t, "example.com", pr.In.Host)
require.EqualT(t, "https://internal.name/version", pr.Out.URL.String())
require.EqualT(t, tt.expectedStatus, rec.Result().StatusCode)
require.EqualT(t, tt.expectedBody, string(b))
})
}
}
func TestGenerateSelfSignedCert(t *testing.T) {
+2 -1
View File
@@ -15,6 +15,7 @@ import (
"golang.org/x/sync/errgroup"
"github.com/netbirdio/netbird/client/cmd"
"github.com/netbirdio/netbird/client/embed"
netbird "github.com/netbirdio/netbird/shared/management/client/rest"
@@ -67,7 +68,7 @@ func run(ctx context.Context, kubeAPIServer, mgmtURL, apiKey, setupKey, instance
SetupKey: setupKey,
DeviceName: instanceName,
LogOutput: io.Discard,
DNSLabels: []string{clusterName + "." + "netbird-kubeapi-proxy"},
DNSLabels: []string{clusterName + "." + cmd.KubernetesDNSSuffix},
}
embedClient, err := embed.New(opts)
if err != nil {