mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-18 16:26:38 +00:00
Add method to track and merge default interface blacklist entries
Introduce `mergeDefaultIFaceBlacklist` to ensure new defaults are appended to the interface blacklist while preserving user modifications. Add tests to verify behavior, including migration of old configs and handling of user-removed entries.
This commit is contained in:
@@ -100,6 +100,7 @@ type Config struct {
|
|||||||
WgPort int
|
WgPort int
|
||||||
NetworkMonitor *bool
|
NetworkMonitor *bool
|
||||||
IFaceBlackList []string
|
IFaceBlackList []string
|
||||||
|
IFaceBlackListAppliedDefaults []string `json:",omitempty"`
|
||||||
DisableIPv6Discovery bool
|
DisableIPv6Discovery bool
|
||||||
RosenpassEnabled bool
|
RosenpassEnabled bool
|
||||||
RosenpassPermissive bool
|
RosenpassPermissive bool
|
||||||
@@ -359,10 +360,7 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
|
|||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(config.IFaceBlackList) == 0 {
|
if changed := config.mergeDefaultIFaceBlacklist(); changed {
|
||||||
log.Infof("filling in interface blacklist with defaults: [ %s ]",
|
|
||||||
strings.Join(DefaultInterfaceBlacklist, " "))
|
|
||||||
config.IFaceBlackList = append(config.IFaceBlackList, DefaultInterfaceBlacklist...)
|
|
||||||
updated = true
|
updated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,6 +594,37 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
|
|||||||
return updated, nil
|
return updated, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mergeDefaultIFaceBlacklist ensures that new entries added to DefaultInterfaceBlacklist
|
||||||
|
// are merged into an existing IFaceBlackList on upgrade, while respecting entries that
|
||||||
|
// the user deliberately removed. It tracks which defaults have been offered via
|
||||||
|
// IFaceBlackListAppliedDefaults so removals are not undone.
|
||||||
|
func (config *Config) mergeDefaultIFaceBlacklist() (updated bool) {
|
||||||
|
if len(config.IFaceBlackList) == 0 {
|
||||||
|
log.Infof("filling in interface blacklist with defaults: [ %s ]",
|
||||||
|
strings.Join(DefaultInterfaceBlacklist, " "))
|
||||||
|
config.IFaceBlackList = append(config.IFaceBlackList, DefaultInterfaceBlacklist...)
|
||||||
|
config.IFaceBlackListAppliedDefaults = append([]string{}, DefaultInterfaceBlacklist...)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find defaults not yet tracked in AppliedDefaults — these are genuinely new.
|
||||||
|
// Entries already in AppliedDefaults were either kept or deliberately removed by the user.
|
||||||
|
newDefaults := util.SliceDiff(DefaultInterfaceBlacklist, config.IFaceBlackListAppliedDefaults)
|
||||||
|
if len(newDefaults) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only add entries not already present in the blacklist (avoid duplicates)
|
||||||
|
toAdd := util.SliceDiff(newDefaults, config.IFaceBlackList)
|
||||||
|
if len(toAdd) > 0 {
|
||||||
|
log.Infof("merging new default interface blacklist entries: [ %s ]",
|
||||||
|
strings.Join(toAdd, " "))
|
||||||
|
config.IFaceBlackList = append(config.IFaceBlackList, toAdd...)
|
||||||
|
}
|
||||||
|
config.IFaceBlackListAppliedDefaults = append(config.IFaceBlackListAppliedDefaults, newDefaults...)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// parseURL parses and validates a service URL
|
// parseURL parses and validates a service URL
|
||||||
func parseURL(serviceName, serviceURL string) (*url.URL, error) {
|
func parseURL(serviceName, serviceURL string) (*url.URL, error) {
|
||||||
parsedMgmtURL, err := url.ParseRequestURI(serviceURL)
|
parsedMgmtURL, err := url.ParseRequestURI(serviceURL)
|
||||||
|
|||||||
@@ -108,6 +108,87 @@ func TestExtraIFaceBlackList(t *testing.T) {
|
|||||||
assert.Contains(t, readConf.(*Config).IFaceBlackList, "eth1")
|
assert.Contains(t, readConf.(*Config).IFaceBlackList, "eth1")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIFaceBlackListMigratesNewDefaults(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
configPath := filepath.Join(tempDir, "config.json")
|
||||||
|
|
||||||
|
// Create a config that simulates an old install with a partial IFaceBlackList
|
||||||
|
// (missing the newer CNI entries like "cilium_", "cali", etc.)
|
||||||
|
config, err := UpdateOrCreateConfig(ConfigInput{
|
||||||
|
ConfigPath: configPath,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Simulate an old config that predates AppliedDefaults tracking:
|
||||||
|
// it has only the original entries, no CNI prefixes, and no AppliedDefaults.
|
||||||
|
oldList := []string{iface.WgInterfaceDefault, "wt", "utun", "tun0", "zt", "ZeroTier", "wg", "ts",
|
||||||
|
"Tailscale", "tailscale", "docker", "veth", "br-", "lo"}
|
||||||
|
config.IFaceBlackList = oldList
|
||||||
|
config.IFaceBlackListAppliedDefaults = nil
|
||||||
|
err = WriteOutConfig(configPath, config)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Re-read the config — apply() should merge in missing defaults
|
||||||
|
reloaded, err := GetConfig(configPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, entry := range DefaultInterfaceBlacklist {
|
||||||
|
assert.Contains(t, reloaded.IFaceBlackList, entry,
|
||||||
|
"IFaceBlackList should contain default entry %q after migration", entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify no duplicates were introduced
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
for _, entry := range reloaded.IFaceBlackList {
|
||||||
|
assert.False(t, seen[entry], "duplicate entry %q in IFaceBlackList", entry)
|
||||||
|
seen[entry] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppliedDefaults should now track all current defaults
|
||||||
|
for _, entry := range DefaultInterfaceBlacklist {
|
||||||
|
assert.Contains(t, reloaded.IFaceBlackListAppliedDefaults, entry,
|
||||||
|
"AppliedDefaults should track %q", entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-read again — should not change (idempotent)
|
||||||
|
reloaded2, err := GetConfig(configPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, reloaded.IFaceBlackList, reloaded2.IFaceBlackList,
|
||||||
|
"IFaceBlackList should be stable on subsequent reads")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIFaceBlackListRespectsUserRemoval(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
configPath := filepath.Join(tempDir, "config.json")
|
||||||
|
|
||||||
|
// Create a fresh config (all defaults applied)
|
||||||
|
config, err := UpdateOrCreateConfig(ConfigInput{
|
||||||
|
ConfigPath: configPath,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Contains(t, config.IFaceBlackList, "cali")
|
||||||
|
|
||||||
|
// User deliberately removes "cali" from their blacklist
|
||||||
|
filtered := make([]string, 0, len(config.IFaceBlackList))
|
||||||
|
for _, entry := range config.IFaceBlackList {
|
||||||
|
if entry != "cali" {
|
||||||
|
filtered = append(filtered, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.IFaceBlackList = filtered
|
||||||
|
err = WriteOutConfig(configPath, config)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Re-read — "cali" should NOT be re-added because it's in AppliedDefaults
|
||||||
|
reloaded, err := GetConfig(configPath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotContains(t, reloaded.IFaceBlackList, "cali",
|
||||||
|
"user-removed entry should not be re-added")
|
||||||
|
|
||||||
|
// AppliedDefaults should still contain "cali" (it was offered)
|
||||||
|
assert.Contains(t, reloaded.IFaceBlackListAppliedDefaults, "cali")
|
||||||
|
}
|
||||||
|
|
||||||
func TestHiddenPreSharedKey(t *testing.T) {
|
func TestHiddenPreSharedKey(t *testing.T) {
|
||||||
hidden := "**********"
|
hidden := "**********"
|
||||||
samplePreSharedKey := "mysecretpresharedkey"
|
samplePreSharedKey := "mysecretpresharedkey"
|
||||||
|
|||||||
Reference in New Issue
Block a user