[client,signal,management] Add browser client support (#4415)

This commit is contained in:
Viktor Liu
2025-10-01 20:10:11 +02:00
committed by GitHub
parent 5e1a40c33f
commit b5daec3b51
107 changed files with 3591 additions and 284 deletions

View File

@@ -8,14 +8,16 @@ import (
"fmt"
"net"
"net/http"
// nolint:gosec
_ "net/http/pprof"
"strings"
"net/netip"
"time"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel/metric"
"golang.org/x/crypto/acme/autocert"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"github.com/netbirdio/netbird/signal/metrics"
@@ -23,6 +25,8 @@ import (
"github.com/netbirdio/netbird/shared/signal/proto"
"github.com/netbirdio/netbird/signal/server"
"github.com/netbirdio/netbird/util"
"github.com/netbirdio/netbird/util/wsproxy"
wsproxyserver "github.com/netbirdio/netbird/util/wsproxy/server"
"github.com/netbirdio/netbird/version"
log "github.com/sirupsen/logrus"
@@ -32,6 +36,8 @@ import (
"google.golang.org/grpc/keepalive"
)
const legacyGRPCPort = 10000
var (
signalPort int
metricsPort int
@@ -113,7 +119,7 @@ var (
}
proto.RegisterSignalExchangeServer(grpcServer, srv)
grpcRootHandler := grpcHandlerFunc(grpcServer)
grpcRootHandler := grpcHandlerFunc(grpcServer, metricsServer.Meter)
if certManager != nil {
startServerWithCertManager(certManager, grpcRootHandler)
@@ -123,19 +129,30 @@ var (
var grpcListener net.Listener
var httpListener net.Listener
// If certManager is configured and signalPort == 443, then the gRPC server has already been started
if certManager == nil || signalPort != 443 {
grpcListener, err = serveGRPC(grpcServer, signalPort)
// Start the main server - always serve HTTP with WebSocket proxy support
// If certManager is configured and signalPort == 443, it's already handled by startServerWithCertManager
if certManager == nil {
// Without TLS, serve plain HTTP
httpListener, err = net.Listen("tcp", fmt.Sprintf(":%d", signalPort))
if err != nil {
return err
}
log.Infof("running gRPC server: %s", grpcListener.Addr().String())
log.Infof("running HTTP server with WebSocket proxy (no TLS): %s", httpListener.Addr().String())
serveHTTP(httpListener, grpcRootHandler)
} else if signalPort != 443 {
// With TLS but not on port 443, serve HTTPS
httpListener, err = tls.Listen("tcp", fmt.Sprintf(":%d", signalPort), certManager.TLSConfig())
if err != nil {
return err
}
log.Infof("running HTTPS server with WebSocket proxy: %s", httpListener.Addr().String())
serveHTTP(httpListener, grpcRootHandler)
}
if signalPort != 10000 {
if signalPort != legacyGRPCPort {
// The Signal gRPC server was running on port 10000 previously. Old agents that are already connected to Signal
// are using port 10000. For compatibility purposes we keep running a 2nd gRPC server on port 10000.
compatListener, err = serveGRPC(grpcServer, 10000)
compatListener, err = serveGRPC(grpcServer, legacyGRPCPort)
if err != nil {
return err
}
@@ -236,11 +253,14 @@ func startServerWithCertManager(certManager *autocert.Manager, grpcRootHandler h
}
}
func grpcHandlerFunc(grpcServer *grpc.Server) http.Handler {
func grpcHandlerFunc(grpcServer *grpc.Server, meter metric.Meter) http.Handler {
wsProxy := wsproxyserver.New(netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), legacyGRPCPort), wsproxyserver.WithOTelMeter(meter))
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
grpcHeader := strings.HasPrefix(r.Header.Get("Content-Type"), "application/grpc") ||
strings.HasPrefix(r.Header.Get("Content-Type"), "application/grpc+proto")
if r.ProtoMajor == 2 && grpcHeader {
switch {
case r.URL.Path == wsproxy.ProxyPath:
wsProxy.Handler().ServeHTTP(w, r)
default:
grpcServer.ServeHTTP(w, r)
}
})
@@ -257,7 +277,11 @@ func notifyStop(msg string) {
func serveHTTP(httpListener net.Listener, handler http.Handler) {
go func() {
err := http.Serve(httpListener, handler)
// Use h2c to support HTTP/2 without TLS (needed for gRPC)
h1s := &http.Server{
Handler: h2c.NewHandler(handler, &http2.Server{}),
}
err := h1s.Serve(httpListener)
if err != nil {
notifyStop(fmt.Sprintf("failed running HTTP server %v", err))
}