mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-27 23:09:08 +01:00
Support per-peer lazy connection state and default proxy peers to lazy
This commit is contained in:
+44
-89
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/internal/peerstore"
|
"github.com/netbirdio/netbird/client/internal/peerstore"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
|
mgmProto "github.com/netbirdio/netbird/shared/management/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// lazyForce is the resolved local decision for lazy connections, layered above the
|
// lazyForce is the resolved local decision for lazy connections, layered above the
|
||||||
@@ -42,6 +43,9 @@ type ConnMgr struct {
|
|||||||
iface lazyconn.WGIface
|
iface lazyconn.WGIface
|
||||||
force lazyForce
|
force lazyForce
|
||||||
rosenpassEnabled bool
|
rosenpassEnabled bool
|
||||||
|
// remoteLazyEnabled caches the account-wide lazy feature flag from management.
|
||||||
|
// It is the default for peers that do not carry a per-peer lazy hint.
|
||||||
|
remoteLazyEnabled bool
|
||||||
|
|
||||||
lazyConnMgr *manager.Manager
|
lazyConnMgr *manager.Manager
|
||||||
// lazyConnMgrMu guards the lazyConnMgr pointer for readers outside the
|
// lazyConnMgrMu guards the lazyConnMgr pointer for readers outside the
|
||||||
@@ -75,69 +79,56 @@ func NewConnMgr(engineConfig *EngineConfig, statusRecorder *peer.Status, peerSto
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start initializes the connection manager. It starts the lazy connection manager when a
|
// Start initializes the connection manager. The lazy connection manager always runs so that
|
||||||
// local override forces it on; with no local override it waits for the management feature flag.
|
// per-peer lazy defaults (e.g. proxy peers) work even when the account flag is off; the
|
||||||
|
// account flag and the local override decide the default lazy state per peer (see
|
||||||
|
// PeerLazyDefault). Rosenpass is the only condition that disables it.
|
||||||
func (e *ConnMgr) Start(ctx context.Context) {
|
func (e *ConnMgr) Start(ctx context.Context) {
|
||||||
if e.lazyConnMgr != nil {
|
if e.lazyConnMgr != nil {
|
||||||
log.Errorf("lazy connection manager is already started")
|
log.Errorf("lazy connection manager is already started")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch e.force {
|
|
||||||
case lazyForceOff:
|
|
||||||
log.Infof("lazy connection manager is disabled by local override (%s or MDM policy)", lazyconn.EnvLazyConn)
|
|
||||||
e.statusRecorder.UpdateLazyConnection(false)
|
|
||||||
return
|
|
||||||
case lazyForceNone:
|
|
||||||
log.Infof("lazy connection manager is managed by the management feature flag")
|
|
||||||
e.statusRecorder.UpdateLazyConnection(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.rosenpassEnabled {
|
if e.rosenpassEnabled {
|
||||||
log.Warnf("rosenpass connection manager is enabled, lazy connection manager will not be started")
|
log.Warnf("rosenpass is enabled, lazy connection manager will not be started")
|
||||||
e.statusRecorder.UpdateLazyConnection(false)
|
e.statusRecorder.UpdateLazyConnection(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
e.initLazyManager(ctx)
|
e.initLazyManager(ctx)
|
||||||
e.statusRecorder.UpdateLazyConnection(true)
|
e.statusRecorder.UpdateLazyConnection(e.PeerLazyDefault(mgmProto.LazyState_LazyStateDefault))
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatedRemoteFeatureFlag is called when the remote feature flag is updated.
|
// UpdatedRemoteFeatureFlag caches the account-wide lazy feature flag. The manager itself is
|
||||||
// If enabled, it initializes the lazy connection manager and start it. Do not need to call Start() again.
|
// not started or stopped here; the per-sync exclude-list reconciliation moves normal peers
|
||||||
// If disabled, then it closes the lazy connection manager and open the connections to all peers.
|
// between the lazy and always-active sets when the flag flips.
|
||||||
func (e *ConnMgr) UpdatedRemoteFeatureFlag(ctx context.Context, enabled bool) error {
|
func (e *ConnMgr) UpdatedRemoteFeatureFlag(_ context.Context, enabled bool) error {
|
||||||
// a local override (NB_LAZY_CONN or local config) takes precedence over management
|
e.remoteLazyEnabled = enabled
|
||||||
if e.force != lazyForceNone {
|
if e.isStartedWithLazyMgr() {
|
||||||
return nil
|
e.statusRecorder.UpdateLazyConnection(e.PeerLazyDefault(mgmProto.LazyState_LazyStateDefault))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerLazyDefault reports whether a peer should be lazy. The local override
|
||||||
|
// (NB_LAZY_CONN/MDM) wins over everything; without a local override the
|
||||||
|
// management per-peer state applies (LazyStateLazy/Eager force the decision),
|
||||||
|
// and LazyStateDefault follows the account-wide flag.
|
||||||
|
func (e *ConnMgr) PeerLazyDefault(state mgmProto.LazyState) bool {
|
||||||
|
switch e.force {
|
||||||
|
case lazyForceOn:
|
||||||
|
return true
|
||||||
|
case lazyForceOff:
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if enabled {
|
switch state {
|
||||||
// if the lazy connection manager is already started, do not start it again
|
case mgmProto.LazyState_LazyStateLazy:
|
||||||
if e.lazyConnMgr != nil {
|
return true
|
||||||
return nil
|
case mgmProto.LazyState_LazyStateEager:
|
||||||
}
|
return false
|
||||||
|
default:
|
||||||
if e.rosenpassEnabled {
|
return e.remoteLazyEnabled
|
||||||
log.Infof("rosenpass connection manager is enabled, lazy connection manager will not be started")
|
|
||||||
e.statusRecorder.UpdateLazyConnection(false)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("lazy connection manager is enabled by the management feature flag")
|
|
||||||
e.initLazyManager(ctx)
|
|
||||||
e.statusRecorder.UpdateLazyConnection(true)
|
|
||||||
return e.addPeersToLazyConnManager()
|
|
||||||
} else {
|
|
||||||
if e.lazyConnMgr == nil {
|
|
||||||
e.statusRecorder.UpdateLazyConnection(false)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
log.Infof("lazy connection manager is disabled by management feature flag")
|
|
||||||
e.closeManager(ctx)
|
|
||||||
e.statusRecorder.UpdateLazyConnection(false)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,12 +183,16 @@ func (e *ConnMgr) SetExcludeList(ctx context.Context, peerIDs map[string]bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ConnMgr) AddPeerConn(ctx context.Context, peerKey string, conn *peer.Conn) (exists bool) {
|
// AddPeerConn registers a peer connection. permanent requests an always-active connection
|
||||||
|
// (the peer belongs to the exclude set: a forwarder, or a peer that is not lazy by policy).
|
||||||
|
// Non-permanent peers are handed to the lazy manager. The subsequent SetExcludeList call
|
||||||
|
// reconciles membership for existing peers across flag flips.
|
||||||
|
func (e *ConnMgr) AddPeerConn(ctx context.Context, peerKey string, conn *peer.Conn, permanent bool) (exists bool) {
|
||||||
if success := e.peerStore.AddPeerConn(peerKey, conn); !success {
|
if success := e.peerStore.AddPeerConn(peerKey, conn); !success {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !e.isStartedWithLazyMgr() {
|
if !e.isStartedWithLazyMgr() || permanent {
|
||||||
if err := conn.Open(ctx); err != nil {
|
if err := conn.Open(ctx); err != nil {
|
||||||
conn.Log.Errorf("failed to open connection: %v", err)
|
conn.Log.Errorf("failed to open connection: %v", err)
|
||||||
}
|
}
|
||||||
@@ -316,46 +311,6 @@ func (e *ConnMgr) initLazyManager(engineCtx context.Context) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *ConnMgr) addPeersToLazyConnManager() error {
|
|
||||||
peers := e.peerStore.PeersPubKey()
|
|
||||||
lazyPeerCfgs := make([]lazyconn.PeerConfig, 0, len(peers))
|
|
||||||
for _, peerID := range peers {
|
|
||||||
var peerConn *peer.Conn
|
|
||||||
var exists bool
|
|
||||||
if peerConn, exists = e.peerStore.PeerConn(peerID); !exists {
|
|
||||||
log.Warnf("failed to find peer conn for peerID: %s", peerID)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lazyPeerCfg := lazyconn.PeerConfig{
|
|
||||||
PublicKey: peerID,
|
|
||||||
AllowedIPs: peerConn.WgConfig().AllowedIps,
|
|
||||||
PeerConnID: peerConn.ConnID(),
|
|
||||||
Log: peerConn.Log,
|
|
||||||
}
|
|
||||||
lazyPeerCfgs = append(lazyPeerCfgs, lazyPeerCfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return e.lazyConnMgr.AddActivePeers(lazyPeerCfgs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *ConnMgr) closeManager(ctx context.Context) {
|
|
||||||
if e.lazyConnMgr == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
e.lazyCtxCancel()
|
|
||||||
e.wg.Wait()
|
|
||||||
|
|
||||||
e.lazyConnMgrMu.Lock()
|
|
||||||
e.lazyConnMgr = nil
|
|
||||||
e.lazyConnMgrMu.Unlock()
|
|
||||||
|
|
||||||
for _, peerID := range e.peerStore.PeersPubKey() {
|
|
||||||
e.peerStore.PeerConnOpen(ctx, peerID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *ConnMgr) isStartedWithLazyMgr() bool {
|
func (e *ConnMgr) isStartedWithLazyMgr() bool {
|
||||||
return e.lazyConnMgr != nil && e.lazyCtxCancel != nil
|
return e.lazyConnMgr != nil && e.lazyCtxCancel != nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/internal/peerstore"
|
"github.com/netbirdio/netbird/client/internal/peerstore"
|
||||||
"github.com/netbirdio/netbird/monotime"
|
"github.com/netbirdio/netbird/monotime"
|
||||||
|
mgmProto "github.com/netbirdio/netbird/shared/management/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResolveLazyForce(t *testing.T) {
|
func TestResolveLazyForce(t *testing.T) {
|
||||||
@@ -138,4 +139,91 @@ func TestInactivityThresholdEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPeerLazyDefault(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
force lazyForce
|
||||||
|
remoteEnabled bool
|
||||||
|
state mgmProto.LazyState
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{name: "force on wins over eager state", force: lazyForceOn, state: mgmProto.LazyState_LazyStateEager, want: true},
|
||||||
|
{name: "force off wins over lazy state", force: lazyForceOff, remoteEnabled: true, state: mgmProto.LazyState_LazyStateLazy, want: false},
|
||||||
|
{name: "none, default, account off -> active", force: lazyForceNone, state: mgmProto.LazyState_LazyStateDefault, want: false},
|
||||||
|
{name: "none, default, account on -> lazy", force: lazyForceNone, remoteEnabled: true, state: mgmProto.LazyState_LazyStateDefault, want: true},
|
||||||
|
{name: "none, lazy state, account off -> lazy", force: lazyForceNone, state: mgmProto.LazyState_LazyStateLazy, want: true},
|
||||||
|
{name: "none, eager state, account on -> active", force: lazyForceNone, remoteEnabled: true, state: mgmProto.LazyState_LazyStateEager, want: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
e := &ConnMgr{force: tt.force, remoteLazyEnabled: tt.remoteEnabled}
|
||||||
|
if got := e.PeerLazyDefault(tt.state); got != tt.want {
|
||||||
|
t.Fatalf("PeerLazyDefault(%v) = %v, want %v", tt.state, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func durPtr(d time.Duration) *time.Duration { return &d }
|
func durPtr(d time.Duration) *time.Duration { return &d }
|
||||||
|
|
||||||
|
// TestToExcludedLazyPeers covers the per-peer lazy classification (proxy vs
|
||||||
|
// normal, across the force/account-flag matrix). Forwarder-target exclusion is
|
||||||
|
// covered by TestToExcludedLazyPeers_ForwardTarget.
|
||||||
|
func TestToExcludedLazyPeers(t *testing.T) {
|
||||||
|
const (
|
||||||
|
normalKey = "normal"
|
||||||
|
lazyKey = "lazy-state"
|
||||||
|
eagerKey = "eager-state"
|
||||||
|
)
|
||||||
|
|
||||||
|
peers := []*mgmProto.RemotePeerConfig{
|
||||||
|
{WgPubKey: normalKey, AllowedIps: []string{"100.64.0.1/32"}},
|
||||||
|
{WgPubKey: lazyKey, AllowedIps: []string{"100.64.0.2/32"}, LazyState: mgmProto.LazyState_LazyStateLazy},
|
||||||
|
{WgPubKey: eagerKey, AllowedIps: []string{"100.64.0.3/32"}, LazyState: mgmProto.LazyState_LazyStateEager},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
force lazyForce
|
||||||
|
remoteEnabled bool
|
||||||
|
want map[string]bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "account off: lazy-state peer lazy, normal + eager active",
|
||||||
|
force: lazyForceNone, remoteEnabled: false,
|
||||||
|
want: map[string]bool{normalKey: true, eagerKey: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "account on: only eager-state peer active",
|
||||||
|
force: lazyForceNone, remoteEnabled: true,
|
||||||
|
want: map[string]bool{eagerKey: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "force off: everything active",
|
||||||
|
force: lazyForceOff, remoteEnabled: true,
|
||||||
|
want: map[string]bool{normalKey: true, lazyKey: true, eagerKey: true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "force on: nothing active",
|
||||||
|
force: lazyForceOn, remoteEnabled: false,
|
||||||
|
want: map[string]bool{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
e := &Engine{connMgr: &ConnMgr{force: tt.force, remoteLazyEnabled: tt.remoteEnabled}}
|
||||||
|
got := e.toExcludedLazyPeers(nil, peers)
|
||||||
|
|
||||||
|
if len(got) != len(tt.want) {
|
||||||
|
t.Fatalf("toExcludedLazyPeers() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
for k := range tt.want {
|
||||||
|
if !got[k] {
|
||||||
|
t.Fatalf("expected peer %s excluded, got %v", k, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+40
-26
@@ -830,7 +830,7 @@ func (e *Engine) blockLanAccess() {
|
|||||||
|
|
||||||
// modifyPeers updates peers that have been modified (e.g. IP address has been changed).
|
// modifyPeers updates peers that have been modified (e.g. IP address has been changed).
|
||||||
// It closes the existing connection, removes it from the peerConns map, and creates a new one.
|
// It closes the existing connection, removes it from the peerConns map, and creates a new one.
|
||||||
func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig, forwardingRules []firewallManager.ForwardRule) error {
|
||||||
|
|
||||||
// first, check if peers have been modified
|
// first, check if peers have been modified
|
||||||
var modified []*mgmProto.RemotePeerConfig
|
var modified []*mgmProto.RemotePeerConfig
|
||||||
@@ -869,7 +869,7 @@ func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
|||||||
}
|
}
|
||||||
// third, add the peer connections again
|
// third, add the peer connections again
|
||||||
for _, p := range modified {
|
for _, p := range modified {
|
||||||
err := e.addNewPeer(p)
|
err := e.addNewPeer(p, forwardingRules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1570,15 +1570,14 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
e.updateOfflinePeers(networkMap.GetOfflinePeers())
|
e.updateOfflinePeers(networkMap.GetOfflinePeers())
|
||||||
done()
|
done()
|
||||||
|
|
||||||
remotePeers, err := e.reconcilePeers(networkMap)
|
remotePeers, err := e.reconcilePeers(networkMap, forwardingRules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// must set the exclude list after the peers are added. Without it the manager can not figure out the peers parameters from the store
|
// must set the exclude list after the peers are added. Without it the manager can not figure out the peers parameters from the store
|
||||||
done = e.phase("lazy_exclude")
|
done = e.phase("lazy_exclude")
|
||||||
excludedLazyPeers := e.toExcludedLazyPeers(forwardingRules, remotePeers)
|
e.connMgr.SetExcludeList(e.ctx, e.toExcludedLazyPeers(forwardingRules, remotePeers))
|
||||||
e.connMgr.SetExcludeList(e.ctx, excludedLazyPeers)
|
|
||||||
done()
|
done()
|
||||||
|
|
||||||
e.networkSerial = serial
|
e.networkSerial = serial
|
||||||
@@ -1588,8 +1587,10 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
|
|
||||||
// reconcilePeers applies the remote peer list from the network map (removing,
|
// reconcilePeers applies the remote peer list from the network map (removing,
|
||||||
// modifying and adding peers, then updating SSH config) and returns the remote
|
// modifying and adding peers, then updating SSH config) and returns the remote
|
||||||
// peers with our own peer filtered out, for use by later sync steps.
|
// peers with our own peer filtered out, for use by later sync steps. The
|
||||||
func (e *Engine) reconcilePeers(networkMap *mgmProto.NetworkMap) ([]*mgmProto.RemotePeerConfig, error) {
|
// forwarding rules are used to decide whether a newly added peer needs an
|
||||||
|
// always-active connection.
|
||||||
|
func (e *Engine) reconcilePeers(networkMap *mgmProto.NetworkMap, forwardingRules []firewallManager.ForwardRule) ([]*mgmProto.RemotePeerConfig, error) {
|
||||||
// Filter out own peer from the remote peers list
|
// Filter out own peer from the remote peers list
|
||||||
localPubKey := e.config.WgPrivateKey.PublicKey().String()
|
localPubKey := e.config.WgPrivateKey.PublicKey().String()
|
||||||
remotePeers := make([]*mgmProto.RemotePeerConfig, 0, len(networkMap.GetRemotePeers()))
|
remotePeers := make([]*mgmProto.RemotePeerConfig, 0, len(networkMap.GetRemotePeers()))
|
||||||
@@ -1617,14 +1618,14 @@ func (e *Engine) reconcilePeers(networkMap *mgmProto.NetworkMap) ([]*mgmProto.Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
done = e.phase("modified_peers")
|
done = e.phase("modified_peers")
|
||||||
err = e.modifyPeers(remotePeers)
|
err = e.modifyPeers(remotePeers, forwardingRules)
|
||||||
done()
|
done()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
done = e.phase("added_peers")
|
done = e.phase("added_peers")
|
||||||
err = e.addNewPeers(remotePeers)
|
err = e.addNewPeers(remotePeers, forwardingRules)
|
||||||
done()
|
done()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -1820,9 +1821,9 @@ func addrToString(addr netip.Addr) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addNewPeers adds peers that were not know before but arrived from the Management service with the update
|
// addNewPeers adds peers that were not know before but arrived from the Management service with the update
|
||||||
func (e *Engine) addNewPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
func (e *Engine) addNewPeers(peersUpdate []*mgmProto.RemotePeerConfig, forwardingRules []firewallManager.ForwardRule) error {
|
||||||
for _, p := range peersUpdate {
|
for _, p := range peersUpdate {
|
||||||
err := e.addNewPeer(p)
|
err := e.addNewPeer(p, forwardingRules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1830,8 +1831,9 @@ func (e *Engine) addNewPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// addNewPeer add peer if connection doesn't exist
|
// addNewPeer add peer if connection doesn't exist. A peer that is not lazy by
|
||||||
func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig) error {
|
// policy (or is a forwarder) gets an always-active connection instead.
|
||||||
|
func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig, forwardingRules []firewallManager.ForwardRule) error {
|
||||||
peerKey := peerConfig.GetWgPubKey()
|
peerKey := peerConfig.GetWgPubKey()
|
||||||
peerIPs := make([]netip.Prefix, 0, len(peerConfig.GetAllowedIps()))
|
peerIPs := make([]netip.Prefix, 0, len(peerConfig.GetAllowedIps()))
|
||||||
if _, ok := e.peerStore.PeerConn(peerKey); ok {
|
if _, ok := e.peerStore.PeerConn(peerKey); ok {
|
||||||
@@ -1865,7 +1867,7 @@ func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig) error {
|
|||||||
log.Warnf("error adding peer %s to status recorder, got error: %v", peerKey, err)
|
log.Warnf("error adding peer %s to status recorder, got error: %v", peerKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if exists := e.connMgr.AddPeerConn(e.ctx, peerKey, conn); exists {
|
if exists := e.connMgr.AddPeerConn(e.ctx, peerKey, conn, e.isPermanentPeer(peerConfig, forwardingRules)); exists {
|
||||||
conn.Close(false)
|
conn.Close(false)
|
||||||
return fmt.Errorf("peer already exists: %s", peerKey)
|
return fmt.Errorf("peer already exists: %s", peerKey)
|
||||||
}
|
}
|
||||||
@@ -2692,25 +2694,37 @@ func (e *Engine) updateForwardRules(rules []*mgmProto.ForwardingRule) ([]firewal
|
|||||||
return forwardingRules, nberrors.FormatErrorOrNil(merr)
|
return forwardingRules, nberrors.FormatErrorOrNil(merr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// toExcludedLazyPeers returns the peers that must have an always-active
|
||||||
|
// connection, so the caller can reconcile the lazy manager's exclude list.
|
||||||
func (e *Engine) toExcludedLazyPeers(rules []firewallManager.ForwardRule, peers []*mgmProto.RemotePeerConfig) map[string]bool {
|
func (e *Engine) toExcludedLazyPeers(rules []firewallManager.ForwardRule, peers []*mgmProto.RemotePeerConfig) map[string]bool {
|
||||||
excludedPeers := make(map[string]bool)
|
excludedPeers := make(map[string]bool)
|
||||||
|
for _, p := range peers {
|
||||||
// Ingress forward targets: inbound forwarded traffic is initiated remotely and
|
if e.isPermanentPeer(p, rules) {
|
||||||
// cannot wake a lazy connection, so the peer routing the target must stay
|
excludedPeers[p.GetWgPubKey()] = true
|
||||||
// permanently connected. AllowedIPs are already parsed on the peer conn, so
|
|
||||||
// reuse those typed prefixes instead of re-parsing the network map strings.
|
|
||||||
for _, r := range rules {
|
|
||||||
for _, p := range peers {
|
|
||||||
if e.peerRoutesAddr(p, r.TranslatedAddress) {
|
|
||||||
log.Infof("exclude forwarder peer from lazy connection: %s", p.GetWgPubKey())
|
|
||||||
excludedPeers[p.GetWgPubKey()] = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return excludedPeers
|
return excludedPeers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isPermanentPeer reports whether a peer needs an always-active connection: it
|
||||||
|
// is not lazy by policy (the per-peer lazy hint or account flag, subject to the
|
||||||
|
// local override), or it is an ingress forward target. Inbound forwarded traffic
|
||||||
|
// is initiated remotely and cannot wake a lazy connection, so the peer routing
|
||||||
|
// the target must stay permanently connected.
|
||||||
|
func (e *Engine) isPermanentPeer(p *mgmProto.RemotePeerConfig, rules []firewallManager.ForwardRule) bool {
|
||||||
|
if !e.connMgr.PeerLazyDefault(p.GetLazyState()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range rules {
|
||||||
|
if e.peerRoutesAddr(p, r.TranslatedAddress) {
|
||||||
|
log.Infof("exclude forwarder peer from lazy connection: %s", p.GetWgPubKey())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// peerRoutesAddr reports whether the peer is a router for addr, matched against
|
// peerRoutesAddr reports whether the peer is a router for addr, matched against
|
||||||
// the peer's already-parsed AllowedIPs from the store (the same typed value the
|
// the peer's already-parsed AllowedIPs from the store (the same typed value the
|
||||||
// lazy manager consumes) rather than re-parsing the network map strings.
|
// lazy manager consumes) rather than re-parsing the network map strings.
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ func TestToExcludedLazyPeers_ForwardTarget(t *testing.T) {
|
|||||||
store.AddPeerConn(targetPeerKey, newTestConn(t, targetPeerKey, "100.110.8.145/32"))
|
store.AddPeerConn(targetPeerKey, newTestConn(t, targetPeerKey, "100.110.8.145/32"))
|
||||||
store.AddPeerConn(otherPeerKey, newTestConn(t, otherPeerKey, "100.110.9.10/32"))
|
store.AddPeerConn(otherPeerKey, newTestConn(t, otherPeerKey, "100.110.9.10/32"))
|
||||||
|
|
||||||
e := &Engine{peerStore: store}
|
// Lazy on for normal peers, so the only exclusion under test is the forward target.
|
||||||
|
e := &Engine{peerStore: store, connMgr: &ConnMgr{force: lazyForceOn}}
|
||||||
|
|
||||||
peers := []*mgmProto.RemotePeerConfig{
|
peers := []*mgmProto.RemotePeerConfig{
|
||||||
{WgPubKey: targetPeerKey, AllowedIps: []string{"100.110.8.145/32"}},
|
{WgPubKey: targetPeerKey, AllowedIps: []string{"100.110.8.145/32"}},
|
||||||
@@ -67,7 +68,8 @@ func TestToExcludedLazyPeers_ForwardTarget(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestToExcludedLazyPeers_NoRules(t *testing.T) {
|
func TestToExcludedLazyPeers_NoRules(t *testing.T) {
|
||||||
e := &Engine{peerStore: peerstore.NewConnStore()}
|
// Lazy on for normal peers and no forward rules, so nothing is excluded.
|
||||||
|
e := &Engine{peerStore: peerstore.NewConnStore(), connMgr: &ConnMgr{force: lazyForceOn}}
|
||||||
|
|
||||||
peers := []*mgmProto.RemotePeerConfig{
|
peers := []*mgmProto.RemotePeerConfig{
|
||||||
{WgPubKey: "peer-a", AllowedIps: []string{"100.110.8.145/32"}},
|
{WgPubKey: "peer-a", AllowedIps: []string{"100.110.8.145/32"}},
|
||||||
|
|||||||
@@ -701,6 +701,7 @@ func toPeerCompact(p *types.ComponentPeer) *proto.PeerCompact {
|
|||||||
SupportsIpv6: p.SupportsIPv6,
|
SupportsIpv6: p.SupportsIPv6,
|
||||||
SupportsSourcePrefixes: p.SupportsSourcePrefixes,
|
SupportsSourcePrefixes: p.SupportsSourcePrefixes,
|
||||||
ServerSshAllowed: p.ServerSSHAllowed,
|
ServerSshAllowed: p.ServerSSHAllowed,
|
||||||
|
ProxyEmbedded: p.ProxyEmbedded,
|
||||||
}
|
}
|
||||||
if !p.LastLogin.IsZero() {
|
if !p.LastLogin.IsZero() {
|
||||||
pc.LastLoginUnixNano = p.LastLogin.UnixNano()
|
pc.LastLoginUnixNano = p.LastLogin.UnixNano()
|
||||||
|
|||||||
@@ -684,8 +684,8 @@ func TestEncodeNetworkMapEnvelope_GroupIDToUserIDs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestToProxyPatch_EmptyInputReturnsNil(t *testing.T) {
|
func TestToProxyPatch_EmptyInputReturnsNil(t *testing.T) {
|
||||||
assert.Nil(t, toProxyPatch(nil, "netbird.cloud", false, false))
|
assert.Nil(t, toProxyPatch(nil, "netbird.cloud", false, false, false))
|
||||||
assert.Nil(t, toProxyPatch(&types.NetworkMap{}, "netbird.cloud", false, false),
|
assert.Nil(t, toProxyPatch(&types.NetworkMap{}, "netbird.cloud", false, false, false),
|
||||||
"empty NetworkMap (no peers, rules, routes etc) → nil patch so proto3 omits the field")
|
"empty NetworkMap (no peers, rules, routes etc) → nil patch so proto3 omits the field")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -700,7 +700,7 @@ func TestToProxyPatch_PopulatesAllFields(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
|
||||||
patch := toProxyPatch(nm, "netbird.cloud", false, false)
|
patch := toProxyPatch(nm, "netbird.cloud", false, false, false)
|
||||||
|
|
||||||
require.NotNil(t, patch)
|
require.NotNil(t, patch)
|
||||||
assert.Len(t, patch.Peers, 1)
|
assert.Len(t, patch.Peers, 1)
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func ToComponentSyncResponse(
|
|||||||
DNSDomain: dnsName,
|
DNSDomain: dnsName,
|
||||||
DNSForwarderPort: dnsFwdPort,
|
DNSForwarderPort: dnsFwdPort,
|
||||||
UserIDClaim: userIDClaim,
|
UserIDClaim: userIDClaim,
|
||||||
ProxyPatch: toProxyPatch(proxyPatch, dnsName, includeIPv6, useSourcePrefixes),
|
ProxyPatch: toProxyPatch(proxyPatch, dnsName, includeIPv6, useSourcePrefixes, peer.ProxyMeta.Embedded),
|
||||||
})
|
})
|
||||||
|
|
||||||
resp := &proto.SyncResponse{
|
resp := &proto.SyncResponse{
|
||||||
@@ -104,7 +104,7 @@ func ToComponentSyncResponse(
|
|||||||
// derive them from. Components purity isn't violated: proxy data isn't
|
// derive them from. Components purity isn't violated: proxy data isn't
|
||||||
// policy-graph-derived, it's externally injected post-Calculate, so the
|
// policy-graph-derived, it's externally injected post-Calculate, so the
|
||||||
// client merges it on top of its locally-computed NetworkMap.
|
// client merges it on top of its locally-computed NetworkMap.
|
||||||
func toProxyPatch(nm *types.NetworkMap, dnsName string, includeIPv6, useSourcePrefixes bool) *proto.ProxyPatch {
|
func toProxyPatch(nm *types.NetworkMap, dnsName string, includeIPv6, useSourcePrefixes, localIsProxy bool) *proto.ProxyPatch {
|
||||||
if nm == nil {
|
if nm == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -114,8 +114,8 @@ func toProxyPatch(nm *types.NetworkMap, dnsName string, includeIPv6, useSourcePr
|
|||||||
}
|
}
|
||||||
|
|
||||||
patch := &proto.ProxyPatch{
|
patch := &proto.ProxyPatch{
|
||||||
Peers: networkmap.AppendRemotePeerConfig(nil, nm.Peers, dnsName, includeIPv6),
|
Peers: networkmap.AppendRemotePeerConfig(nil, nm.Peers, dnsName, includeIPv6, localIsProxy),
|
||||||
OfflinePeers: networkmap.AppendRemotePeerConfig(nil, nm.OfflinePeers, dnsName, includeIPv6),
|
OfflinePeers: networkmap.AppendRemotePeerConfig(nil, nm.OfflinePeers, dnsName, includeIPv6, localIsProxy),
|
||||||
FirewallRules: networkmap.ToProtocolFirewallRules(nm.FirewallRules, includeIPv6, useSourcePrefixes),
|
FirewallRules: networkmap.ToProtocolFirewallRules(nm.FirewallRules, includeIPv6, useSourcePrefixes),
|
||||||
Routes: networkmap.ToProtocolRoutes(nm.Routes),
|
Routes: networkmap.ToProtocolRoutes(nm.Routes),
|
||||||
RouteFirewallRules: networkmap.ToProtocolRoutesFirewallRules(nm.RoutesFirewallRules),
|
RouteFirewallRules: networkmap.ToProtocolRoutesFirewallRules(nm.RoutesFirewallRules),
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nb
|
|||||||
// filtered at the source (network map builder).
|
// filtered at the source (network map builder).
|
||||||
includeIPv6 := peer.SupportsIPv6() && peer.IPv6.IsValid()
|
includeIPv6 := peer.SupportsIPv6() && peer.IPv6.IsValid()
|
||||||
useSourcePrefixes := peer.SupportsSourcePrefixes()
|
useSourcePrefixes := peer.SupportsSourcePrefixes()
|
||||||
|
localIsProxy := peer.ProxyMeta.Embedded
|
||||||
|
|
||||||
response := &proto.SyncResponse{
|
response := &proto.SyncResponse{
|
||||||
PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, settings, httpConfig, deviceFlowConfig, networkMap.EnableSSH, networkMap.ForceRoutingPeerDNSResolution),
|
PeerConfig: toPeerConfig(peer, networkMap.Network, dnsName, settings, httpConfig, deviceFlowConfig, networkMap.EnableSSH, networkMap.ForceRoutingPeerDNSResolution),
|
||||||
@@ -179,7 +180,7 @@ func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nb
|
|||||||
response.NetworkMap.PeerConfig = response.PeerConfig
|
response.NetworkMap.PeerConfig = response.PeerConfig
|
||||||
|
|
||||||
remotePeers := make([]*proto.RemotePeerConfig, 0, len(networkMap.Peers)+len(networkMap.OfflinePeers))
|
remotePeers := make([]*proto.RemotePeerConfig, 0, len(networkMap.Peers)+len(networkMap.OfflinePeers))
|
||||||
remotePeers = networkmap.AppendRemotePeerConfig(remotePeers, networkMap.Peers, dnsName, includeIPv6)
|
remotePeers = networkmap.AppendRemotePeerConfig(remotePeers, networkMap.Peers, dnsName, includeIPv6, localIsProxy)
|
||||||
|
|
||||||
if !shouldSkipSendingDeprecatedRemotePeers(peer.Meta.WtVersion) {
|
if !shouldSkipSendingDeprecatedRemotePeers(peer.Meta.WtVersion) {
|
||||||
response.RemotePeers = remotePeers
|
response.RemotePeers = remotePeers
|
||||||
@@ -189,7 +190,7 @@ func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nb
|
|||||||
response.RemotePeersIsEmpty = len(remotePeers) == 0
|
response.RemotePeersIsEmpty = len(remotePeers) == 0
|
||||||
response.NetworkMap.RemotePeersIsEmpty = response.RemotePeersIsEmpty
|
response.NetworkMap.RemotePeersIsEmpty = response.RemotePeersIsEmpty
|
||||||
|
|
||||||
response.NetworkMap.OfflinePeers = networkmap.AppendRemotePeerConfig(nil, networkMap.OfflinePeers, dnsName, includeIPv6)
|
response.NetworkMap.OfflinePeers = networkmap.AppendRemotePeerConfig(nil, networkMap.OfflinePeers, dnsName, includeIPv6, localIsProxy)
|
||||||
|
|
||||||
firewallRules := networkmap.ToProtocolFirewallRules(networkMap.FirewallRules, includeIPv6, useSourcePrefixes)
|
firewallRules := networkmap.ToProtocolFirewallRules(networkMap.FirewallRules, includeIPv6, useSourcePrefixes)
|
||||||
response.NetworkMap.FirewallRules = firewallRules
|
response.NetworkMap.FirewallRules = firewallRules
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ func (p *Peer) ToComponent() *sharedTypes.ComponentPeer {
|
|||||||
SupportsIPv6: p.SupportsIPv6(),
|
SupportsIPv6: p.SupportsIPv6(),
|
||||||
LoginExpirationEnabled: p.LoginExpirationEnabled,
|
LoginExpirationEnabled: p.LoginExpirationEnabled,
|
||||||
AddedWithSSOLogin: p.AddedWithSSOLogin(),
|
AddedWithSSOLogin: p.AddedWithSSOLogin(),
|
||||||
|
ProxyEmbedded: p.ProxyMeta.Embedded,
|
||||||
}
|
}
|
||||||
if p.LastLogin != nil {
|
if p.LastLogin != nil {
|
||||||
cp.LastLogin = *p.LastLogin
|
cp.LastLogin = *p.LastLogin
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ func decodePeerCompact(pc *proto.PeerCompact, peerID string) *types.ComponentPee
|
|||||||
SupportsIPv6: pc.SupportsIpv6,
|
SupportsIPv6: pc.SupportsIpv6,
|
||||||
ServerSSHAllowed: pc.ServerSshAllowed,
|
ServerSSHAllowed: pc.ServerSshAllowed,
|
||||||
AddedWithSSOLogin: pc.AddedWithSsoLogin,
|
AddedWithSSOLogin: pc.AddedWithSsoLogin,
|
||||||
|
ProxyEmbedded: pc.ProxyEmbedded,
|
||||||
}
|
}
|
||||||
if pc.LastLoginUnixNano != 0 {
|
if pc.LastLoginUnixNano != 0 {
|
||||||
peer.LastLogin = time.Unix(0, pc.LastLoginUnixNano)
|
peer.LastLogin = time.Unix(0, pc.LastLoginUnixNano)
|
||||||
|
|||||||
@@ -272,8 +272,9 @@ func ToProtocolDNSConfig(update nbdns.Config, cache DNSConfigCache, forwardPort
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AppendRemotePeerConfig appends typed peers as proto.RemotePeerConfig
|
// AppendRemotePeerConfig appends typed peers as proto.RemotePeerConfig
|
||||||
// entries to dst and returns the result.
|
// entries to dst and returns the result. localIsProxy reports whether the peer
|
||||||
func AppendRemotePeerConfig(dst []*proto.RemotePeerConfig, peers []*types.ComponentPeer, dnsName string, includeIPv6 bool) []*proto.RemotePeerConfig {
|
// receiving this config is itself an embedded proxy.
|
||||||
|
func AppendRemotePeerConfig(dst []*proto.RemotePeerConfig, peers []*types.ComponentPeer, dnsName string, includeIPv6 bool, localIsProxy bool) []*proto.RemotePeerConfig {
|
||||||
for _, rPeer := range peers {
|
for _, rPeer := range peers {
|
||||||
allowedIPs := []string{rPeer.IP.String() + "/32"}
|
allowedIPs := []string{rPeer.IP.String() + "/32"}
|
||||||
if includeIPv6 && rPeer.IPv6.IsValid() {
|
if includeIPv6 && rPeer.IPv6.IsValid() {
|
||||||
@@ -285,11 +286,24 @@ func AppendRemotePeerConfig(dst []*proto.RemotePeerConfig, peers []*types.Compon
|
|||||||
SshConfig: &proto.SSHConfig{SshPubKey: []byte(rPeer.SSHKey)},
|
SshConfig: &proto.SSHConfig{SshPubKey: []byte(rPeer.SSHKey)},
|
||||||
Fqdn: rPeer.FQDN(dnsName),
|
Fqdn: rPeer.FQDN(dnsName),
|
||||||
AgentVersion: rPeer.AgentVersion,
|
AgentVersion: rPeer.AgentVersion,
|
||||||
|
LazyState: lazyStateFor(localIsProxy, rPeer),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lazyStateFor returns the per-peer lazy override for a remote peer. Connections
|
||||||
|
// involving an ephemeral proxy peer on either endpoint default to lazy so shared
|
||||||
|
// proxy infrastructure is not kept permanently connected to every peer. All
|
||||||
|
// other peers follow the account-wide flag. A future admin-facing per-peer
|
||||||
|
// setting can return LazyStateEager here to force a peer always-active.
|
||||||
|
func lazyStateFor(localIsProxy bool, rPeer *types.ComponentPeer) proto.LazyState {
|
||||||
|
if localIsProxy || rPeer.ProxyEmbedded {
|
||||||
|
return proto.LazyState_LazyStateLazy
|
||||||
|
}
|
||||||
|
return proto.LazyState_LazyStateDefault
|
||||||
|
}
|
||||||
|
|
||||||
// BuildAuthorizedUsersProto deduplicates user-IDs into a hashed list and
|
// BuildAuthorizedUsersProto deduplicates user-IDs into a hashed list and
|
||||||
// builds per-machine-user index maps. Returns (hashedUsers, machineUsers).
|
// builds per-machine-user index maps. Returns (hashedUsers, machineUsers).
|
||||||
// Errors from individual hash failures are logged via the provided context;
|
// Errors from individual hash failures are logged via the provided context;
|
||||||
|
|||||||
@@ -74,11 +74,11 @@ func EnvelopeToNetworkMap(ctx context.Context, env *proto.NetworkMapEnvelope, lo
|
|||||||
protoNM.Routes = ToProtocolRoutes(typedNM.Routes)
|
protoNM.Routes = ToProtocolRoutes(typedNM.Routes)
|
||||||
protoNM.DNSConfig = ToProtocolDNSConfig(typedNM.DNSConfig, nil, dnsFwdPort)
|
protoNM.DNSConfig = ToProtocolDNSConfig(typedNM.DNSConfig, nil, dnsFwdPort)
|
||||||
|
|
||||||
remotePeers := AppendRemotePeerConfig(nil, typedNM.Peers, dnsName, includeIPv6)
|
remotePeers := AppendRemotePeerConfig(nil, typedNM.Peers, dnsName, includeIPv6, localPeer.ProxyEmbedded)
|
||||||
protoNM.RemotePeers = remotePeers
|
protoNM.RemotePeers = remotePeers
|
||||||
protoNM.RemotePeersIsEmpty = len(remotePeers) == 0
|
protoNM.RemotePeersIsEmpty = len(remotePeers) == 0
|
||||||
|
|
||||||
protoNM.OfflinePeers = AppendRemotePeerConfig(nil, typedNM.OfflinePeers, dnsName, includeIPv6)
|
protoNM.OfflinePeers = AppendRemotePeerConfig(nil, typedNM.OfflinePeers, dnsName, includeIPv6, localPeer.ProxyEmbedded)
|
||||||
|
|
||||||
firewallRules := ToProtocolFirewallRules(typedNM.FirewallRules, includeIPv6, useSourcePrefixes)
|
firewallRules := ToProtocolFirewallRules(typedNM.FirewallRules, includeIPv6, useSourcePrefixes)
|
||||||
protoNM.FirewallRules = firewallRules
|
protoNM.FirewallRules = firewallRules
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -497,6 +497,22 @@ message RemotePeerConfig {
|
|||||||
string fqdn = 4;
|
string fqdn = 4;
|
||||||
|
|
||||||
string agentVersion = 5;
|
string agentVersion = 5;
|
||||||
|
|
||||||
|
// lazyState is the management per-peer override for lazy (on-demand)
|
||||||
|
// connections to this remote peer. LazyStateDefault follows the account-wide
|
||||||
|
// flag; LazyStateLazy forces lazy; LazyStateEager forces an always-active
|
||||||
|
// connection. A local NB_LAZY_CONN/MDM override still wins over this.
|
||||||
|
LazyState lazyState = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LazyState is the management per-peer override for lazy connections.
|
||||||
|
enum LazyState {
|
||||||
|
// Follow the account-wide lazy connection flag.
|
||||||
|
LazyStateDefault = 0;
|
||||||
|
// Force a lazy (on-demand) connection regardless of the account flag.
|
||||||
|
LazyStateLazy = 1;
|
||||||
|
// Force an always-active connection regardless of the account flag.
|
||||||
|
LazyStateEager = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSHConfig represents SSH configurations of a peer.
|
// SSHConfig represents SSH configurations of a peer.
|
||||||
@@ -1012,6 +1028,11 @@ message PeerCompact {
|
|||||||
// (port 22022) is only added when this flag is set and the peer agent
|
// (port 22022) is only added when this flag is set and the peer agent
|
||||||
// version supports it.
|
// version supports it.
|
||||||
bool server_ssh_allowed = 13;
|
bool server_ssh_allowed = 13;
|
||||||
|
|
||||||
|
// Mirror of types.Peer.ProxyMeta.Embedded. Connections involving an
|
||||||
|
// ephemeral proxy peer on either endpoint default to lazy, so this bit
|
||||||
|
// feeds the per-peer lazyState emitted in RemotePeerConfig.
|
||||||
|
bool proxy_embedded = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PolicyCompact is the compact form of a policy rule. Group references use
|
// PolicyCompact is the compact form of a policy rule. Group references use
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ type ComponentPeer struct {
|
|||||||
LoginExpirationEnabled bool
|
LoginExpirationEnabled bool
|
||||||
AddedWithSSOLogin bool
|
AddedWithSSOLogin bool
|
||||||
LastLogin time.Time
|
LastLogin time.Time
|
||||||
|
// ProxyEmbedded marks an ephemeral embedded proxy peer. Connections
|
||||||
|
// involving such a peer on either endpoint default to lazy.
|
||||||
|
ProxyEmbedded bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// FQDN returns the peer's FQDN combined of the peer's DNS label and the system's DNS domain.
|
// FQDN returns the peer's FQDN combined of the peer's DNS label and the system's DNS domain.
|
||||||
|
|||||||
Reference in New Issue
Block a user