mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-21 06:09:07 +02:00
Wire up json socket as a named pipe with metdata exchange to daemon
This commit is contained in:
+22
-13
@@ -269,18 +269,17 @@ func FlagNameToEnvVar(cmdFlag string, prefix string) string {
|
||||
}
|
||||
|
||||
// DialClientGRPCServer returns client connection to the daemon server.
|
||||
func DialClientGRPCServer(ctx context.Context, addr string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||
defer cancel()
|
||||
|
||||
opts = append([]grpc.DialOption{
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithBlock()}, opts...)
|
||||
|
||||
// The daemon reads the caller's kernel identity from the transport
|
||||
// (SO_PEERCRED on a Unix socket, the client token on a Windows named pipe),
|
||||
// so the client stays insecure. For npipe we install a context dialer since
|
||||
// gRPC's resolver does not understand Windows named pipes.
|
||||
// daemonDialTarget returns the gRPC dial target and base options for the daemon
|
||||
// address, handling the npipe scheme (Windows named pipe, via a context dialer)
|
||||
// and unix/tcp. It sets insecure transport credentials but NOT WithBlock, so it
|
||||
// serves both the blocking CLI dial and the JSON gateway's lazy client.
|
||||
//
|
||||
// The daemon reads the caller's kernel identity from the transport (SO_PEERCRED
|
||||
// on a Unix socket, the client token on a Windows named pipe), so the client
|
||||
// stays insecure. gRPC's resolver does not understand Windows named pipes, hence
|
||||
// the context dialer.
|
||||
func daemonDialTarget(addr string) (string, []grpc.DialOption) {
|
||||
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
|
||||
target := strings.TrimPrefix(addr, "tcp://")
|
||||
if strings.HasPrefix(addr, "npipe://") {
|
||||
path := pipePath(strings.TrimPrefix(addr, "npipe://"))
|
||||
@@ -289,8 +288,18 @@ func DialClientGRPCServer(ctx context.Context, addr string, opts ...grpc.DialOpt
|
||||
}))
|
||||
target = "passthrough:///netbird-daemon-pipe"
|
||||
}
|
||||
return target, opts
|
||||
}
|
||||
|
||||
return grpc.DialContext(ctx, target, opts...)
|
||||
func DialClientGRPCServer(ctx context.Context, addr string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||
defer cancel()
|
||||
|
||||
target, dialOpts := daemonDialTarget(addr)
|
||||
dialOpts = append(dialOpts, grpc.WithBlock())
|
||||
dialOpts = append(dialOpts, opts...)
|
||||
|
||||
return grpc.DialContext(ctx, target, dialOpts...)
|
||||
}
|
||||
|
||||
// WithBackOff execute function in backoff cycle.
|
||||
|
||||
@@ -54,7 +54,7 @@ func init() {
|
||||
serviceCmd.PersistentFlags().BoolVar(&captureEnabled, "enable-capture", false, "Enables packet capture via 'netbird debug capture'. To persist, use: netbird service install --enable-capture")
|
||||
serviceCmd.PersistentFlags().BoolVar(&networksDisabled, "disable-networks", false, "Disables network selection. If enabled, the client will not allow listing, selecting, or deselecting networks. To persist, use: netbird service install --disable-networks")
|
||||
serviceCmd.PersistentFlags().BoolVar(&enableJSONSocket, "enable-json-socket", false, "Enables the HTTP/JSON API socket served by grpc-gateway. To persist, use: netbird service install --enable-json-socket")
|
||||
serviceCmd.PersistentFlags().StringVar(&jsonSocket, "json-socket", defaultJSONSocket, "HTTP/JSON API socket address [unix|tcp]://[path|host:port]. Requires --enable-json-socket to serve. To persist, use: netbird service install --enable-json-socket --json-socket")
|
||||
serviceCmd.PersistentFlags().StringVar(&jsonSocket, "json-socket", defaultJSONSocket, "HTTP/JSON API socket address [unix|tcp|npipe]://[path|host:port|name]. Requires --enable-json-socket to serve. To persist, use: netbird service install --enable-json-socket --json-socket")
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&serviceName, "service", "s", defaultServiceName, "Netbird system service name")
|
||||
serviceEnvDesc := `Sets extra environment variables for the service. ` +
|
||||
|
||||
@@ -121,7 +121,7 @@ func (p *program) Start(svc service.Service) error {
|
||||
p.serverInstanceMu.Unlock()
|
||||
|
||||
if jsonListener != nil {
|
||||
log.Warnf("JSON gateway (--enable-json-socket) re-dials the daemon locally; the HTTP client's identity is forwarded so per-caller authorization still applies, but restrict access to %s appropriately", jsonSocket)
|
||||
log.Warnf("JSON gateway (--enable-json-socket) re-dials the daemon locally. The HTTP client's identity is forwarded so per-caller authorization still applies, but restrict access to %s appropriately", jsonSocket)
|
||||
if err := p.startJSONGateway(jsonListener, daemonAddr); err != nil {
|
||||
log.Fatalf("failed to start daemon JSON server: %v", err)
|
||||
}
|
||||
|
||||
@@ -5,15 +5,14 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/ipcauth"
|
||||
@@ -28,7 +27,7 @@ type jsonPeerCtxKey struct{}
|
||||
// gateway re-dials the daemon as the daemon's own identity, so without this the
|
||||
// daemon would see every JSON request as privileged.
|
||||
func jsonConnContext(ctx context.Context, c net.Conn) context.Context {
|
||||
id, err := ipcauth.PeerIdentity(c)
|
||||
id, err := ipcauth.ConnIdentity(c)
|
||||
if err != nil {
|
||||
log.Debugf("json gateway: cannot read HTTP client identity, requests won't carry it: %v", err)
|
||||
return ctx
|
||||
@@ -47,14 +46,17 @@ func jsonForwardIdentity(ctx context.Context, _ *http.Request) metadata.MD {
|
||||
return ipcauth.ForwardIdentityMetadata(id)
|
||||
}
|
||||
|
||||
func grpcGatewayEndpoint(addr string) string {
|
||||
return strings.TrimPrefix(addr, "tcp://")
|
||||
}
|
||||
|
||||
func (p *program) startJSONGateway(jsonListener *socketListener, daemonEndpoint string) error {
|
||||
mux := runtime.NewServeMux(runtime.WithMetadata(jsonForwardIdentity))
|
||||
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
|
||||
if err := proto.RegisterDaemonServiceHandlerFromEndpoint(p.ctx, mux, grpcGatewayEndpoint(daemonEndpoint), opts); err != nil {
|
||||
|
||||
// Lazy client to the daemon, npipe-aware (grpc.NewClient does not connect
|
||||
// until the first request, so this does not block startup before Serve).
|
||||
target, opts := daemonDialTarget(daemonEndpoint)
|
||||
conn, err := grpc.NewClient(target, opts...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create daemon client for JSON gateway: %w", err)
|
||||
}
|
||||
if err := proto.RegisterDaemonServiceHandler(p.ctx, mux, conn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ func listenOnAddress(addr string) (*socketListener, error) {
|
||||
func parseListenAddress(addr string) (string, string, error) {
|
||||
network, address, ok := strings.Cut(addr, "://")
|
||||
if !ok || network == "" || address == "" {
|
||||
return "", "", fmt.Errorf("address must be in [unix|tcp]://[path|host:port] format: %q", addr)
|
||||
return "", "", fmt.Errorf("address must be in [unix|tcp|npipe]://[path|host:port|name] format: %q", addr)
|
||||
}
|
||||
|
||||
switch network {
|
||||
|
||||
Reference in New Issue
Block a user