mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-12 17:59:06 +02:00
WIP: add session ownership
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/ipcauth"
|
||||
"github.com/netbirdio/netbird/client/server"
|
||||
)
|
||||
|
||||
@@ -45,6 +46,7 @@ type program struct {
|
||||
jsonServMu sync.Mutex
|
||||
serverInstance *server.Server
|
||||
serverInstanceMu sync.Mutex
|
||||
policyGate *ipcauth.PolicyGate
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -80,8 +80,14 @@ func (p *program) Start(svc service.Service) error {
|
||||
return fmt.Errorf("parse daemon address: %w", err)
|
||||
}
|
||||
|
||||
p.policyGate = ipcauth.NewPolicyGate()
|
||||
|
||||
// in any case, even if configuration does not exists we run daemon to serve CLI gRPC API.
|
||||
p.serv = grpc.NewServer(daemonServerOptions(network)...)
|
||||
opts := append(daemonServerOptions(network),
|
||||
grpc.ChainUnaryInterceptor(p.policyGate.UnaryPolicyInterceptor()),
|
||||
grpc.ChainStreamInterceptor(p.policyGate.StreamPolicyInterceptor()),
|
||||
)
|
||||
p.serv = grpc.NewServer(opts...)
|
||||
|
||||
daemonListener, jsonListener, err := listenDaemonSockets()
|
||||
if err != nil {
|
||||
@@ -145,6 +151,7 @@ func (p *program) serve(daemonListener, jsonListener *socketListener) error {
|
||||
}
|
||||
|
||||
serverInstance := server.New(p.ctx, util.FindFirstLogPath(logFiles), configPath, profilesDisabled, updateSettingsDisabled, captureEnabled, networksDisabled)
|
||||
p.policyGate.SetPolicy(serverInstance)
|
||||
if err := serverInstance.Start(); err != nil {
|
||||
return fmt.Errorf("start daemon: %w", err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package ipcauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type Policy interface {
|
||||
SessionHolder() (Identity, bool)
|
||||
}
|
||||
|
||||
type PolicyGate struct {
|
||||
mu sync.Mutex
|
||||
policy Policy
|
||||
}
|
||||
|
||||
func NewPolicyGate() *PolicyGate {
|
||||
return &PolicyGate{}
|
||||
}
|
||||
|
||||
func (g *PolicyGate) SetPolicy(p Policy) {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
g.policy = p
|
||||
}
|
||||
|
||||
func (g *PolicyGate) SessionHolder() (Identity, bool) {
|
||||
g.mu.Lock()
|
||||
defer g.mu.Unlock()
|
||||
if g.policy == nil {
|
||||
return Identity{}, false
|
||||
}
|
||||
return g.policy.SessionHolder()
|
||||
}
|
||||
|
||||
func (g *PolicyGate) StreamPolicyInterceptor() grpc.StreamServerInterceptor {
|
||||
return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
if !g.authorize(ss.Context()) {
|
||||
return status.Error(codes.PermissionDenied, "caller is not session owner")
|
||||
}
|
||||
return handler(ss.Context(), ss)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *PolicyGate) UnaryPolicyInterceptor() grpc.UnaryServerInterceptor {
|
||||
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
|
||||
if !g.authorize(ctx) {
|
||||
return nil, status.Error(codes.PermissionDenied, "caller is not session owner")
|
||||
}
|
||||
return handler(ctx, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *PolicyGate) authorize(ctx context.Context) bool {
|
||||
id, ok := CallerIdentity(ctx)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
g.mu.Lock()
|
||||
if g.policy == nil {
|
||||
g.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
sessionId, running := g.policy.SessionHolder()
|
||||
// TODO improve logging
|
||||
log.Infof("id : %v, session holder: %v", id, sessionId)
|
||||
g.mu.Unlock()
|
||||
// TODO windows
|
||||
// TODO allow root
|
||||
if running && sessionId.UID != id.UID {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/netbirdio/netbird/shared/management/domain"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/ipcauth"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/internal/statemanager"
|
||||
"github.com/netbirdio/netbird/client/internal/updater"
|
||||
@@ -148,6 +149,8 @@ type Server struct {
|
||||
loginAttemptFn func(ctx context.Context, setupKey, jwtToken string) (internal.StatusType, error)
|
||||
|
||||
isLoginRequiredFn func(ctx context.Context) (bool, error)
|
||||
|
||||
sessionHolder ipcauth.Identity
|
||||
}
|
||||
|
||||
type oauthAuthFlow struct {
|
||||
@@ -1083,6 +1086,14 @@ func (s *Server) Up(callerCtx context.Context, msg *proto.UpRequest) (*proto.UpR
|
||||
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
|
||||
s.localMetrics.Reconcile(s.config.LocalMetricsEnabled, s.config.LocalMetricsAddress)
|
||||
|
||||
id, ok := ipcauth.CallerIdentity(callerCtx)
|
||||
if !ok {
|
||||
s.mutex.Unlock()
|
||||
return nil, fmt.Errorf("failed to get identity")
|
||||
}
|
||||
|
||||
log.Infof("setting session holder: %d", id.UID)
|
||||
s.sessionHolder = id
|
||||
s.clientRunning = true
|
||||
s.clientRunningChan = make(chan struct{})
|
||||
s.clientGiveUpChan = make(chan struct{})
|
||||
@@ -1332,6 +1343,7 @@ func (s *Server) cleanupConnection() error {
|
||||
// explicitly asked for it. MDM restart does NOT go through this
|
||||
// path, so its clientRunning stays true.
|
||||
s.clientRunning = false
|
||||
s.sessionHolder = ipcauth.Identity{}
|
||||
|
||||
// Capture the engine reference before cancelling the context.
|
||||
// After actCancel(), the connectWithRetryRuns goroutine wakes up
|
||||
@@ -2695,6 +2707,15 @@ func (s *Server) authorizeAndPrepareLogin(callerCtx context.Context, msg *proto.
|
||||
return ctx, activeProf, nil
|
||||
}
|
||||
|
||||
func (s *Server) SessionHolder() (ipcauth.Identity, bool) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
if !s.clientRunning {
|
||||
return ipcauth.Identity{}, false
|
||||
}
|
||||
return s.sessionHolder, true
|
||||
}
|
||||
|
||||
func persistLoginOverrides(activeProf *profilemanager.ActiveProfileState, managementURL string, preSharedKey *string) error {
|
||||
if preSharedKey != nil && *preSharedKey == "" {
|
||||
preSharedKey = nil
|
||||
|
||||
Reference in New Issue
Block a user