mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-13 00:49:07 +02:00
[management,client] Carry the debug upload destination on PeerConfig
A destination set from the dashboard never reached a connected peer. The peer fan-out that follows a settings change builds its sync with a nil server config, and toNetbirdConfig returns nil for that, so no NetbirdConfig went out at all and the peer kept whatever it had. The new URL only arrived when the peer reconnected, and until then its bundles kept going to NetBird's upload service — which is the disclosure this PR exists to close. PeerConfig is the message the fan-out already fills from the account settings, alongside the other knobs an operator flips at runtime: lazy connection, MTU, routing-peer DNS resolution, auto-update. The destination belongs with them. Sending a partial NetbirdConfig from the fan-out instead would be worse: clients treat a non-nil one as authoritative for relay and would drop their relay URLs. Start now reads the destination off the PeerConfig it was given in the previous commit, for the same reason it used to read it off NetbirdConfig: without it, a bundle collected between login and the first sync sees no destination, and the first seconds after a start are exactly when someone collects a bundle about a start that went wrong. DebugConfig is removed rather than deprecated. It was added earlier in this branch and has never been in a release, so no deployed client reads it.
This commit is contained in:
@@ -50,7 +50,7 @@ func ToComponentSyncResponse(
|
||||
// TODO (dmitri) consider using invariants?
|
||||
//
|
||||
enableSSH := computeSSHEnabledForPeer(components, peer)
|
||||
peerConfig := toPeerConfig(peer, components.Network, dnsName, settings, httpConfig, deviceFlowConfig, enableSSH, components.ForceRoutingPeerDNSResolution)
|
||||
peerConfig := toPeerConfig(peer, components.Network, dnsName, settings, httpConfig, deviceFlowConfig, enableSSH, components.ForceRoutingPeerDNSResolution, debugUploadDefault)
|
||||
|
||||
includeIPv6 := peer.SupportsIPv6() && peer.IPv6.IsValid()
|
||||
useSourcePrefixes := peer.SupportsSourcePrefixes()
|
||||
@@ -76,7 +76,7 @@ func ToComponentSyncResponse(
|
||||
Version: int32(sharedgrpc.ComponentNetworkMap),
|
||||
}
|
||||
|
||||
nbConfig := toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings, settings, debugUploadDefault)
|
||||
nbConfig := toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings, settings)
|
||||
resp.NetbirdConfig = integrationsConfig.ExtendNetBirdConfig(peer.ID, peerGroups, nbConfig, extraSettings)
|
||||
|
||||
// settings == nil → field stays nil → "no info in this snapshot", client
|
||||
|
||||
@@ -46,7 +46,7 @@ func init() {
|
||||
// nil when no server config is set (the fan-out network-map path) because clients treat any
|
||||
// non-nil config as authoritative: a config without a relay section is interpreted as relay
|
||||
// disabled and wipes the clients' relay URLs.
|
||||
func toNetbirdConfig(config *nbconfig.Config, turnCredentials *Token, relayToken *Token, extraSettings *types.ExtraSettings, settings *nmdata.AccountSettingsInfo, debugUploadDefault string) *proto.NetbirdConfig {
|
||||
func toNetbirdConfig(config *nbconfig.Config, turnCredentials *Token, relayToken *Token, extraSettings *types.ExtraSettings, settings *nmdata.AccountSettingsInfo) *proto.NetbirdConfig {
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -115,19 +115,19 @@ func toNetbirdConfig(config *nbconfig.Config, turnCredentials *Token, relayToken
|
||||
}
|
||||
}
|
||||
|
||||
// Always sent, empty included: this is a full config, so the peer can tell an
|
||||
// operator clearing the destination from the partial updates that carry only
|
||||
// TURN or relay credentials and say nothing about it.
|
||||
nbConfig.Debug = &proto.DebugConfig{UploadUrl: resolveDebugUploadURL(settings, debugUploadDefault)}
|
||||
|
||||
return nbConfig
|
||||
}
|
||||
|
||||
// resolveDebugUploadURL picks the debug-bundle destination a peer is told to
|
||||
// resolveDebugUploadURL picks the debug-bundle destination this peer is told to
|
||||
// use. The account setting wins; deploymentDefault is the server-config value a
|
||||
// self-hosted install sets once for every account. Both are https-validated
|
||||
// where they are written, and neither set means the peer falls back to the
|
||||
// service NetBird runs.
|
||||
//
|
||||
// The deployment default is passed in rather than read from the server config
|
||||
// here because the peer fan-out builds its sync with a nil server config: left
|
||||
// to that, a settings change would publish an empty destination and silently
|
||||
// move the deployment's bundles back to NetBird's service.
|
||||
func resolveDebugUploadURL(settings *nmdata.AccountSettingsInfo, deploymentDefault string) string {
|
||||
if settings != nil && settings.DebugBundleUploadURL != "" {
|
||||
return settings.DebugBundleUploadURL
|
||||
@@ -135,7 +135,7 @@ func resolveDebugUploadURL(settings *nmdata.AccountSettingsInfo, deploymentDefau
|
||||
return deploymentDefault
|
||||
}
|
||||
|
||||
func toPeerConfig(peer *nmdata.Peer, network *nmdata.Network, dnsName string, settings *nmdata.AccountSettingsInfo, httpConfig *nbconfig.HttpServerConfig, deviceFlowConfig *nbconfig.DeviceAuthorizationFlow, enableSSH bool, forceRoutingPeerDNS bool) *proto.PeerConfig {
|
||||
func toPeerConfig(peer *nmdata.Peer, network *nmdata.Network, dnsName string, settings *nmdata.AccountSettingsInfo, httpConfig *nbconfig.HttpServerConfig, deviceFlowConfig *nbconfig.DeviceAuthorizationFlow, enableSSH bool, forceRoutingPeerDNS bool, debugUploadDefault string) *proto.PeerConfig {
|
||||
netmask, _ := network.Net.Mask.Size()
|
||||
fqdn := peer.FQDN(dnsName)
|
||||
|
||||
@@ -157,6 +157,7 @@ func toPeerConfig(peer *nmdata.Peer, network *nmdata.Network, dnsName string, se
|
||||
Version: settings.AutoUpdateVersion,
|
||||
AlwaysUpdate: settings.AutoUpdateAlways,
|
||||
},
|
||||
DebugBundleUploadUrl: resolveDebugUploadURL(settings, debugUploadDefault),
|
||||
}
|
||||
|
||||
if peer.SupportsIPv6() && peer.IPv6.IsValid() && network.NetV6.IP != nil {
|
||||
@@ -179,17 +180,17 @@ func ToSyncResponse(ctx context.Context, config *nbconfig.Config, httpConfig *nb
|
||||
localIsProxy := peer.ProxyMeta.Embedded
|
||||
|
||||
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, debugUploadDefault),
|
||||
NetworkMap: &proto.NetworkMap{
|
||||
Serial: networkMap.Network.CurrentSerial(),
|
||||
Routes: networkmap.ToProtocolRoutes(networkMap.Routes),
|
||||
DNSConfig: networkmap.ToProtocolDNSConfig(networkMap.DNSConfig, dnsCache, dnsFwdPort),
|
||||
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, debugUploadDefault),
|
||||
},
|
||||
Checks: toProtocolChecks(ctx, checks),
|
||||
}
|
||||
|
||||
nbConfig := toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings, settings, debugUploadDefault)
|
||||
nbConfig := toNetbirdConfig(config, turnCredentials, relayCredentials, extraSettings, settings)
|
||||
extendedConfig := integrationsConfig.ExtendNetBirdConfig(peer.ID, peerGroups, nbConfig, extraSettings)
|
||||
response.NetbirdConfig = extendedConfig
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ func TestToNetbirdConfig_RelayInvariant(t *testing.T) {
|
||||
settings := &types.Settings{MetricsPushEnabled: true}
|
||||
|
||||
t.Run("nil server config returns nil config", func(t *testing.T) {
|
||||
nbCfg := toNetbirdConfig(nil, nil, nil, nil, types.TwinAccountSettings(settings), "")
|
||||
nbCfg := toNetbirdConfig(nil, nil, nil, nil, types.TwinAccountSettings(settings))
|
||||
assert.Nil(t, nbCfg, "fan-out updates must not carry a partial NetbirdConfig even when settings are present")
|
||||
})
|
||||
|
||||
@@ -293,7 +293,7 @@ func TestToNetbirdConfig_RelayInvariant(t *testing.T) {
|
||||
}
|
||||
relayToken := &Token{Payload: "token-payload", Signature: "token-signature"}
|
||||
|
||||
nbCfg := toNetbirdConfig(cfg, nil, relayToken, nil, types.TwinAccountSettings(settings), "")
|
||||
nbCfg := toNetbirdConfig(cfg, nil, relayToken, nil, types.TwinAccountSettings(settings))
|
||||
require.NotNil(t, nbCfg)
|
||||
require.NotNil(t, nbCfg.Relay, "non-nil NetbirdConfig must include the relay section")
|
||||
assert.Equal(t, cfg.Relay.Addresses, nbCfg.Relay.Urls, "relay URLs should match the server config")
|
||||
@@ -329,9 +329,56 @@ func TestToPeerConfig_RoutingPeerDNSResolution(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
settings := &types.Settings{RoutingPeerDNSResolutionEnabled: tt.globalFlag}
|
||||
cfg := toPeerConfig(types.TwinPeer(newPeer(tt.embedded)), types.TwinNetwork(network), "netbird.selfhosted", types.TwinAccountSettings(settings), nil, nil, false, tt.forceParam)
|
||||
cfg := toPeerConfig(types.TwinPeer(newPeer(tt.embedded)), types.TwinNetwork(network), "netbird.selfhosted", types.TwinAccountSettings(settings), nil, nil, false, tt.forceParam, "")
|
||||
assert.Equal(t, tt.wantEnabled, cfg.RoutingPeerDnsResolutionEnabled,
|
||||
"RoutingPeerDnsResolutionEnabled should reflect global || embedded || forced")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// The debug-bundle destination rides PeerConfig because the peer fan-out that
|
||||
// follows a settings change builds its sync with a nil server config. The
|
||||
// deployment-wide default therefore has to reach toPeerConfig as a value: left
|
||||
// to be read off the server config, a fan-out would publish an empty
|
||||
// destination and move the deployment's bundles back to NetBird's service.
|
||||
func TestToPeerConfigDebugUploadURL(t *testing.T) {
|
||||
network := &types.Network{Net: net.IPNet{IP: net.IPv4(100, 0, 0, 0), Mask: net.CIDRMask(8, 32)}}
|
||||
peer := &nbpeer.Peer{IP: netip.MustParseAddr("100.0.0.1")}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
accountSetting string
|
||||
deploymentDefault string
|
||||
want string
|
||||
}{
|
||||
{name: "neither set leaves the peer on the NetBird service"},
|
||||
{
|
||||
name: "the account setting is published",
|
||||
accountSetting: "https://account.example.com/upload-url",
|
||||
want: "https://account.example.com/upload-url",
|
||||
},
|
||||
{
|
||||
// What the fan-out sends: no server config in hand, so this is the
|
||||
// value the controller passes in.
|
||||
name: "the deployment default is published when the account names none",
|
||||
deploymentDefault: "https://deployment.example.com/upload-url",
|
||||
want: "https://deployment.example.com/upload-url",
|
||||
},
|
||||
{
|
||||
name: "the account setting wins over the deployment default",
|
||||
accountSetting: "https://account.example.com/upload-url",
|
||||
deploymentDefault: "https://deployment.example.com/upload-url",
|
||||
want: "https://account.example.com/upload-url",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
settings := &types.Settings{DebugBundleUploadURL: tt.accountSetting}
|
||||
cfg := toPeerConfig(types.TwinPeer(peer), types.TwinNetwork(network), "netbird.selfhosted",
|
||||
types.TwinAccountSettings(settings), nil, nil, false, false, tt.deploymentDefault)
|
||||
assert.Equal(t, tt.want, cfg.GetDebugBundleUploadUrl(),
|
||||
"the peer must be told the destination its operator configured")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,8 +828,8 @@ func (s *Server) prepareLoginResponse(ctx context.Context, peer *nbpeer.Peer, ne
|
||||
|
||||
// if peer has reached this point then it has logged in
|
||||
loginResp := &proto.LoginResponse{
|
||||
NetbirdConfig: toNetbirdConfig(s.config, nil, relayToken, nil, types.TwinAccountSettings(settings), s.config.DebugUpload.URL),
|
||||
PeerConfig: toPeerConfig(types.TwinPeer(peer), types.TwinNetwork(network), s.networkMapController.GetDNSDomain(settings), types.TwinAccountSettings(settings), s.config.HttpConfig, s.config.DeviceAuthorizationFlow, enableSSH, false),
|
||||
NetbirdConfig: toNetbirdConfig(s.config, nil, relayToken, nil, types.TwinAccountSettings(settings)),
|
||||
PeerConfig: toPeerConfig(types.TwinPeer(peer), types.TwinNetwork(network), s.networkMapController.GetDNSDomain(settings), types.TwinAccountSettings(settings), s.config.HttpConfig, s.config.DeviceAuthorizationFlow, enableSSH, false, s.config.DebugUpload.URL),
|
||||
Checks: toProtocolChecks(ctx, postureChecks),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user