[management] use peer.Status.LastSeen field to detect stale peer.Status.Connected values (#7268)

This commit is contained in:
dmitri-netbird
2026-10-28 18:39:51 +01:00
committed by GitHub
parent 4ff3267542
commit dea73ff13f
20 changed files with 348 additions and 30 deletions
@@ -11,7 +11,7 @@ import (
const (
GetPeersQuery = `
select id, key, ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
peer_status_requires_approval, peer_status_connected, peer_status_last_seen, proxy_meta_embedded, proxy_meta_cluster,
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files, meta_certificates, meta_capabilities, meta_flags, meta_sync_message_version,
location_country_code, location_city_name, location_connection_ip
from peers
@@ -11,6 +11,7 @@ import (
"github.com/miekg/dns"
"github.com/netbirdio/netbird/management/server/integrations/integrated_validator"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/shared/management/networkmap"
"github.com/netbirdio/netbird/shared/management/networkmap/nmdata"
@@ -186,6 +187,7 @@ type Peer struct {
IPv6 []byte `nmap:"json"`
PeerStatusRequiresApproval sql.NullBool `nmap:"map_to:RequiresApproval"`
PeerStatusConnected sql.NullBool `nmap:"skip"`
PeerStatusLastSeen sql.NullTime `nmap:"skip"`
ProxyMetaEmbedded sql.NullBool `nmap:"skip"`
ProxyMetaCluster sql.NullString `nmap:"skip"`
MetaWtVersion sql.NullString `nmap:"skip"`
@@ -318,8 +320,8 @@ func ConvertToNmdataPeers(peers []Peer) ([]nmdata.Peer, map[string][]*nmdata.Pee
}
dp.ProxyMeta.Cluster = p.ProxyMetaCluster.String
// This is only used to build private service candidates, not connected peers are skipped
dp.Connected = p.PeerStatusConnected.Bool
if dp.ProxyMeta.Embedded && p.PeerStatusConnected.Bool {
dp.Connected = nbpeer.IsConnected(p.PeerStatusConnected.Bool, p.PeerStatusLastSeen.Time)
if dp.ProxyMeta.Embedded && dp.Connected {
clusterToPeerIdx[p.ProxyMetaCluster.String] = append(clusterToPeerIdx[p.ProxyMetaCluster.String], &dp)
}
if p.MetaWtVersion.Valid {
@@ -1,10 +1,14 @@
package networkmapdb
import (
"database/sql"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
)
func TestRecordTypeAndRdata(t *testing.T) {
@@ -58,3 +62,31 @@ func TestConvertToNmdataPeers_RejectsMalformedCertificates(t *testing.T) {
assert.Error(t, err, "a stored value that is not a list of chains must not load as no certificates")
}
// TestConvertToNmdataPeers_StaleEmbeddedProxyIsNotACandidate: a management instance that
// stops without marking its peers disconnected leaves the connected flag set. An embedded
// proxy peer in that state must not become a private service DNS target, or clients
// resolve the service to a tunnel IP that no longer answers.
func TestConvertToNmdataPeers_StaleEmbeddedProxyIsNotACandidate(t *testing.T) {
embedded := func(id string, lastSeen time.Time) Peer {
return Peer{
ID: id,
PeerStatusConnected: sql.NullBool{Bool: true, Valid: true},
PeerStatusLastSeen: sql.NullTime{Time: lastSeen, Valid: true},
ProxyMetaEmbedded: sql.NullBool{Bool: true, Valid: true},
ProxyMetaCluster: sql.NullString{String: "cluster", Valid: true},
}
}
peers, clusterToPeers, err := ConvertToNmdataPeers([]Peer{
embedded("live", time.Now().Add(-time.Minute)),
embedded("stale", time.Now().Add(-nbpeer.StaleAfter-time.Minute)),
})
require.NoError(t, err)
require.Len(t, peers, 2)
assert.True(t, peers[0].Connected)
assert.False(t, peers[1].Connected, "a peer not seen for longer than StaleAfter must read as disconnected")
require.Len(t, clusterToPeers["cluster"], 1)
assert.Equal(t, "live", clusterToPeers["cluster"][0].ID)
}
@@ -10,7 +10,7 @@ import (
const (
GetPeersQuery = `
select id, key, ssh_key, dns_label, extra_dns_labels, user_id, ssh_enabled, login_expiration_enabled, last_login, ip, ipv6,
peer_status_requires_approval, peer_status_connected, proxy_meta_embedded, proxy_meta_cluster,
peer_status_requires_approval, peer_status_connected, peer_status_last_seen, proxy_meta_embedded, proxy_meta_cluster,
meta_wt_version, meta_go_os, meta_os_version, meta_kernel_version, meta_network_addresses, meta_files, meta_certificates, meta_capabilities, meta_flags, meta_sync_message_version,
location_country_code, location_city_name, location_connection_ip
from peers