mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-31 03:51:29 +02:00
Evict orphaned packet captures and annotate VNC streams
This commit is contained in:
@@ -15,16 +15,13 @@ import (
|
||||
"github.com/netbirdio/netbird/client/internal/metrics"
|
||||
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
sshauth "github.com/netbirdio/netbird/shared/sessionauth"
|
||||
"github.com/netbirdio/netbird/client/vnc"
|
||||
vncserver "github.com/netbirdio/netbird/client/vnc/server"
|
||||
sshauth "github.com/netbirdio/netbird/shared/sessionauth"
|
||||
mgmProto "github.com/netbirdio/netbird/shared/management/proto"
|
||||
sshuserhash "github.com/netbirdio/netbird/shared/sshauth"
|
||||
)
|
||||
|
||||
const (
|
||||
vncExternalPort uint16 = 5900
|
||||
vncInternalPort uint16 = 25900
|
||||
)
|
||||
|
||||
type vncServer interface {
|
||||
Start(ctx context.Context, addr netip.AddrPort, network netip.Prefix) error
|
||||
@@ -42,10 +39,10 @@ func (e *Engine) setupVNCPortRedirection() error {
|
||||
return errors.New("invalid local NetBird address")
|
||||
}
|
||||
|
||||
if err := e.firewall.AddInboundDNAT(localAddr, firewallManager.ProtocolTCP, vncExternalPort, vncInternalPort); err != nil {
|
||||
if err := e.firewall.AddInboundDNAT(localAddr, firewallManager.ProtocolTCP, vnc.ExternalPort, vnc.InternalPort); err != nil {
|
||||
return fmt.Errorf("add VNC port redirection: %w", err)
|
||||
}
|
||||
log.Infof("VNC port redirection: %s:%d -> %s:%d", localAddr, vncExternalPort, localAddr, vncInternalPort)
|
||||
log.Infof("VNC port redirection: %s:%d -> %s:%d", localAddr, vnc.ExternalPort, localAddr, vnc.InternalPort)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -60,7 +57,7 @@ func (e *Engine) cleanupVNCPortRedirection() error {
|
||||
return errors.New("invalid local NetBird address")
|
||||
}
|
||||
|
||||
if err := e.firewall.RemoveInboundDNAT(localAddr, firewallManager.ProtocolTCP, vncExternalPort, vncInternalPort); err != nil {
|
||||
if err := e.firewall.RemoveInboundDNAT(localAddr, firewallManager.ProtocolTCP, vnc.ExternalPort, vnc.InternalPort); err != nil {
|
||||
return fmt.Errorf("remove VNC port redirection: %w", err)
|
||||
}
|
||||
|
||||
@@ -132,7 +129,7 @@ func (e *Engine) startVNCServer() error {
|
||||
Approver: &vncApprover{broker: e.approvalBroker, statusRecorder: e.statusRecorder},
|
||||
})
|
||||
|
||||
listenAddr := netip.AddrPortFrom(netbirdIP, vncInternalPort)
|
||||
listenAddr := netip.AddrPortFrom(netbirdIP, vnc.InternalPort)
|
||||
network := e.wgInterface.Address().Network
|
||||
if err := srv.Start(e.ctx, listenAddr, network); err != nil {
|
||||
return fmt.Errorf("start VNC server: %w", err)
|
||||
@@ -144,8 +141,8 @@ func (e *Engine) startVNCServer() error {
|
||||
if registrar, ok := e.firewall.(interface {
|
||||
RegisterNetstackService(protocol nftypes.Protocol, port uint16)
|
||||
}); ok {
|
||||
registrar.RegisterNetstackService(nftypes.TCP, vncInternalPort)
|
||||
log.Debugf("registered VNC service with netstack for TCP:%d", vncInternalPort)
|
||||
registrar.RegisterNetstackService(nftypes.TCP, vnc.InternalPort)
|
||||
log.Debugf("registered VNC service with netstack for TCP:%d", vnc.InternalPort)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +228,7 @@ func (e *Engine) stopVNCServer() error {
|
||||
if registrar, ok := e.firewall.(interface {
|
||||
UnregisterNetstackService(protocol nftypes.Protocol, port uint16)
|
||||
}); ok {
|
||||
registrar.UnregisterNetstackService(nftypes.TCP, vncInternalPort)
|
||||
registrar.UnregisterNetstackService(nftypes.TCP, vnc.InternalPort)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,10 +190,7 @@ func (s *Server) StartBundleCapture(_ context.Context, req *proto.StartBundleCap
|
||||
|
||||
s.stopBundleCaptureLocked()
|
||||
s.cleanupBundleCapture()
|
||||
|
||||
if s.activeCapture != nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "another capture is already running")
|
||||
}
|
||||
s.evictActiveCaptureLocked()
|
||||
|
||||
engine, err := s.getCaptureEngineLocked()
|
||||
if err != nil {
|
||||
@@ -304,15 +301,15 @@ func (s *Server) cleanupBundleCapture() {
|
||||
s.bundleCapture = nil
|
||||
}
|
||||
|
||||
// claimCapture reserves the engine's capture slot for sess. Returns
|
||||
// FailedPrecondition if another capture is already active.
|
||||
// claimCapture reserves the engine's capture slot for sess. If another
|
||||
// capture is already running it is evicted: a previous streaming session
|
||||
// whose gRPC client died and never freed the slot stays stuck otherwise,
|
||||
// and a bundle capture is just informational state.
|
||||
func (s *Server) claimCapture(sess *capture.Session) (*internal.Engine, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if s.activeCapture != nil {
|
||||
return nil, status.Error(codes.FailedPrecondition, "another capture is already running")
|
||||
}
|
||||
s.evictActiveCaptureLocked()
|
||||
engine, err := s.getCaptureEngineLocked()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -321,6 +318,28 @@ func (s *Server) claimCapture(sess *capture.Session) (*internal.Engine, error) {
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
// evictActiveCaptureLocked tears down whatever capture currently owns
|
||||
// the engine slot so a fresh claim can succeed. Caller must hold mutex.
|
||||
func (s *Server) evictActiveCaptureLocked() {
|
||||
if s.activeCapture == nil {
|
||||
return
|
||||
}
|
||||
if s.bundleCapture != nil && s.bundleCapture.sess == s.activeCapture {
|
||||
log.Infof("evicting running bundle capture to start a new capture")
|
||||
s.stopBundleCaptureLocked()
|
||||
return
|
||||
}
|
||||
log.Infof("evicting previous streaming capture to start a new one")
|
||||
prev := s.activeCapture
|
||||
if engine, err := s.getCaptureEngineLocked(); err == nil {
|
||||
if err := engine.SetCapture(nil); err != nil {
|
||||
log.Debugf("clear previous capture: %v", err)
|
||||
}
|
||||
}
|
||||
s.activeCapture = nil
|
||||
prev.Stop()
|
||||
}
|
||||
|
||||
// releaseCapture clears the active-capture owner if it still matches sess.
|
||||
func (s *Server) releaseCapture(sess *capture.Session) {
|
||||
s.mutex.Lock()
|
||||
|
||||
31
client/vnc/ports.go
Normal file
31
client/vnc/ports.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Package vnc holds shared constants for the NetBird embedded VNC stack
|
||||
// so non-server consumers (CLI capture, debug tooling) can refer to the
|
||||
// well-known ports without depending on internal engine packages.
|
||||
package vnc
|
||||
|
||||
// External and internal listen ports for the embedded VNC server.
|
||||
// ExternalPort is what dashboard / browser clients see; the daemon
|
||||
// DNATs it to InternalPort, where the in-process VNC server actually
|
||||
// listens. Both flow over the WireGuard interface. AgentLegacyPort is
|
||||
// the TCP port the per-session agent used before it switched to Unix
|
||||
// sockets; kept here so packet captures from older builds still get
|
||||
// tagged, and so any future on-wire agent variant has a reserved port.
|
||||
const (
|
||||
ExternalPort uint16 = 5900
|
||||
InternalPort uint16 = 25900
|
||||
AgentLegacyPort uint16 = 15900
|
||||
)
|
||||
|
||||
// WellKnownPorts is the unordered set of ports a packet capture should
|
||||
// treat as carrying NetBird VNC traffic.
|
||||
var WellKnownPorts = [...]uint16{ExternalPort, InternalPort, AgentLegacyPort}
|
||||
|
||||
// IsWellKnownPort reports whether port matches any of WellKnownPorts.
|
||||
func IsWellKnownPort(port uint16) bool {
|
||||
for _, p := range WellKnownPorts {
|
||||
if port == p {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -222,8 +222,8 @@ func waitForAgent(ctx context.Context, socketPath string, wait time.Duration) er
|
||||
}
|
||||
|
||||
// vncAgentRunning reports whether any vnc-agent process exists on the
|
||||
// system. The daemon owns the only port-15900 listener model, so any
|
||||
// match is "the" agent.
|
||||
// system. There is at most one agent per machine, so any match is "the"
|
||||
// agent.
|
||||
func vncAgentRunning() bool {
|
||||
pids, err := vncAgentPIDs()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user