mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-03 20:19:07 +02:00
[client] Add opt-in --allow-server-rdp flag, reuse SSH ACL, dynamic DLL registration
- Add ServerRDPAllowed field to daemon proto, EngineConfig, and profile config - Add --allow-server-rdp flag to `netbird up` (opt-in, defaults to false) - Wire RDP server start/stop in engine based on the flag - Reuse SSH ACL (SSHAuth proto) for RDP authorization via sshauth.Authorizer - Register/unregister credential provider COM DLL dynamically when flag is toggled - Ship DLL alongside netbird.exe, register via regsvr32 at runtime (not install time) - Update SetConfig tests to cover the new field https://claude.ai/code/session_01C38bCDyYzLgxYLVwJkcUng
This commit is contained in:
@@ -543,6 +543,7 @@ func createEngineConfig(key wgtypes.Key, config *profilemanager.Config, peerConf
|
||||
RosenpassEnabled: config.RosenpassEnabled,
|
||||
RosenpassPermissive: config.RosenpassPermissive,
|
||||
ServerSSHAllowed: util.ReturnBoolWithDefaultTrue(config.ServerSSHAllowed),
|
||||
ServerRDPAllowed: config.ServerRDPAllowed != nil && *config.ServerRDPAllowed,
|
||||
EnableSSHRoot: config.EnableSSHRoot,
|
||||
EnableSSHSFTP: config.EnableSSHSFTP,
|
||||
EnableSSHLocalPortForwarding: config.EnableSSHLocalPortForwarding,
|
||||
|
||||
@@ -117,6 +117,7 @@ type EngineConfig struct {
|
||||
RosenpassPermissive bool
|
||||
|
||||
ServerSSHAllowed bool
|
||||
ServerRDPAllowed bool
|
||||
EnableSSHRoot *bool
|
||||
EnableSSHSFTP *bool
|
||||
EnableSSHLocalPortForwarding *bool
|
||||
@@ -1037,6 +1038,10 @@ func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := e.updateRDP(); err != nil {
|
||||
log.Warnf("failed handling RDP server setup: %v", err)
|
||||
}
|
||||
|
||||
state := e.statusRecorder.GetLocalPeerState()
|
||||
state.IP = e.wgInterface.Address().String()
|
||||
state.PubKey = e.config.WgPrivateKey.PublicKey().String()
|
||||
@@ -1324,6 +1329,9 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
||||
}
|
||||
|
||||
e.updateSSHServerAuth(networkMap.GetSshAuth())
|
||||
|
||||
// Reuse SSH ACL for RDP authorization
|
||||
e.updateRDPServerAuth(networkMap.GetSshAuth())
|
||||
}
|
||||
|
||||
// must set the exclude list after the peers are added. Without it the manager can not figure out the peers parameters from the store
|
||||
|
||||
@@ -10,13 +10,17 @@ import (
|
||||
|
||||
firewallManager "github.com/netbirdio/netbird/client/firewall/manager"
|
||||
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
|
||||
sshauth "github.com/netbirdio/netbird/client/ssh/auth"
|
||||
rdpserver "github.com/netbirdio/netbird/client/rdp/server"
|
||||
mgmProto "github.com/netbirdio/netbird/shared/management/proto"
|
||||
sshuserhash "github.com/netbirdio/netbird/shared/sshauth"
|
||||
)
|
||||
|
||||
type rdpServer interface {
|
||||
Start(ctx context.Context, addr netip.AddrPort) error
|
||||
Stop() error
|
||||
GetPendingStore() *rdpserver.PendingStore
|
||||
UpdateRDPAuth(config *sshauth.Config)
|
||||
}
|
||||
|
||||
func (e *Engine) setupRDPPortRedirection() error {
|
||||
@@ -57,6 +61,28 @@ func (e *Engine) cleanupRDPPortRedirection() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRDP handles starting/stopping the RDP server based on the config flag.
|
||||
func (e *Engine) updateRDP() error {
|
||||
if !e.config.ServerRDPAllowed {
|
||||
if e.rdpServer != nil {
|
||||
log.Info("RDP passthrough disabled, stopping RDP auth server")
|
||||
}
|
||||
return e.stopRDPServer()
|
||||
}
|
||||
|
||||
if e.config.BlockInbound {
|
||||
log.Info("RDP server is disabled because inbound connections are blocked")
|
||||
return e.stopRDPServer()
|
||||
}
|
||||
|
||||
if e.rdpServer != nil {
|
||||
log.Debug("RDP auth server is already running")
|
||||
return nil
|
||||
}
|
||||
|
||||
return e.startRDPServer()
|
||||
}
|
||||
|
||||
func (e *Engine) startRDPServer() error {
|
||||
if e.wgInterface == nil {
|
||||
return errors.New("wg interface not initialized")
|
||||
@@ -92,6 +118,12 @@ func (e *Engine) startRDPServer() error {
|
||||
log.Warnf("failed to setup RDP auth port redirection: %v", err)
|
||||
}
|
||||
|
||||
// Register the credential provider DLL dynamically (Windows only)
|
||||
if err := rdpserver.RegisterCredentialProvider(); err != nil {
|
||||
log.Warnf("failed to register RDP credential provider (passwordless RDP will not work): %v", err)
|
||||
}
|
||||
|
||||
log.Info("RDP passthrough enabled")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -113,6 +145,11 @@ func (e *Engine) stopRDPServer() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Unregister the credential provider DLL (Windows only)
|
||||
if err := rdpserver.UnregisterCredentialProvider(); err != nil {
|
||||
log.Warnf("failed to unregister RDP credential provider: %v", err)
|
||||
}
|
||||
|
||||
log.Info("stopping RDP auth server")
|
||||
err := e.rdpServer.Stop()
|
||||
e.rdpServer = nil
|
||||
@@ -121,3 +158,34 @@ func (e *Engine) stopRDPServer() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRDPServerAuth reuses the SSH authorization config for RDP access control.
|
||||
// This means the same user/machine-user mappings that control SSH access also control RDP.
|
||||
func (e *Engine) updateRDPServerAuth(sshAuth *mgmProto.SSHAuth) {
|
||||
if sshAuth == nil || e.rdpServer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
protoUsers := sshAuth.GetAuthorizedUsers()
|
||||
authorizedUsers := make([]sshuserhash.UserIDHash, len(protoUsers))
|
||||
for i, hash := range protoUsers {
|
||||
if len(hash) != 16 {
|
||||
log.Warnf("invalid hash length %d, expected 16 - skipping RDP server auth update", len(hash))
|
||||
return
|
||||
}
|
||||
authorizedUsers[i] = sshuserhash.UserIDHash(hash)
|
||||
}
|
||||
|
||||
machineUsers := make(map[string][]uint32)
|
||||
for osUser, indexes := range sshAuth.GetMachineUsers() {
|
||||
machineUsers[osUser] = indexes.GetIndexes()
|
||||
}
|
||||
|
||||
authConfig := &sshauth.Config{
|
||||
UserIDClaim: sshAuth.GetUserIDClaim(),
|
||||
AuthorizedUsers: authorizedUsers,
|
||||
MachineUsers: machineUsers,
|
||||
}
|
||||
|
||||
e.rdpServer.UpdateRDPAuth(authConfig)
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ type ConfigInput struct {
|
||||
StateFilePath string
|
||||
PreSharedKey *string
|
||||
ServerSSHAllowed *bool
|
||||
ServerRDPAllowed *bool
|
||||
EnableSSHRoot *bool
|
||||
EnableSSHSFTP *bool
|
||||
EnableSSHLocalPortForwarding *bool
|
||||
@@ -114,6 +115,7 @@ type Config struct {
|
||||
RosenpassEnabled bool
|
||||
RosenpassPermissive bool
|
||||
ServerSSHAllowed *bool
|
||||
ServerRDPAllowed *bool
|
||||
EnableSSHRoot *bool
|
||||
EnableSSHSFTP *bool
|
||||
EnableSSHLocalPortForwarding *bool
|
||||
@@ -415,6 +417,21 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
|
||||
updated = true
|
||||
}
|
||||
|
||||
if input.ServerRDPAllowed != nil {
|
||||
if config.ServerRDPAllowed == nil || *input.ServerRDPAllowed != *config.ServerRDPAllowed {
|
||||
if *input.ServerRDPAllowed {
|
||||
log.Infof("enabling RDP passthrough")
|
||||
} else {
|
||||
log.Infof("disabling RDP passthrough")
|
||||
}
|
||||
config.ServerRDPAllowed = input.ServerRDPAllowed
|
||||
updated = true
|
||||
}
|
||||
} else if config.ServerRDPAllowed == nil {
|
||||
config.ServerRDPAllowed = util.False()
|
||||
updated = true
|
||||
}
|
||||
|
||||
if input.EnableSSHRoot != nil && input.EnableSSHRoot != config.EnableSSHRoot {
|
||||
if *input.EnableSSHRoot {
|
||||
log.Infof("enabling SSH root login")
|
||||
|
||||
Reference in New Issue
Block a user