mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-09 06:59:08 +02:00
Address CodeRabbit review on embedded VNC PR
This commit is contained in:
@@ -155,8 +155,10 @@ func (e *Engine) startVNCServer() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// updateVNCServerAuth updates VNC fine-grained access control from management.
|
// updateVNCServerAuth updates VNC fine-grained access control from management.
|
||||||
|
// A nil vncAuth clears all authorized users and session pubkeys so management
|
||||||
|
// can revoke access by omitting the field on the next sync.
|
||||||
func (e *Engine) updateVNCServerAuth(vncAuth *mgmProto.VNCAuth) {
|
func (e *Engine) updateVNCServerAuth(vncAuth *mgmProto.VNCAuth) {
|
||||||
if vncAuth == nil || e.vncSrv == nil {
|
if e.vncSrv == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +167,11 @@ func (e *Engine) updateVNCServerAuth(vncAuth *mgmProto.VNCAuth) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if vncAuth == nil {
|
||||||
|
vncSrv.UpdateVNCAuth(&sshauth.Config{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
protoUsers := vncAuth.GetAuthorizedUsers()
|
protoUsers := vncAuth.GetAuthorizedUsers()
|
||||||
authorizedUsers := make([]sshuserhash.UserIDHash, len(protoUsers))
|
authorizedUsers := make([]sshuserhash.UserIDHash, len(protoUsers))
|
||||||
for i, hash := range protoUsers {
|
for i, hash := range protoUsers {
|
||||||
@@ -207,12 +214,17 @@ func (e *Engine) updateVNCServerAuth(vncAuth *mgmProto.VNCAuth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetVNCServerStatus returns whether the VNC server is running and the list
|
// GetVNCServerStatus returns whether the VNC server is running and the list
|
||||||
// of active VNC sessions.
|
// of active VNC sessions. The pointer is captured under syncMsgMux so a
|
||||||
|
// concurrent updateVNC/stopVNCServer cannot swap it out between the nil
|
||||||
|
// check and the ActiveSessions call.
|
||||||
func (e *Engine) GetVNCServerStatus() (enabled bool, sessions []vncserver.ActiveSessionInfo) {
|
func (e *Engine) GetVNCServerStatus() (enabled bool, sessions []vncserver.ActiveSessionInfo) {
|
||||||
if e.vncSrv == nil {
|
e.syncMsgMux.Lock()
|
||||||
|
vncSrv := e.vncSrv
|
||||||
|
e.syncMsgMux.Unlock()
|
||||||
|
if vncSrv == nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return true, e.vncSrv.ActiveSessions()
|
return true, vncSrv.ActiveSessions()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) stopVNCServer() error {
|
func (e *Engine) stopVNCServer() error {
|
||||||
|
|||||||
@@ -324,7 +324,11 @@ func spawnAgentInSession(sessionID uint32, socketPath, authToken string, jobHand
|
|||||||
}
|
}
|
||||||
|
|
||||||
if _, err := windows.ResumeThread(pi.Thread); err != nil {
|
if _, err := windows.ResumeThread(pi.Thread); err != nil {
|
||||||
log.Warnf("resume agent main thread: %v", err)
|
_ = windows.CloseHandle(pi.Thread)
|
||||||
|
_ = windows.TerminateProcess(pi.Process, 1)
|
||||||
|
_ = windows.CloseHandle(pi.Process)
|
||||||
|
_ = windows.CloseHandle(stderrRead)
|
||||||
|
return 0, fmt.Errorf("ResumeThread: %w", err)
|
||||||
}
|
}
|
||||||
_ = windows.CloseHandle(pi.Thread)
|
_ = windows.CloseHandle(pi.Thread)
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ func TestNoise_WrongServerStatic_HandshakeFails(t *testing.T) {
|
|||||||
CipherSuite: vncNoiseSuite,
|
CipherSuite: vncNoiseSuite,
|
||||||
Pattern: noise.HandshakeIK,
|
Pattern: noise.HandshakeIK,
|
||||||
Initiator: true,
|
Initiator: true,
|
||||||
|
Prologue: BuildVNCNoisePrologue(ModeAttach, ""),
|
||||||
StaticKeypair: clientKey,
|
StaticKeypair: clientKey,
|
||||||
PeerStatic: bogusServerKey.Public,
|
PeerStatic: bogusServerKey.Public,
|
||||||
})
|
})
|
||||||
@@ -382,6 +383,7 @@ func TestNoise_NoIdentityKey_FailsClosed(t *testing.T) {
|
|||||||
CipherSuite: vncNoiseSuite,
|
CipherSuite: vncNoiseSuite,
|
||||||
Pattern: noise.HandshakeIK,
|
Pattern: noise.HandshakeIK,
|
||||||
Initiator: true,
|
Initiator: true,
|
||||||
|
Prologue: BuildVNCNoisePrologue(ModeAttach, ""),
|
||||||
StaticKeypair: clientKey,
|
StaticKeypair: clientKey,
|
||||||
PeerStatic: fakeServerKey.Public,
|
PeerStatic: fakeServerKey.Public,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -180,6 +180,10 @@ type Server struct {
|
|||||||
netstackNet *netstack.Net
|
netstackNet *netstack.Net
|
||||||
// agentToken holds the raw token bytes for agent-mode auth.
|
// agentToken holds the raw token bytes for agent-mode auth.
|
||||||
agentToken []byte
|
agentToken []byte
|
||||||
|
// invalidAgentToken latches when AgentTokenHex was provided but failed
|
||||||
|
// to decode. Start refuses to listen in that case so the daemon never
|
||||||
|
// silently downgrades the local IPC hop to unauthenticated access.
|
||||||
|
invalidAgentToken bool
|
||||||
// identityKey is the daemon's static X25519 private key used in the
|
// identityKey is the daemon's static X25519 private key used in the
|
||||||
// Noise_IK handshake. Nil disables the handshake.
|
// Noise_IK handshake. Nil disables the handshake.
|
||||||
identityKey []byte
|
identityKey []byte
|
||||||
@@ -356,6 +360,7 @@ func New(cfg Config) *Server {
|
|||||||
if b, err := hex.DecodeString(cfg.AgentTokenHex); err == nil {
|
if b, err := hex.DecodeString(cfg.AgentTokenHex); err == nil {
|
||||||
s.agentToken = b
|
s.agentToken = b
|
||||||
} else {
|
} else {
|
||||||
|
s.invalidAgentToken = true
|
||||||
s.log.Warnf("invalid agent token: %v", err)
|
s.log.Warnf("invalid agent token: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,6 +583,9 @@ func (s *Server) Start(ctx context.Context, addr netip.AddrPort, network netip.P
|
|||||||
if s.listener != nil {
|
if s.listener != nil {
|
||||||
return fmt.Errorf("server already running")
|
return fmt.Errorf("server already running")
|
||||||
}
|
}
|
||||||
|
if s.invalidAgentToken {
|
||||||
|
return fmt.Errorf("invalid agent token configuration")
|
||||||
|
}
|
||||||
|
|
||||||
s.ctx, s.cancel = context.WithCancel(ctx)
|
s.ctx, s.cancel = context.WithCancel(ctx)
|
||||||
s.vmgr = s.platformSessionManager()
|
s.vmgr = s.platformSessionManager()
|
||||||
@@ -686,13 +694,17 @@ func (s *Server) acceptLoop() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track before any early-reject path so a concurrent Stop's
|
||||||
|
// closeActiveSessions snapshot can never miss a just-accepted
|
||||||
|
// socket and let it survive shutdown.
|
||||||
|
s.trackConn(conn)
|
||||||
if !s.tryAcquireConnSlot() {
|
if !s.tryAcquireConnSlot() {
|
||||||
|
s.untrackConn(conn)
|
||||||
s.log.Warnf("rejecting VNC connection from %s: %d concurrent connections in flight", conn.RemoteAddr(), maxConcurrentVNCConns)
|
s.log.Warnf("rejecting VNC connection from %s: %d concurrent connections in flight", conn.RemoteAddr(), maxConcurrentVNCConns)
|
||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
enableTCPKeepAlive(conn, s.log)
|
enableTCPKeepAlive(conn, s.log)
|
||||||
s.trackConn(conn)
|
|
||||||
go func(c net.Conn) {
|
go func(c net.Conn) {
|
||||||
defer s.releaseConnSlot()
|
defer s.releaseConnSlot()
|
||||||
defer s.untrackConn(c)
|
defer s.untrackConn(c)
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ func sasSecurityAttributes() (*windows.SecurityAttributes, error) {
|
|||||||
type sasOriginalState struct {
|
type sasOriginalState struct {
|
||||||
had bool // true if the value existed before we wrote
|
had bool // true if the value existed before we wrote
|
||||||
value uint32 // its prior DWORD value, if had == true
|
value uint32 // its prior DWORD value, if had == true
|
||||||
|
// captured stays true once we have read the genuine pre-enable state
|
||||||
|
// for the first time, so a second enableSoftwareSAS call (e.g. after
|
||||||
|
// a daemon restart with no intervening disable) cannot overwrite the
|
||||||
|
// snapshot with our own forced value.
|
||||||
|
captured bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var savedSASState sasOriginalState
|
var savedSASState sasOriginalState
|
||||||
@@ -74,10 +79,12 @@ func enableSoftwareSAS() {
|
|||||||
}
|
}
|
||||||
defer key.Close()
|
defer key.Close()
|
||||||
|
|
||||||
if prev, _, err := key.GetIntegerValue("SoftwareSASGeneration"); err == nil {
|
if !savedSASState.captured {
|
||||||
savedSASState = sasOriginalState{had: true, value: uint32(prev)}
|
if prev, _, err := key.GetIntegerValue("SoftwareSASGeneration"); err == nil {
|
||||||
} else {
|
savedSASState = sasOriginalState{had: true, value: uint32(prev), captured: true}
|
||||||
savedSASState = sasOriginalState{had: false}
|
} else {
|
||||||
|
savedSASState = sasOriginalState{had: false, captured: true}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := key.SetDWordValue("SoftwareSASGeneration", 1); err != nil {
|
if err := key.SetDWordValue("SoftwareSASGeneration", 1); err != nil {
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ func (c *NetworkMapComponents) resolveRuleEndpoint(
|
|||||||
postureChecks []string,
|
postureChecks []string,
|
||||||
) ([]*nbpeer.Peer, bool) {
|
) ([]*nbpeer.Peer, bool) {
|
||||||
if resource.Type == ResourceTypePeer && resource.ID != "" {
|
if resource.Type == ResourceTypePeer && resource.ID != "" {
|
||||||
return c.getPeerFromResource(resource, peerID)
|
return c.getPeerFromResource(resource, peerID, postureChecks)
|
||||||
}
|
}
|
||||||
return c.getAllPeersFromGroups(groups, peerID, postureChecks)
|
return c.getAllPeersFromGroups(groups, peerID, postureChecks)
|
||||||
}
|
}
|
||||||
@@ -385,8 +385,11 @@ func (c *NetworkMapComponents) getUniquePeerIDsFromGroupsIDs(groups []string) []
|
|||||||
return ids
|
return ids
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *NetworkMapComponents) getPeerFromResource(resource Resource, peerID string) ([]*nbpeer.Peer, bool) {
|
func (c *NetworkMapComponents) getPeerFromResource(resource Resource, peerID string, postureChecks []string) ([]*nbpeer.Peer, bool) {
|
||||||
if resource.ID == peerID {
|
if resource.ID == peerID {
|
||||||
|
if len(postureChecks) > 0 && !c.ValidatePostureChecksOnPeer(peerID, postureChecks) {
|
||||||
|
return []*nbpeer.Peer{}, false
|
||||||
|
}
|
||||||
return []*nbpeer.Peer{}, true
|
return []*nbpeer.Peer{}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,6 +397,9 @@ func (c *NetworkMapComponents) getPeerFromResource(resource Resource, peerID str
|
|||||||
if peerInfo == nil {
|
if peerInfo == nil {
|
||||||
return []*nbpeer.Peer{}, false
|
return []*nbpeer.Peer{}, false
|
||||||
}
|
}
|
||||||
|
if len(postureChecks) > 0 && !c.ValidatePostureChecksOnPeer(resource.ID, postureChecks) {
|
||||||
|
return []*nbpeer.Peer{}, false
|
||||||
|
}
|
||||||
|
|
||||||
return []*nbpeer.Peer{peerInfo}, false
|
return []*nbpeer.Peer{peerInfo}, false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,9 +59,10 @@ func applyResolvedRuleToState(
|
|||||||
) {
|
) {
|
||||||
emitRuleDirections(rule, sourcePeers, destPeers, peerInSources, peerInDestinations, generateResources)
|
emitRuleDirections(rule, sourcePeers, destPeers, peerInSources, peerInDestinations, generateResources)
|
||||||
|
|
||||||
|
receivingPeer := peerInDestinations || (rule.Bidirectional && peerInSources)
|
||||||
switch {
|
switch {
|
||||||
case rule.Protocol == PolicyRuleProtocolNetbirdSSH:
|
case rule.Protocol == PolicyRuleProtocolNetbirdSSH:
|
||||||
if !peerInDestinations {
|
if !receivingPeer {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
state.sshEnabled = true
|
state.sshEnabled = true
|
||||||
@@ -69,7 +70,7 @@ func applyResolvedRuleToState(
|
|||||||
case rule.Protocol == PolicyRuleProtocolNetbirdVNC:
|
case rule.Protocol == PolicyRuleProtocolNetbirdVNC:
|
||||||
cb.handleVNCRule(rule, peerInSources, peerInDestinations, state)
|
cb.handleVNCRule(rule, peerInSources, peerInDestinations, state)
|
||||||
case policyRuleImpliesLegacySSH(rule) && targetPeerSSHEnabled:
|
case policyRuleImpliesLegacySSH(rule) && targetPeerSSHEnabled:
|
||||||
if !peerInDestinations {
|
if !receivingPeer {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
state.sshEnabled = true
|
state.sshEnabled = true
|
||||||
|
|||||||
Reference in New Issue
Block a user