Skip to content

Commit 8d19547

Browse files
Merge branch 'main' into refactor/wire-permissions-manager
# Conflicts: # go.mod # go.sum
2 parents e8b7476 + 709e24e commit 8d19547

File tree

19 files changed

+287
-51
lines changed

19 files changed

+287
-51
lines changed

client/cmd/debug.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,14 @@ func getStatusOutput(cmd *cobra.Command, anon bool) string {
307307
if err != nil {
308308
cmd.PrintErrf("Failed to get status: %v\n", err)
309309
} else {
310+
pm := profilemanager.NewProfileManager()
311+
var profName string
312+
if activeProf, err := pm.GetActiveProfile(); err == nil {
313+
profName = activeProf.Name
314+
}
315+
310316
statusOutputString = nbstatus.ParseToFullDetailSummary(
311-
nbstatus.ConvertToStatusOutputOverview(statusResp, anon, "", nil, nil, nil, "", ""),
317+
nbstatus.ConvertToStatusOutputOverview(statusResp, anon, "", nil, nil, nil, "", profName),
312318
)
313319
}
314320
return statusOutputString

client/internal/debug/debug.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ nftables.txt: Anonymized nftables rules with packet counters, if --system-info f
4747
resolved_domains.txt: Anonymized resolved domain IP addresses from the status recorder.
4848
config.txt: Anonymized configuration information of the NetBird client.
4949
network_map.json: Anonymized sync response containing peer configurations, routes, DNS settings, and firewall rules.
50-
state.json: Anonymized client state dump containing netbird states.
50+
state.json: Anonymized client state dump containing netbird states for the active profile.
5151
mutex.prof: Mutex profiling information.
5252
goroutine.prof: Goroutine profiling information.
5353
block.prof: Block profiling information.
@@ -564,6 +564,8 @@ func (g *BundleGenerator) addStateFile() error {
564564
return nil
565565
}
566566

567+
log.Debugf("Adding state file from: %s", path)
568+
567569
data, err := os.ReadFile(path)
568570
if err != nil {
569571
if errors.Is(err, fs.ErrNotExist) {

client/internal/dns/host_windows.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717

1818
nberrors "github.com/netbirdio/netbird/client/errors"
1919
"github.com/netbirdio/netbird/client/internal/statemanager"
20+
"github.com/netbirdio/netbird/client/internal/winregistry"
2021
)
2122

2223
var (
@@ -197,16 +198,17 @@ func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager
197198
matchDomains = append(matchDomains, "."+strings.TrimSuffix(dConf.Domain, "."))
198199
}
199200

201+
if err := r.removeDNSMatchPolicies(); err != nil {
202+
log.Errorf("cleanup old dns match policies: %s", err)
203+
}
204+
200205
if len(matchDomains) != 0 {
201206
count, err := r.addDNSMatchPolicy(matchDomains, config.ServerIP)
202207
if err != nil {
203208
return fmt.Errorf("add dns match policy: %w", err)
204209
}
205210
r.nrptEntryCount = count
206211
} else {
207-
if err := r.removeDNSMatchPolicies(); err != nil {
208-
return fmt.Errorf("remove dns match policies: %w", err)
209-
}
210212
r.nrptEntryCount = 0
211213
}
212214

@@ -273,9 +275,9 @@ func (r *registryConfigurator) configureDNSPolicy(policyPath string, domains []s
273275
return fmt.Errorf("remove existing dns policy: %w", err)
274276
}
275277

276-
regKey, _, err := registry.CreateKey(registry.LOCAL_MACHINE, policyPath, registry.SET_VALUE)
278+
regKey, _, err := winregistry.CreateVolatileKey(registry.LOCAL_MACHINE, policyPath, registry.SET_VALUE)
277279
if err != nil {
278-
return fmt.Errorf("create registry key HKEY_LOCAL_MACHINE\\%s: %w", policyPath, err)
280+
return fmt.Errorf("create volatile registry key HKEY_LOCAL_MACHINE\\%s: %w", policyPath, err)
279281
}
280282
defer closer(regKey)
281283

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package dns
2+
3+
import (
4+
"fmt"
5+
"net/netip"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
"golang.org/x/sys/windows/registry"
11+
)
12+
13+
// TestNRPTEntriesCleanupOnConfigChange tests that old NRPT entries are properly cleaned up
14+
// when the number of match domains decreases between configuration changes.
15+
func TestNRPTEntriesCleanupOnConfigChange(t *testing.T) {
16+
if testing.Short() {
17+
t.Skip("skipping registry integration test in short mode")
18+
}
19+
20+
defer cleanupRegistryKeys(t)
21+
cleanupRegistryKeys(t)
22+
23+
testIP := netip.MustParseAddr("100.64.0.1")
24+
25+
// Create a test interface registry key so updateSearchDomains doesn't fail
26+
testGUID := "{12345678-1234-1234-1234-123456789ABC}"
27+
interfacePath := `SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\` + testGUID
28+
testKey, _, err := registry.CreateKey(registry.LOCAL_MACHINE, interfacePath, registry.SET_VALUE)
29+
require.NoError(t, err, "Should create test interface registry key")
30+
testKey.Close()
31+
defer func() {
32+
_ = registry.DeleteKey(registry.LOCAL_MACHINE, interfacePath)
33+
}()
34+
35+
cfg := &registryConfigurator{
36+
guid: testGUID,
37+
gpo: false,
38+
}
39+
40+
config5 := HostDNSConfig{
41+
ServerIP: testIP,
42+
Domains: []DomainConfig{
43+
{Domain: "domain1.com", MatchOnly: true},
44+
{Domain: "domain2.com", MatchOnly: true},
45+
{Domain: "domain3.com", MatchOnly: true},
46+
{Domain: "domain4.com", MatchOnly: true},
47+
{Domain: "domain5.com", MatchOnly: true},
48+
},
49+
}
50+
51+
err = cfg.applyDNSConfig(config5, nil)
52+
require.NoError(t, err)
53+
54+
// Verify all 5 entries exist
55+
for i := 0; i < 5; i++ {
56+
exists, err := registryKeyExists(fmt.Sprintf("%s-%d", dnsPolicyConfigMatchPath, i))
57+
require.NoError(t, err)
58+
assert.True(t, exists, "Entry %d should exist after first config", i)
59+
}
60+
61+
config2 := HostDNSConfig{
62+
ServerIP: testIP,
63+
Domains: []DomainConfig{
64+
{Domain: "domain1.com", MatchOnly: true},
65+
{Domain: "domain2.com", MatchOnly: true},
66+
},
67+
}
68+
69+
err = cfg.applyDNSConfig(config2, nil)
70+
require.NoError(t, err)
71+
72+
// Verify first 2 entries exist
73+
for i := 0; i < 2; i++ {
74+
exists, err := registryKeyExists(fmt.Sprintf("%s-%d", dnsPolicyConfigMatchPath, i))
75+
require.NoError(t, err)
76+
assert.True(t, exists, "Entry %d should exist after second config", i)
77+
}
78+
79+
// Verify entries 2-4 are cleaned up
80+
for i := 2; i < 5; i++ {
81+
exists, err := registryKeyExists(fmt.Sprintf("%s-%d", dnsPolicyConfigMatchPath, i))
82+
require.NoError(t, err)
83+
assert.False(t, exists, "Entry %d should NOT exist after reducing to 2 domains", i)
84+
}
85+
}
86+
87+
func registryKeyExists(path string) (bool, error) {
88+
k, err := registry.OpenKey(registry.LOCAL_MACHINE, path, registry.QUERY_VALUE)
89+
if err != nil {
90+
if err == registry.ErrNotExist {
91+
return false, nil
92+
}
93+
return false, err
94+
}
95+
k.Close()
96+
return true, nil
97+
}
98+
99+
func cleanupRegistryKeys(*testing.T) {
100+
cfg := &registryConfigurator{nrptEntryCount: 10}
101+
_ = cfg.removeDNSMatchPolicies()
102+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package winregistry
2+
3+
import (
4+
"syscall"
5+
"unsafe"
6+
7+
"golang.org/x/sys/windows/registry"
8+
)
9+
10+
var (
11+
advapi = syscall.NewLazyDLL("advapi32.dll")
12+
regCreateKeyExW = advapi.NewProc("RegCreateKeyExW")
13+
)
14+
15+
const (
16+
// Registry key options
17+
regOptionNonVolatile = 0x0 // Key is preserved when system is rebooted
18+
regOptionVolatile = 0x1 // Key is not preserved when system is rebooted
19+
20+
// Registry disposition values
21+
regCreatedNewKey = 0x1
22+
regOpenedExistingKey = 0x2
23+
)
24+
25+
// CreateVolatileKey creates a volatile registry key named path under open key root.
26+
// CreateVolatileKey returns the new key and a boolean flag that reports whether the key already existed.
27+
// The access parameter specifies the access rights for the key to be created.
28+
//
29+
// Volatile keys are stored in memory and are automatically deleted when the system is shut down.
30+
// This provides automatic cleanup without requiring manual registry maintenance.
31+
func CreateVolatileKey(root registry.Key, path string, access uint32) (registry.Key, bool, error) {
32+
pathPtr, err := syscall.UTF16PtrFromString(path)
33+
if err != nil {
34+
return 0, false, err
35+
}
36+
37+
var (
38+
handle syscall.Handle
39+
disposition uint32
40+
)
41+
42+
ret, _, _ := regCreateKeyExW.Call(
43+
uintptr(root),
44+
uintptr(unsafe.Pointer(pathPtr)),
45+
0, // reserved
46+
0, // class
47+
uintptr(regOptionVolatile), // options - volatile key
48+
uintptr(access), // desired access
49+
0, // security attributes
50+
uintptr(unsafe.Pointer(&handle)),
51+
uintptr(unsafe.Pointer(&disposition)),
52+
)
53+
54+
if ret != 0 {
55+
return 0, false, syscall.Errno(ret)
56+
}
57+
58+
return registry.Key(handle), disposition == regOpenedExistingKey, nil
59+
}

client/ui/debug.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/skratchdot/open-golang/open"
1919

2020
"github.com/netbirdio/netbird/client/internal"
21+
"github.com/netbirdio/netbird/client/internal/profilemanager"
2122
"github.com/netbirdio/netbird/client/proto"
2223
nbstatus "github.com/netbirdio/netbird/client/status"
2324
uptypes "github.com/netbirdio/netbird/upload-server/types"
@@ -426,14 +427,20 @@ func (s *serviceClient) collectDebugData(
426427
return "", err
427428
}
428429

430+
pm := profilemanager.NewProfileManager()
431+
var profName string
432+
if activeProf, err := pm.GetActiveProfile(); err == nil {
433+
profName = activeProf.Name
434+
}
435+
429436
postUpStatus, err := conn.Status(s.ctx, &proto.StatusRequest{GetFullPeerStatus: true})
430437
if err != nil {
431438
log.Warnf("Failed to get post-up status: %v", err)
432439
}
433440

434441
var postUpStatusOutput string
435442
if postUpStatus != nil {
436-
overview := nbstatus.ConvertToStatusOutputOverview(postUpStatus, params.anonymize, "", nil, nil, nil, "", "")
443+
overview := nbstatus.ConvertToStatusOutputOverview(postUpStatus, params.anonymize, "", nil, nil, nil, "", profName)
437444
postUpStatusOutput = nbstatus.ParseToFullDetailSummary(overview)
438445
}
439446
headerPostUp := fmt.Sprintf("----- NetBird post-up - Timestamp: %s", time.Now().Format(time.RFC3339))
@@ -450,7 +457,7 @@ func (s *serviceClient) collectDebugData(
450457

451458
var preDownStatusOutput string
452459
if preDownStatus != nil {
453-
overview := nbstatus.ConvertToStatusOutputOverview(preDownStatus, params.anonymize, "", nil, nil, nil, "", "")
460+
overview := nbstatus.ConvertToStatusOutputOverview(preDownStatus, params.anonymize, "", nil, nil, nil, "", profName)
454461
preDownStatusOutput = nbstatus.ParseToFullDetailSummary(overview)
455462
}
456463
headerPreDown := fmt.Sprintf("----- NetBird pre-down - Timestamp: %s - Duration: %s",
@@ -574,14 +581,20 @@ func (s *serviceClient) createDebugBundle(anonymize bool, systemInfo bool, uploa
574581
return nil, fmt.Errorf("get client: %v", err)
575582
}
576583

584+
pm := profilemanager.NewProfileManager()
585+
var profName string
586+
if activeProf, err := pm.GetActiveProfile(); err == nil {
587+
profName = activeProf.Name
588+
}
589+
577590
statusResp, err := conn.Status(s.ctx, &proto.StatusRequest{GetFullPeerStatus: true})
578591
if err != nil {
579592
log.Warnf("failed to get status for debug bundle: %v", err)
580593
}
581594

582595
var statusOutput string
583596
if statusResp != nil {
584-
overview := nbstatus.ConvertToStatusOutputOverview(statusResp, anonymize, "", nil, nil, nil, "", "")
597+
overview := nbstatus.ConvertToStatusOutputOverview(statusResp, anonymize, "", nil, nil, nil, "", profName)
585598
statusOutput = nbstatus.ParseToFullDetailSummary(overview)
586599
}
587600

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ require (
6262
github.com/miekg/dns v1.1.59
6363
github.com/mitchellh/hashstructure/v2 v2.0.2
6464
github.com/nadoo/ipset v0.5.0
65-
github.com/netbirdio/management-integrations/integrations v0.0.0-20251020220640-8537921c9adc
65+
github.com/netbirdio/management-integrations/integrations v0.0.0-20251022080146-b1caade69396
6666
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250805121659-6b4ac470ca45
6767
github.com/okta/okta-sdk-golang/v2 v2.18.0
6868
github.com/oschwald/maxminddb-golang v1.12.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -503,8 +503,8 @@ github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944 h1:TDtJKmM6S
503503
github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ=
504504
github.com/netbirdio/ice/v4 v4.0.0-20250908184934-6202be846b51 h1:Ov4qdafATOgGMB1wbSuh+0aAHcwz9hdvB6VZjh1mVMI=
505505
github.com/netbirdio/ice/v4 v4.0.0-20250908184934-6202be846b51/go.mod h1:ZSIbPdBn5hePO8CpF1PekH2SfpTxg1PDhEwtbqZS7R8=
506-
github.com/netbirdio/management-integrations/integrations v0.0.0-20251020220640-8537921c9adc h1:5AAcrPG/+NajbBOZ+H8HTIlo20pOjtDX6BafmWVQLFs=
507-
github.com/netbirdio/management-integrations/integrations v0.0.0-20251020220640-8537921c9adc/go.mod h1:ifKa2jGPsOzZhJFo72v2AE5nMP3GYvlhoZ9JV6lHlJ8=
506+
github.com/netbirdio/management-integrations/integrations v0.0.0-20251022080146-b1caade69396 h1:aXHS63QWf0Z5fDN19Swl6npdJjGMyXthAvvgW7rbKJQ=
507+
github.com/netbirdio/management-integrations/integrations v0.0.0-20251022080146-b1caade69396/go.mod h1:v0nUbbHbuQnqR7yKIYnKzsLBCswLtp2JctmKYmGgVhc=
508508
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8=
509509
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
510510
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20250805121659-6b4ac470ca45 h1:ujgviVYmx243Ksy7NdSwrdGPSRNE3pb8kEDSpH0QuAQ=

infrastructure_files/configure.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,15 @@ if [[ "$NETBIRD_DISABLE_LETSENCRYPT" == "true" ]]; then
185185
echo "You are also free to remove any occurrences of the Letsencrypt-volume $LETSENCRYPT_VOLUMENAME"
186186
echo ""
187187

188-
export NETBIRD_SIGNAL_PROTOCOL="https"
189188
unset NETBIRD_LETSENCRYPT_DOMAIN
190189
unset NETBIRD_MGMT_API_CERT_FILE
191190
unset NETBIRD_MGMT_API_CERT_KEY_FILE
192191
fi
193192

193+
if [[ -n "$NETBIRD_MGMT_API_CERT_FILE" && -n "$NETBIRD_MGMT_API_CERT_KEY_FILE" ]]; then
194+
export NETBIRD_SIGNAL_PROTOCOL="https"
195+
fi
196+
194197
# Check if management identity provider is set
195198
if [ -n "$NETBIRD_MGMT_IDP" ]; then
196199
EXTRA_CONFIG={}

infrastructure_files/docker-compose.yml.tmpl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,21 @@ services:
4040
signal:
4141
<<: *default
4242
image: netbirdio/signal:$NETBIRD_SIGNAL_TAG
43+
depends_on:
44+
- dashboard
4345
volumes:
4446
- $SIGNAL_VOLUMENAME:/var/lib/netbird
47+
- $LETSENCRYPT_VOLUMENAME:/etc/letsencrypt:ro
4548
ports:
4649
- $NETBIRD_SIGNAL_PORT:80
4750
# # port and command for Let's Encrypt validation
4851
# - 443:443
4952
# command: ["--letsencrypt-domain", "$NETBIRD_LETSENCRYPT_DOMAIN", "--log-file", "console"]
53+
command: [
54+
"--cert-file", "$NETBIRD_MGMT_API_CERT_FILE",
55+
"--cert-key", "$NETBIRD_MGMT_API_CERT_KEY_FILE",
56+
"--log-file", "console"
57+
]
5058

5159
# Relay
5260
relay:

0 commit comments

Comments
 (0)