diff --git a/.github/ISSUE_TEMPLATE/bug-issue-report.md b/.github/ISSUE_TEMPLATE/bug-issue-report.md
index 87f757f42..3633cca4f 100644
--- a/.github/ISSUE_TEMPLATE/bug-issue-report.md
+++ b/.github/ISSUE_TEMPLATE/bug-issue-report.md
@@ -31,14 +31,22 @@ Please specify whether you use NetBird Cloud or self-host NetBird's control plan
`netbird version`
-**NetBird status -dA output:**
+**Is any other VPN software installed?**
-If applicable, add the `netbird status -dA' command output.
+If yes, which one?
-**Do you face any (non-mobile) client issues?**
+**Debug output**
-Please provide the file created by `netbird debug for 1m -AS`.
-We advise reviewing the anonymized files for any remaining PII.
+To help us resolve the problem, please attach the following debug output
+
+ netbird status -dA
+
+As well as the file created by
+
+ netbird debug for 1m -AS
+
+
+We advise reviewing the anonymized output for any remaining personal information.
**Screenshots**
@@ -47,3 +55,10 @@ If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
+
+**Have you tried these troubleshooting steps?**
+- [ ] Checked for newer NetBird versions
+- [ ] Searched for similar issues on GitHub (including closed ones)
+- [ ] Restarted the NetBird client
+- [ ] Disabled other VPN software
+- [ ] Checked firewall settings
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 04874bdf4..919351f18 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -71,7 +71,7 @@ jobs:
- name: Install goversioninfo
run: go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@233067e
- name: Generate windows syso amd64
- run: goversioninfo -icon client/ui/netbird.ico -manifest client/manifest.xml -product-name ${{ env.PRODUCT_NAME }} -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/resources_windows_amd64.syso
+ run: goversioninfo -icon client/ui/assets/netbird.ico -manifest client/manifest.xml -product-name ${{ env.PRODUCT_NAME }} -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/resources_windows_amd64.syso
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
with:
@@ -150,7 +150,7 @@ jobs:
- name: Install goversioninfo
run: go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@233067e
- name: Generate windows syso amd64
- run: goversioninfo -64 -icon client/ui/netbird.ico -manifest client/ui/manifest.xml -product-name ${{ env.PRODUCT_NAME }}-"UI" -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/ui/resources_windows_amd64.syso
+ run: goversioninfo -64 -icon client/ui/assets/netbird.ico -manifest client/ui/manifest.xml -product-name ${{ env.PRODUCT_NAME }}-"UI" -copyright "${{ env.COPYRIGHT }}" -ver-major ${{ steps.semver_parser.outputs.major }} -ver-minor ${{ steps.semver_parser.outputs.minor }} -ver-patch ${{ steps.semver_parser.outputs.patch }} -ver-build 0 -file-version ${{ steps.semver_parser.outputs.fullversion }}.0 -product-version ${{ steps.semver_parser.outputs.fullversion }}.0 -o client/ui/resources_windows_amd64.syso
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v4
diff --git a/.goreleaser_ui.yaml b/.goreleaser_ui.yaml
index 1dd649d1b..459f204d3 100644
--- a/.goreleaser_ui.yaml
+++ b/.goreleaser_ui.yaml
@@ -53,9 +53,9 @@ nfpms:
scripts:
postinstall: "release_files/ui-post-install.sh"
contents:
- - src: client/ui/netbird.desktop
+ - src: client/ui/build/netbird.desktop
dst: /usr/share/applications/netbird.desktop
- - src: client/ui/netbird.png
+ - src: client/ui/assets/netbird.png
dst: /usr/share/pixmaps/netbird.png
dependencies:
- netbird
@@ -72,9 +72,9 @@ nfpms:
scripts:
postinstall: "release_files/ui-post-install.sh"
contents:
- - src: client/ui/netbird.desktop
+ - src: client/ui/build/netbird.desktop
dst: /usr/share/applications/netbird.desktop
- - src: client/ui/netbird.png
+ - src: client/ui/assets/netbird.png
dst: /usr/share/pixmaps/netbird.png
dependencies:
- netbird
diff --git a/README.md b/README.md
index 7cee2f8dc..5b136eff6 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,4 @@
-
- Webinar: How to Achieve Zero Trust Access to Kubernetes — Effortlessly
-
@@ -36,6 +33,10 @@
+
+
+ Webinar: Securely Access Kubernetes without Port Forwarding and Jump Hosts
+
diff --git a/client/firewall/iptables/manager_linux.go b/client/firewall/iptables/manager_linux.go
index 929e8a656..144d5a17f 100644
--- a/client/firewall/iptables/manager_linux.go
+++ b/client/firewall/iptables/manager_linux.go
@@ -166,7 +166,7 @@ func (m *Manager) SetLegacyManagement(isLegacy bool) error {
}
// Reset firewall to the default state
-func (m *Manager) Reset(stateManager *statemanager.Manager) error {
+func (m *Manager) Close(stateManager *statemanager.Manager) error {
m.mutex.Lock()
defer m.mutex.Unlock()
diff --git a/client/firewall/iptables/manager_linux_test.go b/client/firewall/iptables/manager_linux_test.go
index ba578c033..856633409 100644
--- a/client/firewall/iptables/manager_linux_test.go
+++ b/client/firewall/iptables/manager_linux_test.go
@@ -62,7 +62,7 @@ func TestIptablesManager(t *testing.T) {
time.Sleep(time.Second)
defer func() {
- err := manager.Reset(nil)
+ err := manager.Close(nil)
require.NoError(t, err, "clear the manager state")
time.Sleep(time.Second)
@@ -100,14 +100,14 @@ func TestIptablesManager(t *testing.T) {
_, err = manager.AddPeerFiltering(ip, "udp", nil, port, fw.ActionAccept, "", "accept Fake DNS traffic")
require.NoError(t, err, "failed to add rule")
- err = manager.Reset(nil)
+ err = manager.Close(nil)
require.NoError(t, err, "failed to reset")
ok, err := ipv4Client.ChainExists("filter", chainNameInputRules)
require.NoError(t, err, "failed check chain exists")
if ok {
- require.NoErrorf(t, err, "chain '%v' still exists after Reset", chainNameInputRules)
+ require.NoErrorf(t, err, "chain '%v' still exists after Close", chainNameInputRules)
}
})
}
@@ -136,7 +136,7 @@ func TestIptablesManagerIPSet(t *testing.T) {
time.Sleep(time.Second)
defer func() {
- err := manager.Reset(nil)
+ err := manager.Close(nil)
require.NoError(t, err, "clear the manager state")
time.Sleep(time.Second)
@@ -166,7 +166,7 @@ func TestIptablesManagerIPSet(t *testing.T) {
})
t.Run("reset check", func(t *testing.T) {
- err = manager.Reset(nil)
+ err = manager.Close(nil)
require.NoError(t, err, "failed to reset")
})
}
@@ -204,7 +204,7 @@ func TestIptablesCreatePerformance(t *testing.T) {
time.Sleep(time.Second)
defer func() {
- err := manager.Reset(nil)
+ err := manager.Close(nil)
require.NoError(t, err, "clear the manager state")
time.Sleep(time.Second)
diff --git a/client/firewall/iptables/state_linux.go b/client/firewall/iptables/state_linux.go
index 44b8340ba..2a7120bbf 100644
--- a/client/firewall/iptables/state_linux.go
+++ b/client/firewall/iptables/state_linux.go
@@ -62,7 +62,7 @@ func (s *ShutdownState) Cleanup() error {
ipt.aclMgr.ipsetStore = s.ACLIPsetStore
}
- if err := ipt.Reset(nil); err != nil {
+ if err := ipt.Close(nil); err != nil {
return fmt.Errorf("reset iptables manager: %w", err)
}
diff --git a/client/firewall/manager/firewall.go b/client/firewall/manager/firewall.go
index d007e20a5..e71328a44 100644
--- a/client/firewall/manager/firewall.go
+++ b/client/firewall/manager/firewall.go
@@ -94,8 +94,8 @@ type Manager interface {
// SetLegacyManagement sets the legacy management mode
SetLegacyManagement(legacy bool) error
- // Reset firewall to the default state
- Reset(stateManager *statemanager.Manager) error
+ // Close closes the firewall manager
+ Close(stateManager *statemanager.Manager) error
// Flush the changes to firewall controller
Flush() error
diff --git a/client/firewall/nftables/manager_linux.go b/client/firewall/nftables/manager_linux.go
index de68f3291..3df9b378d 100644
--- a/client/firewall/nftables/manager_linux.go
+++ b/client/firewall/nftables/manager_linux.go
@@ -87,7 +87,7 @@ func (m *Manager) Init(stateManager *statemanager.Manager) error {
// We only need to record minimal interface state for potential recreation.
// Unlike iptables, which requires tracking individual rules, nftables maintains
// a known state (our netbird table plus a few static rules). This allows for easy
- // cleanup using Reset() without needing to store specific rules.
+ // cleanup using Close() without needing to store specific rules.
if err := stateManager.UpdateState(&ShutdownState{
InterfaceState: &InterfaceState{
NameStr: m.wgIface.Name(),
@@ -242,7 +242,7 @@ func (m *Manager) SetLegacyManagement(isLegacy bool) error {
}
// Reset firewall to the default state
-func (m *Manager) Reset(stateManager *statemanager.Manager) error {
+func (m *Manager) Close(stateManager *statemanager.Manager) error {
m.mutex.Lock()
defer m.mutex.Unlock()
diff --git a/client/firewall/nftables/manager_linux_test.go b/client/firewall/nftables/manager_linux_test.go
index eaa8ef1f5..9ca20889b 100644
--- a/client/firewall/nftables/manager_linux_test.go
+++ b/client/firewall/nftables/manager_linux_test.go
@@ -65,7 +65,7 @@ func TestNftablesManager(t *testing.T) {
time.Sleep(time.Second * 3)
defer func() {
- err = manager.Reset(nil)
+ err = manager.Close(nil)
require.NoError(t, err, "failed to reset")
time.Sleep(time.Second)
}()
@@ -162,7 +162,7 @@ func TestNftablesManager(t *testing.T) {
// established rule remains
require.Len(t, rules, 1, "expected 1 rules after deletion")
- err = manager.Reset(nil)
+ err = manager.Close(nil)
require.NoError(t, err, "failed to reset")
}
@@ -191,7 +191,7 @@ func TestNFtablesCreatePerformance(t *testing.T) {
time.Sleep(time.Second * 3)
defer func() {
- if err := manager.Reset(nil); err != nil {
+ if err := manager.Close(nil); err != nil {
t.Errorf("clear the manager state: %v", err)
}
time.Sleep(time.Second)
@@ -274,7 +274,7 @@ func TestNftablesManagerCompatibilityWithIptables(t *testing.T) {
require.NoError(t, manager.Init(nil))
t.Cleanup(func() {
- err := manager.Reset(nil)
+ err := manager.Close(nil)
require.NoError(t, err, "failed to reset manager state")
// Verify iptables output after reset
diff --git a/client/firewall/nftables/router_linux_test.go b/client/firewall/nftables/router_linux_test.go
index 2a5d7168d..9081a8349 100644
--- a/client/firewall/nftables/router_linux_test.go
+++ b/client/firewall/nftables/router_linux_test.go
@@ -38,7 +38,7 @@ func TestNftablesManager_AddNatRule(t *testing.T) {
// need fw manager to init both acl mgr and router for all chains to be present
manager, err := Create(ifaceMock)
t.Cleanup(func() {
- require.NoError(t, manager.Reset(nil))
+ require.NoError(t, manager.Close(nil))
})
require.NoError(t, err)
require.NoError(t, manager.Init(nil))
@@ -127,7 +127,7 @@ func TestNftablesManager_RemoveNatRule(t *testing.T) {
t.Run(testCase.Name, func(t *testing.T) {
manager, err := Create(ifaceMock)
t.Cleanup(func() {
- require.NoError(t, manager.Reset(nil))
+ require.NoError(t, manager.Close(nil))
})
require.NoError(t, err)
require.NoError(t, manager.Init(nil))
diff --git a/client/firewall/nftables/state_linux.go b/client/firewall/nftables/state_linux.go
index a68c8b8b8..facca1cec 100644
--- a/client/firewall/nftables/state_linux.go
+++ b/client/firewall/nftables/state_linux.go
@@ -39,7 +39,7 @@ func (s *ShutdownState) Cleanup() error {
return fmt.Errorf("create nftables manager: %w", err)
}
- if err := nft.Reset(nil); err != nil {
+ if err := nft.Close(nil); err != nil {
return fmt.Errorf("reset nftables manager: %w", err)
}
diff --git a/client/firewall/uspfilter/allow_netbird.go b/client/firewall/uspfilter/allow_netbird.go
index 03f23f5e6..aba79bc21 100644
--- a/client/firewall/uspfilter/allow_netbird.go
+++ b/client/firewall/uspfilter/allow_netbird.go
@@ -8,12 +8,11 @@ import (
log "github.com/sirupsen/logrus"
- "github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
"github.com/netbirdio/netbird/client/internal/statemanager"
)
// Reset firewall to the default state
-func (m *Manager) Reset(stateManager *statemanager.Manager) error {
+func (m *Manager) Close(stateManager *statemanager.Manager) error {
m.mutex.Lock()
defer m.mutex.Unlock()
@@ -22,17 +21,14 @@ func (m *Manager) Reset(stateManager *statemanager.Manager) error {
if m.udpTracker != nil {
m.udpTracker.Close()
- m.udpTracker = conntrack.NewUDPTracker(conntrack.DefaultUDPTimeout, m.logger)
}
if m.icmpTracker != nil {
m.icmpTracker.Close()
- m.icmpTracker = conntrack.NewICMPTracker(conntrack.DefaultICMPTimeout, m.logger)
}
if m.tcpTracker != nil {
m.tcpTracker.Close()
- m.tcpTracker = conntrack.NewTCPTracker(conntrack.DefaultTCPTimeout, m.logger)
}
if m.forwarder != nil {
@@ -48,7 +44,7 @@ func (m *Manager) Reset(stateManager *statemanager.Manager) error {
}
if m.nativeFirewall != nil {
- return m.nativeFirewall.Reset(stateManager)
+ return m.nativeFirewall.Close(stateManager)
}
return nil
}
diff --git a/client/firewall/uspfilter/allow_netbird_windows.go b/client/firewall/uspfilter/allow_netbird_windows.go
index 379585978..ee540cb1d 100644
--- a/client/firewall/uspfilter/allow_netbird_windows.go
+++ b/client/firewall/uspfilter/allow_netbird_windows.go
@@ -9,7 +9,6 @@ import (
log "github.com/sirupsen/logrus"
- "github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
"github.com/netbirdio/netbird/client/internal/statemanager"
)
@@ -21,8 +20,8 @@ const (
firewallRuleName = "Netbird"
)
-// Reset firewall to the default state
-func (m *Manager) Reset(*statemanager.Manager) error {
+// Close closes the firewall manager
+func (m *Manager) Close(*statemanager.Manager) error {
m.mutex.Lock()
defer m.mutex.Unlock()
@@ -31,17 +30,14 @@ func (m *Manager) Reset(*statemanager.Manager) error {
if m.udpTracker != nil {
m.udpTracker.Close()
- m.udpTracker = conntrack.NewUDPTracker(conntrack.DefaultUDPTimeout, m.logger)
}
if m.icmpTracker != nil {
m.icmpTracker.Close()
- m.icmpTracker = conntrack.NewICMPTracker(conntrack.DefaultICMPTimeout, m.logger)
}
if m.tcpTracker != nil {
m.tcpTracker.Close()
- m.tcpTracker = conntrack.NewTCPTracker(conntrack.DefaultTCPTimeout, m.logger)
}
if m.forwarder != nil {
diff --git a/client/firewall/uspfilter/uspfilter_bench_test.go b/client/firewall/uspfilter/uspfilter_bench_test.go
index 875bb2425..bb42a8052 100644
--- a/client/firewall/uspfilter/uspfilter_bench_test.go
+++ b/client/firewall/uspfilter/uspfilter_bench_test.go
@@ -160,7 +160,7 @@ func BenchmarkCoreFiltering(b *testing.B) {
SetFilterFunc: func(device.PacketFilter) error { return nil },
}, false)
defer b.Cleanup(func() {
- require.NoError(b, manager.Reset(nil))
+ require.NoError(b, manager.Close(nil))
})
manager.wgNetwork = &net.IPNet{
@@ -205,7 +205,7 @@ func BenchmarkStateScaling(b *testing.B) {
SetFilterFunc: func(device.PacketFilter) error { return nil },
}, false)
b.Cleanup(func() {
- require.NoError(b, manager.Reset(nil))
+ require.NoError(b, manager.Close(nil))
})
manager.wgNetwork = &net.IPNet{
@@ -253,7 +253,7 @@ func BenchmarkEstablishmentOverhead(b *testing.B) {
SetFilterFunc: func(device.PacketFilter) error { return nil },
}, false)
b.Cleanup(func() {
- require.NoError(b, manager.Reset(nil))
+ require.NoError(b, manager.Close(nil))
})
manager.wgNetwork = &net.IPNet{
@@ -452,7 +452,7 @@ func BenchmarkRoutedNetworkReturn(b *testing.B) {
SetFilterFunc: func(device.PacketFilter) error { return nil },
}, false)
b.Cleanup(func() {
- require.NoError(b, manager.Reset(nil))
+ require.NoError(b, manager.Close(nil))
})
// Setup scenario
@@ -579,7 +579,7 @@ func BenchmarkLongLivedConnections(b *testing.B) {
SetFilterFunc: func(device.PacketFilter) error { return nil },
}, false)
defer b.Cleanup(func() {
- require.NoError(b, manager.Reset(nil))
+ require.NoError(b, manager.Close(nil))
})
manager.SetNetwork(&net.IPNet{
@@ -670,7 +670,7 @@ func BenchmarkShortLivedConnections(b *testing.B) {
SetFilterFunc: func(device.PacketFilter) error { return nil },
}, false)
defer b.Cleanup(func() {
- require.NoError(b, manager.Reset(nil))
+ require.NoError(b, manager.Close(nil))
})
manager.SetNetwork(&net.IPNet{
@@ -789,7 +789,7 @@ func BenchmarkParallelLongLivedConnections(b *testing.B) {
SetFilterFunc: func(device.PacketFilter) error { return nil },
}, false)
defer b.Cleanup(func() {
- require.NoError(b, manager.Reset(nil))
+ require.NoError(b, manager.Close(nil))
})
manager.SetNetwork(&net.IPNet{
@@ -877,7 +877,7 @@ func BenchmarkParallelShortLivedConnections(b *testing.B) {
SetFilterFunc: func(device.PacketFilter) error { return nil },
}, false)
defer b.Cleanup(func() {
- require.NoError(b, manager.Reset(nil))
+ require.NoError(b, manager.Close(nil))
})
manager.SetNetwork(&net.IPNet{
diff --git a/client/firewall/uspfilter/uspfilter_filter_test.go b/client/firewall/uspfilter/uspfilter_filter_test.go
index 9a1456d00..9a5ec9c66 100644
--- a/client/firewall/uspfilter/uspfilter_filter_test.go
+++ b/client/firewall/uspfilter/uspfilter_filter_test.go
@@ -39,7 +39,7 @@ func TestPeerACLFiltering(t *testing.T) {
require.NotNil(t, manager)
t.Cleanup(func() {
- require.NoError(t, manager.Reset(nil))
+ require.NoError(t, manager.Close(nil))
})
manager.wgNetwork = wgNet
@@ -310,7 +310,7 @@ func setupRoutedManager(tb testing.TB, network string) *Manager {
require.False(tb, manager.nativeRouter)
tb.Cleanup(func() {
- require.NoError(tb, manager.Reset(nil))
+ require.NoError(tb, manager.Close(nil))
})
return manager
diff --git a/client/firewall/uspfilter/uspfilter_test.go b/client/firewall/uspfilter/uspfilter_test.go
index 089bf8f55..c03762984 100644
--- a/client/firewall/uspfilter/uspfilter_test.go
+++ b/client/firewall/uspfilter/uspfilter_test.go
@@ -254,7 +254,7 @@ func TestManagerReset(t *testing.T) {
return
}
- err = m.Reset(nil)
+ err = m.Close(nil)
if err != nil {
t.Errorf("failed to reset Manager: %v", err)
return
@@ -333,7 +333,7 @@ func TestNotMatchByIP(t *testing.T) {
return
}
- if err = m.Reset(nil); err != nil {
+ if err = m.Close(nil); err != nil {
t.Errorf("failed to reset Manager: %v", err)
return
}
@@ -352,7 +352,7 @@ func TestRemovePacketHook(t *testing.T) {
t.Fatalf("Failed to create Manager: %s", err)
}
defer func() {
- require.NoError(t, manager.Reset(nil))
+ require.NoError(t, manager.Close(nil))
}()
// Add a UDP packet hook
@@ -403,7 +403,7 @@ func TestProcessOutgoingHooks(t *testing.T) {
manager.udpTracker.Close()
manager.udpTracker = conntrack.NewUDPTracker(100*time.Millisecond, logger)
defer func() {
- require.NoError(t, manager.Reset(nil))
+ require.NoError(t, manager.Close(nil))
}()
manager.decoders = sync.Pool{
@@ -484,7 +484,7 @@ func TestUSPFilterCreatePerformance(t *testing.T) {
time.Sleep(time.Second)
defer func() {
- if err := manager.Reset(nil); err != nil {
+ if err := manager.Close(nil); err != nil {
t.Errorf("clear the manager state: %v", err)
}
time.Sleep(time.Second)
@@ -530,7 +530,7 @@ func TestStatefulFirewall_UDPTracking(t *testing.T) {
},
}
defer func() {
- require.NoError(t, manager.Reset(nil))
+ require.NoError(t, manager.Close(nil))
}()
// Set up packet parameters
diff --git a/client/iface/bind/ice_bind.go b/client/iface/bind/ice_bind.go
index 41f415af7..6897f04a1 100644
--- a/client/iface/bind/ice_bind.go
+++ b/client/iface/bind/ice_bind.go
@@ -5,7 +5,6 @@ import (
"net"
"net/netip"
"runtime"
- "strings"
"sync"
"github.com/pion/stun/v2"
@@ -108,35 +107,17 @@ func (s *ICEBind) GetICEMux() (*UniversalUDPMuxDefault, error) {
return s.udpMux, nil
}
-func (b *ICEBind) SetEndpoint(peerAddress *net.UDPAddr, conn net.Conn) (*net.UDPAddr, error) {
- fakeUDPAddr, err := fakeAddress(peerAddress)
- if err != nil {
- return nil, err
- }
-
- // force IPv4
- fakeAddr, ok := netip.AddrFromSlice(fakeUDPAddr.IP.To4())
- if !ok {
- return nil, fmt.Errorf("failed to convert IP to netip.Addr")
- }
-
+func (b *ICEBind) SetEndpoint(fakeIP netip.Addr, conn net.Conn) {
b.endpointsMu.Lock()
- b.endpoints[fakeAddr] = conn
+ b.endpoints[fakeIP] = conn
b.endpointsMu.Unlock()
-
- return fakeUDPAddr, nil
}
-func (b *ICEBind) RemoveEndpoint(fakeUDPAddr *net.UDPAddr) {
- fakeAddr, ok := netip.AddrFromSlice(fakeUDPAddr.IP.To4())
- if !ok {
- log.Warnf("failed to convert IP to netip.Addr")
- return
- }
-
+func (b *ICEBind) RemoveEndpoint(fakeIP netip.Addr) {
b.endpointsMu.Lock()
defer b.endpointsMu.Unlock()
- delete(b.endpoints, fakeAddr)
+
+ delete(b.endpoints, fakeIP)
}
func (b *ICEBind) Send(bufs [][]byte, ep wgConn.Endpoint) error {
@@ -275,21 +256,6 @@ func (c *ICEBind) receiveRelayed(buffs [][]byte, sizes []int, eps []wgConn.Endpo
}
}
-// fakeAddress returns a fake address that is used to as an identifier for the peer.
-// The fake address is in the format of 127.1.x.x where x.x is the last two octets of the peer address.
-func fakeAddress(peerAddress *net.UDPAddr) (*net.UDPAddr, error) {
- octets := strings.Split(peerAddress.IP.String(), ".")
- if len(octets) != 4 {
- return nil, fmt.Errorf("invalid IP format")
- }
-
- newAddr := &net.UDPAddr{
- IP: net.ParseIP(fmt.Sprintf("127.1.%s.%s", octets[2], octets[3])),
- Port: peerAddress.Port,
- }
- return newAddr, nil
-}
-
func getMessages(msgsPool *sync.Pool) *[]ipv6.Message {
return msgsPool.Get().(*[]ipv6.Message)
}
diff --git a/client/iface/netstack/tun.go b/client/iface/netstack/tun.go
index 01f19875e..a271a1954 100644
--- a/client/iface/netstack/tun.go
+++ b/client/iface/netstack/tun.go
@@ -55,7 +55,7 @@ func (t *NetStackTun) Create() (tun.Device, *netstack.Net, error) {
skipProxy, err := strconv.ParseBool(os.Getenv(EnvSkipProxy))
if err != nil {
- log.Errorf("failed to parse NB_ETSTACK_SKIP_PROXY: %s", err)
+ log.Errorf("failed to parse %s: %s", EnvSkipProxy, err)
}
if skipProxy {
return nsTunDev, tunNet, nil
diff --git a/client/iface/wgproxy/bind/proxy.go b/client/iface/wgproxy/bind/proxy.go
index 8a2e65382..614787e17 100644
--- a/client/iface/wgproxy/bind/proxy.go
+++ b/client/iface/wgproxy/bind/proxy.go
@@ -6,6 +6,7 @@ import (
"fmt"
"net"
"net/netip"
+ "strings"
"sync"
log "github.com/sirupsen/logrus"
@@ -16,13 +17,13 @@ import (
type ProxyBind struct {
Bind *bind.ICEBind
- wgAddr *net.UDPAddr
- wgEndpoint *bind.Endpoint
- remoteConn net.Conn
- ctx context.Context
- cancel context.CancelFunc
- closeMu sync.Mutex
- closed bool
+ fakeNetIP *netip.AddrPort
+ wgBindEndpoint *bind.Endpoint
+ remoteConn net.Conn
+ ctx context.Context
+ cancel context.CancelFunc
+ closeMu sync.Mutex
+ closed bool
pausedMu sync.Mutex
paused bool
@@ -33,20 +34,24 @@ type ProxyBind struct {
// endpoint is the NetBird address of the remote peer. The SetEndpoint return with the address what will be used in the
// WireGuard configuration.
func (p *ProxyBind) AddTurnConn(ctx context.Context, nbAddr *net.UDPAddr, remoteConn net.Conn) error {
- addr, err := p.Bind.SetEndpoint(nbAddr, remoteConn)
+ fakeNetIP, err := fakeAddress(nbAddr)
if err != nil {
return err
}
- p.wgAddr = addr
- p.wgEndpoint = addrToEndpoint(addr)
+ p.fakeNetIP = fakeNetIP
+ p.wgBindEndpoint = &bind.Endpoint{AddrPort: *fakeNetIP}
p.remoteConn = remoteConn
p.ctx, p.cancel = context.WithCancel(ctx)
- return err
+ return nil
}
func (p *ProxyBind) EndpointAddr() *net.UDPAddr {
- return p.wgAddr
+ return &net.UDPAddr{
+ IP: p.fakeNetIP.Addr().AsSlice(),
+ Port: int(p.fakeNetIP.Port()),
+ Zone: p.fakeNetIP.Addr().Zone(),
+ }
}
func (p *ProxyBind) Work() {
@@ -54,6 +59,8 @@ func (p *ProxyBind) Work() {
return
}
+ p.Bind.SetEndpoint(p.fakeNetIP.Addr(), p.remoteConn)
+
p.pausedMu.Lock()
p.paused = false
p.pausedMu.Unlock()
@@ -93,7 +100,7 @@ func (p *ProxyBind) close() error {
p.cancel()
- p.Bind.RemoveEndpoint(p.wgAddr)
+ p.Bind.RemoveEndpoint(p.fakeNetIP.Addr())
if rErr := p.remoteConn.Close(); rErr != nil && !errors.Is(rErr, net.ErrClosed) {
return rErr
@@ -126,7 +133,7 @@ func (p *ProxyBind) proxyToLocal(ctx context.Context) {
}
msg := bind.RecvMessage{
- Endpoint: p.wgEndpoint,
+ Endpoint: p.wgBindEndpoint,
Buffer: buf[:n],
}
p.Bind.RecvChan <- msg
@@ -134,8 +141,19 @@ func (p *ProxyBind) proxyToLocal(ctx context.Context) {
}
}
-func addrToEndpoint(addr *net.UDPAddr) *bind.Endpoint {
- ip, _ := netip.AddrFromSlice(addr.IP.To4())
- addrPort := netip.AddrPortFrom(ip, uint16(addr.Port))
- return &bind.Endpoint{AddrPort: addrPort}
+// fakeAddress returns a fake address that is used to as an identifier for the peer.
+// The fake address is in the format of 127.1.x.x where x.x is the last two octets of the peer address.
+func fakeAddress(peerAddress *net.UDPAddr) (*netip.AddrPort, error) {
+ octets := strings.Split(peerAddress.IP.String(), ".")
+ if len(octets) != 4 {
+ return nil, fmt.Errorf("invalid IP format")
+ }
+
+ fakeIP, err := netip.ParseAddr(fmt.Sprintf("127.1.%s.%s", octets[2], octets[3]))
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse new IP: %w", err)
+ }
+
+ netipAddr := netip.AddrPortFrom(fakeIP, uint16(peerAddress.Port))
+ return &netipAddr, nil
}
diff --git a/client/installer.nsis b/client/installer.nsis
index af942a868..743c81a6d 100644
--- a/client/installer.nsis
+++ b/client/installer.nsis
@@ -6,8 +6,8 @@
!define DESCRIPTION "A WireGuard®-based mesh network that connects your devices into a single private network"
!define INSTALLER_NAME "netbird-installer.exe"
!define MAIN_APP_EXE "Netbird"
-!define ICON "ui\\netbird.ico"
-!define BANNER "ui\\banner.bmp"
+!define ICON "ui\\assets\\netbird.ico"
+!define BANNER "ui\\build\\banner.bmp"
!define LICENSE_DATA "..\\LICENSE"
!define INSTALL_DIR "$PROGRAMFILES64\${APP_NAME}"
diff --git a/client/internal/acl/manager_test.go b/client/internal/acl/manager_test.go
index 217dbce9f..0327d62ef 100644
--- a/client/internal/acl/manager_test.go
+++ b/client/internal/acl/manager_test.go
@@ -58,7 +58,7 @@ func TestDefaultManager(t *testing.T) {
return
}
defer func(fw manager.Manager) {
- _ = fw.Reset(nil)
+ _ = fw.Close(nil)
}(fw)
acl := NewDefaultManager(fw)
@@ -352,7 +352,7 @@ func TestDefaultManagerEnableSSHRules(t *testing.T) {
return
}
defer func(fw manager.Manager) {
- _ = fw.Reset(nil)
+ _ = fw.Close(nil)
}(fw)
acl := NewDefaultManager(fw)
diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go
index 94b87124b..d9886fcd8 100644
--- a/client/internal/dns/server_test.go
+++ b/client/internal/dns/server_test.go
@@ -1015,7 +1015,7 @@ func TestHandlerChain_DomainPriorities(t *testing.T) {
mh.AssertExpectations(t)
}
- // Reset mocks
+ // Close mocks
if mh, ok := tc.expectedHandler.(*MockHandler); ok {
mh.ExpectedCalls = nil
mh.Calls = nil
diff --git a/client/internal/engine.go b/client/internal/engine.go
index a07fcb0ec..8f173fb55 100644
--- a/client/internal/engine.go
+++ b/client/internal/engine.go
@@ -1362,7 +1362,7 @@ func (e *Engine) close() {
}
if e.firewall != nil {
- err := e.firewall.Reset(e.stateManager)
+ err := e.firewall.Close(e.stateManager)
if err != nil {
log.Warnf("failed to reset firewall: %s", err)
}
diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go
index 6084c3629..eaee10930 100644
--- a/client/internal/peer/conn.go
+++ b/client/internal/peer/conn.go
@@ -448,7 +448,7 @@ func (conn *Conn) onRelayConnectionIsReady(rci RelayConnInfo) {
conn.Log.Infof("created new wgProxy for relay connection: %s", wgProxy.EndpointAddr().String())
- if conn.iceP2PIsActive() {
+ if conn.isICEActive() {
conn.Log.Debugf("do not switch to relay because current priority is: %s", conn.currentConnPriority.String())
conn.setRelayedProxy(wgProxy)
conn.statusRelay.Set(StatusConnected)
@@ -717,8 +717,8 @@ func (conn *Conn) isReadyToUpgrade() bool {
return conn.wgProxyRelay != nil && conn.currentConnPriority != connPriorityRelay
}
-func (conn *Conn) iceP2PIsActive() bool {
- return conn.currentConnPriority == connPriorityICEP2P && conn.statusICE.Get() == StatusConnected
+func (conn *Conn) isICEActive() bool {
+ return (conn.currentConnPriority == connPriorityICEP2P || conn.currentConnPriority == connPriorityICETurn) && conn.statusICE.Get() == StatusConnected
}
func (conn *Conn) removeWgPeer() error {
diff --git a/client/internal/routemanager/dnsinterceptor/handler.go b/client/internal/routemanager/dnsinterceptor/handler.go
index f36285cc4..da1056e2d 100644
--- a/client/internal/routemanager/dnsinterceptor/handler.go
+++ b/client/internal/routemanager/dnsinterceptor/handler.go
@@ -160,6 +160,12 @@ func (d *DnsInterceptor) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
return
}
+ // set the AuthenticatedData flag and the EDNS0 buffer size to 4096 bytes to support larger dns records
+ if r.Extra == nil {
+ r.SetEdns0(4096, false)
+ r.MsgHdr.AuthenticatedData = true
+ }
+
client := &dns.Client{
Timeout: 5 * time.Second,
Net: "udp",
diff --git a/client/netbird.wxs b/client/netbird.wxs
index ee9ab667f..5e03a014d 100644
--- a/client/netbird.wxs
+++ b/client/netbird.wxs
@@ -71,7 +71,7 @@
-
+
diff --git a/client/resources.rc b/client/resources.rc
index ac411245e..696fd0dfa 100644
--- a/client/resources.rc
+++ b/client/resources.rc
@@ -5,5 +5,5 @@
#define STRINGIZE(x) #x
#define EXPAND(x) STRINGIZE(x)
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml
-7 ICON ui/netbird.ico
+7 ICON ui/assets/netbird.ico
wintun.dll RCDATA wintun.dll
diff --git a/client/ui/netbird-systemtray-connected-dark.ico b/client/ui/assets/netbird-systemtray-connected-dark.ico
similarity index 100%
rename from client/ui/netbird-systemtray-connected-dark.ico
rename to client/ui/assets/netbird-systemtray-connected-dark.ico
diff --git a/client/ui/netbird-systemtray-connected-dark.png b/client/ui/assets/netbird-systemtray-connected-dark.png
similarity index 100%
rename from client/ui/netbird-systemtray-connected-dark.png
rename to client/ui/assets/netbird-systemtray-connected-dark.png
diff --git a/client/ui/netbird-systemtray-connected-macos.png b/client/ui/assets/netbird-systemtray-connected-macos.png
similarity index 100%
rename from client/ui/netbird-systemtray-connected-macos.png
rename to client/ui/assets/netbird-systemtray-connected-macos.png
diff --git a/client/ui/netbird-systemtray-connected.ico b/client/ui/assets/netbird-systemtray-connected.ico
similarity index 100%
rename from client/ui/netbird-systemtray-connected.ico
rename to client/ui/assets/netbird-systemtray-connected.ico
diff --git a/client/ui/netbird-systemtray-connected.png b/client/ui/assets/netbird-systemtray-connected.png
similarity index 100%
rename from client/ui/netbird-systemtray-connected.png
rename to client/ui/assets/netbird-systemtray-connected.png
diff --git a/client/ui/netbird-systemtray-connecting-dark.ico b/client/ui/assets/netbird-systemtray-connecting-dark.ico
similarity index 100%
rename from client/ui/netbird-systemtray-connecting-dark.ico
rename to client/ui/assets/netbird-systemtray-connecting-dark.ico
diff --git a/client/ui/netbird-systemtray-connecting-dark.png b/client/ui/assets/netbird-systemtray-connecting-dark.png
similarity index 100%
rename from client/ui/netbird-systemtray-connecting-dark.png
rename to client/ui/assets/netbird-systemtray-connecting-dark.png
diff --git a/client/ui/netbird-systemtray-connecting-macos.png b/client/ui/assets/netbird-systemtray-connecting-macos.png
similarity index 100%
rename from client/ui/netbird-systemtray-connecting-macos.png
rename to client/ui/assets/netbird-systemtray-connecting-macos.png
diff --git a/client/ui/netbird-systemtray-connecting.ico b/client/ui/assets/netbird-systemtray-connecting.ico
similarity index 100%
rename from client/ui/netbird-systemtray-connecting.ico
rename to client/ui/assets/netbird-systemtray-connecting.ico
diff --git a/client/ui/netbird-systemtray-connecting.png b/client/ui/assets/netbird-systemtray-connecting.png
similarity index 100%
rename from client/ui/netbird-systemtray-connecting.png
rename to client/ui/assets/netbird-systemtray-connecting.png
diff --git a/client/ui/netbird-systemtray-disconnected-macos.png b/client/ui/assets/netbird-systemtray-disconnected-macos.png
similarity index 100%
rename from client/ui/netbird-systemtray-disconnected-macos.png
rename to client/ui/assets/netbird-systemtray-disconnected-macos.png
diff --git a/client/ui/netbird-systemtray-disconnected.ico b/client/ui/assets/netbird-systemtray-disconnected.ico
similarity index 100%
rename from client/ui/netbird-systemtray-disconnected.ico
rename to client/ui/assets/netbird-systemtray-disconnected.ico
diff --git a/client/ui/netbird-systemtray-disconnected.png b/client/ui/assets/netbird-systemtray-disconnected.png
similarity index 100%
rename from client/ui/netbird-systemtray-disconnected.png
rename to client/ui/assets/netbird-systemtray-disconnected.png
diff --git a/client/ui/netbird-systemtray-error-dark.ico b/client/ui/assets/netbird-systemtray-error-dark.ico
similarity index 100%
rename from client/ui/netbird-systemtray-error-dark.ico
rename to client/ui/assets/netbird-systemtray-error-dark.ico
diff --git a/client/ui/netbird-systemtray-error-dark.png b/client/ui/assets/netbird-systemtray-error-dark.png
similarity index 100%
rename from client/ui/netbird-systemtray-error-dark.png
rename to client/ui/assets/netbird-systemtray-error-dark.png
diff --git a/client/ui/netbird-systemtray-error-macos.png b/client/ui/assets/netbird-systemtray-error-macos.png
similarity index 100%
rename from client/ui/netbird-systemtray-error-macos.png
rename to client/ui/assets/netbird-systemtray-error-macos.png
diff --git a/client/ui/netbird-systemtray-error.ico b/client/ui/assets/netbird-systemtray-error.ico
similarity index 100%
rename from client/ui/netbird-systemtray-error.ico
rename to client/ui/assets/netbird-systemtray-error.ico
diff --git a/client/ui/netbird-systemtray-error.png b/client/ui/assets/netbird-systemtray-error.png
similarity index 100%
rename from client/ui/netbird-systemtray-error.png
rename to client/ui/assets/netbird-systemtray-error.png
diff --git a/client/ui/netbird-systemtray-update-connected-dark.ico b/client/ui/assets/netbird-systemtray-update-connected-dark.ico
similarity index 100%
rename from client/ui/netbird-systemtray-update-connected-dark.ico
rename to client/ui/assets/netbird-systemtray-update-connected-dark.ico
diff --git a/client/ui/netbird-systemtray-update-connected-dark.png b/client/ui/assets/netbird-systemtray-update-connected-dark.png
similarity index 100%
rename from client/ui/netbird-systemtray-update-connected-dark.png
rename to client/ui/assets/netbird-systemtray-update-connected-dark.png
diff --git a/client/ui/netbird-systemtray-update-connected-macos.png b/client/ui/assets/netbird-systemtray-update-connected-macos.png
similarity index 100%
rename from client/ui/netbird-systemtray-update-connected-macos.png
rename to client/ui/assets/netbird-systemtray-update-connected-macos.png
diff --git a/client/ui/netbird-systemtray-update-connected.ico b/client/ui/assets/netbird-systemtray-update-connected.ico
similarity index 100%
rename from client/ui/netbird-systemtray-update-connected.ico
rename to client/ui/assets/netbird-systemtray-update-connected.ico
diff --git a/client/ui/netbird-systemtray-update-connected.png b/client/ui/assets/netbird-systemtray-update-connected.png
similarity index 100%
rename from client/ui/netbird-systemtray-update-connected.png
rename to client/ui/assets/netbird-systemtray-update-connected.png
diff --git a/client/ui/netbird-systemtray-update-disconnected-dark.ico b/client/ui/assets/netbird-systemtray-update-disconnected-dark.ico
similarity index 100%
rename from client/ui/netbird-systemtray-update-disconnected-dark.ico
rename to client/ui/assets/netbird-systemtray-update-disconnected-dark.ico
diff --git a/client/ui/netbird-systemtray-update-disconnected-dark.png b/client/ui/assets/netbird-systemtray-update-disconnected-dark.png
similarity index 100%
rename from client/ui/netbird-systemtray-update-disconnected-dark.png
rename to client/ui/assets/netbird-systemtray-update-disconnected-dark.png
diff --git a/client/ui/netbird-systemtray-update-disconnected-macos.png b/client/ui/assets/netbird-systemtray-update-disconnected-macos.png
similarity index 100%
rename from client/ui/netbird-systemtray-update-disconnected-macos.png
rename to client/ui/assets/netbird-systemtray-update-disconnected-macos.png
diff --git a/client/ui/netbird-systemtray-update-disconnected.ico b/client/ui/assets/netbird-systemtray-update-disconnected.ico
similarity index 100%
rename from client/ui/netbird-systemtray-update-disconnected.ico
rename to client/ui/assets/netbird-systemtray-update-disconnected.ico
diff --git a/client/ui/netbird-systemtray-update-disconnected.png b/client/ui/assets/netbird-systemtray-update-disconnected.png
similarity index 100%
rename from client/ui/netbird-systemtray-update-disconnected.png
rename to client/ui/assets/netbird-systemtray-update-disconnected.png
diff --git a/client/ui/netbird.ico b/client/ui/assets/netbird.ico
similarity index 100%
rename from client/ui/netbird.ico
rename to client/ui/assets/netbird.ico
diff --git a/client/ui/netbird.png b/client/ui/assets/netbird.png
similarity index 100%
rename from client/ui/netbird.png
rename to client/ui/assets/netbird.png
diff --git a/client/ui/banner.bmp b/client/ui/build/banner.bmp
similarity index 100%
rename from client/ui/banner.bmp
rename to client/ui/build/banner.bmp
diff --git a/client/ui/build-ui-linux.sh b/client/ui/build/build-ui-linux.sh
similarity index 100%
rename from client/ui/build-ui-linux.sh
rename to client/ui/build/build-ui-linux.sh
diff --git a/client/ui/netbird.desktop b/client/ui/build/netbird.desktop
similarity index 100%
rename from client/ui/netbird.desktop
rename to client/ui/build/netbird.desktop
diff --git a/client/ui/client_ui.go b/client/ui/client_ui.go
index 51eec59a5..914d4f3b9 100644
--- a/client/ui/client_ui.go
+++ b/client/ui/client_ui.go
@@ -35,7 +35,9 @@ import (
"github.com/netbirdio/netbird/client/proto"
"github.com/netbirdio/netbird/client/ui/desktop"
"github.com/netbirdio/netbird/client/ui/event"
+ "github.com/netbirdio/netbird/client/ui/process"
"github.com/netbirdio/netbird/util"
+
"github.com/netbirdio/netbird/version"
)
@@ -44,94 +46,125 @@ const (
failFastTimeout = time.Second
)
+const (
+ censoredPreSharedKey = "**********"
+)
+
func main() {
- var daemonAddr string
-
- defaultDaemonAddr := "unix:///var/run/netbird.sock"
- if runtime.GOOS == "windows" {
- defaultDaemonAddr = "tcp://127.0.0.1:41731"
- }
-
- flag.StringVar(
- &daemonAddr, "daemon-addr",
- defaultDaemonAddr,
- "Daemon service address to serve CLI requests [unix|tcp]://[path|host:port]")
-
- var showSettings bool
- flag.BoolVar(&showSettings, "settings", false, "run settings windows")
- var showRoutes bool
- flag.BoolVar(&showRoutes, "networks", false, "run networks windows")
- var errorMSG string
- flag.StringVar(&errorMSG, "error-msg", "", "displays a error message window")
-
- tmpDir := "/tmp"
- if runtime.GOOS == "windows" {
- tmpDir = os.TempDir()
- }
-
- var saveLogsInFile bool
- flag.BoolVar(&saveLogsInFile, "use-log-file", false, fmt.Sprintf("save logs in a file: %s/netbird-ui-PID.log", tmpDir))
-
- flag.Parse()
+ daemonAddr, showSettings, showNetworks, errorMsg, saveLogsInFile := parseFlags()
+ // Initialize file logging if needed.
if saveLogsInFile {
- logFile := path.Join(tmpDir, fmt.Sprintf("netbird-ui-%d.log", os.Getpid()))
- err := util.InitLog("trace", logFile)
- if err != nil {
+ if err := initLogFile(); err != nil {
log.Errorf("error while initializing log: %v", err)
return
}
}
+ // Create the Fyne application.
a := app.NewWithID("NetBird")
a.SetIcon(fyne.NewStaticResource("netbird", iconDisconnected))
- if errorMSG != "" {
- showErrorMSG(errorMSG)
+ // Show error message window if needed.
+ if errorMsg != "" {
+ showErrorMessage(errorMsg)
return
}
- client := newServiceClient(daemonAddr, a, showSettings, showRoutes)
+ // Create the service client (this also builds the settings or networks UI if requested).
+ client := newServiceClient(daemonAddr, a, showSettings, showNetworks)
+
+ // Watch for theme/settings changes to update the icon.
+ go watchSettingsChanges(a, client)
+
+ // Run in window mode if any UI flag was set.
+ if showSettings || showNetworks {
+ a.Run()
+ return
+ }
+
+ // Check for another running process.
+ running, err := process.IsAnotherProcessRunning()
+ if err != nil {
+ log.Errorf("error while checking process: %v", err)
+ return
+ }
+ if running {
+ log.Warn("another process is running")
+ return
+ }
+
+ client.setDefaultFonts()
+ systray.Run(client.onTrayReady, client.onTrayExit)
+}
+
+// parseFlags reads and returns all needed command-line flags.
+func parseFlags() (daemonAddr string, showSettings, showNetworks bool, errorMsg string, saveLogsInFile bool) {
+ defaultDaemonAddr := "unix:///var/run/netbird.sock"
+ if runtime.GOOS == "windows" {
+ defaultDaemonAddr = "tcp://127.0.0.1:41731"
+ }
+ flag.StringVar(&daemonAddr, "daemon-addr", defaultDaemonAddr, "Daemon service address to serve CLI requests [unix|tcp]://[path|host:port]")
+ flag.BoolVar(&showSettings, "settings", false, "run settings window")
+ flag.BoolVar(&showNetworks, "networks", false, "run networks window")
+ flag.StringVar(&errorMsg, "error-msg", "", "displays an error message window")
+
+ tmpDir := "/tmp"
+ if runtime.GOOS == "windows" {
+ tmpDir = os.TempDir()
+ }
+ flag.BoolVar(&saveLogsInFile, "use-log-file", false, fmt.Sprintf("save logs in a file: %s/netbird-ui-PID.log", tmpDir))
+ flag.Parse()
+ return
+}
+
+// initLogFile initializes logging into a file.
+func initLogFile() error {
+ tmpDir := "/tmp"
+ if runtime.GOOS == "windows" {
+ tmpDir = os.TempDir()
+ }
+ logFile := path.Join(tmpDir, fmt.Sprintf("netbird-ui-%d.log", os.Getpid()))
+ return util.InitLog("trace", logFile)
+}
+
+// watchSettingsChanges listens for Fyne theme/settings changes and updates the client icon.
+func watchSettingsChanges(a fyne.App, client *serviceClient) {
settingsChangeChan := make(chan fyne.Settings)
a.Settings().AddChangeListener(settingsChangeChan)
- go func() {
- for range settingsChangeChan {
- client.updateIcon()
- }
- }()
-
- if showSettings || showRoutes {
- a.Run()
- } else {
- running, err := isAnotherProcessRunning()
- if err != nil {
- log.Errorf("error while checking process: %v", err)
- }
- if running {
- log.Warn("another process is running")
- return
- }
- client.setDefaultFonts()
- systray.Run(client.onTrayReady, client.onTrayExit)
+ for range settingsChangeChan {
+ client.updateIcon()
}
}
-//go:embed netbird-systemtray-connected-macos.png
+// showErrorMessage displays an error message in a simple window.
+func showErrorMessage(msg string) {
+ a := app.New()
+ w := a.NewWindow("NetBird Error")
+ label := widget.NewLabel(msg)
+ label.Wrapping = fyne.TextWrapWord
+ w.SetContent(label)
+ w.Resize(fyne.NewSize(400, 100))
+ w.Show()
+ a.Run()
+}
+
+//go:embed assets/netbird-systemtray-connected-macos.png
var iconConnectedMacOS []byte
-//go:embed netbird-systemtray-disconnected-macos.png
+//go:embed assets/netbird-systemtray-disconnected-macos.png
var iconDisconnectedMacOS []byte
-//go:embed netbird-systemtray-update-disconnected-macos.png
+//go:embed assets/netbird-systemtray-update-disconnected-macos.png
var iconUpdateDisconnectedMacOS []byte
-//go:embed netbird-systemtray-update-connected-macos.png
+//go:embed assets/netbird-systemtray-update-connected-macos.png
var iconUpdateConnectedMacOS []byte
-//go:embed netbird-systemtray-connecting-macos.png
+//go:embed assets/netbird-systemtray-connecting-macos.png
var iconConnectingMacOS []byte
-//go:embed netbird-systemtray-error-macos.png
+//go:embed assets/netbird-systemtray-error-macos.png
var iconErrorMacOS []byte
type serviceClient struct {
@@ -154,6 +187,7 @@ type serviceClient struct {
mAdminPanel *systray.MenuItem
mSettings *systray.MenuItem
mAbout *systray.MenuItem
+ mGitHub *systray.MenuItem
mVersionUI *systray.MenuItem
mVersionDaemon *systray.MenuItem
mUpdate *systray.MenuItem
@@ -300,18 +334,6 @@ func (s *serviceClient) showSettingsUI() {
s.wSettings.Show()
}
-// showErrorMSG opens a fyne app window to display the supplied message
-func showErrorMSG(msg string) {
- app := app.New()
- w := app.NewWindow("NetBird Error")
- content := widget.NewLabel(msg)
- content.Wrapping = fyne.TextWrapWord
- w.SetContent(content)
- w.Resize(fyne.NewSize(400, 100))
- w.Show()
- app.Run()
-}
-
// getSettingsForm to embed it into settings window.
func (s *serviceClient) getSettingsForm() *widget.Form {
return &widget.Form{
@@ -327,7 +349,7 @@ func (s *serviceClient) getSettingsForm() *widget.Form {
},
SubmitText: "Save",
OnSubmit: func() {
- if s.iPreSharedKey.Text != "" && s.iPreSharedKey.Text != "**********" {
+ if s.iPreSharedKey.Text != "" && s.iPreSharedKey.Text != censoredPreSharedKey {
// validate preSharedKey if it added
if _, err := wgtypes.ParseKey(s.iPreSharedKey.Text); err != nil {
dialog.ShowError(fmt.Errorf("Invalid Pre-shared Key Value"), s.wSettings)
@@ -365,7 +387,7 @@ func (s *serviceClient) getSettingsForm() *widget.Form {
WireguardPort: &port,
}
- if s.iPreSharedKey.Text != "**********" {
+ if s.iPreSharedKey.Text != censoredPreSharedKey {
loginRequest.OptionalPreSharedKey = &s.iPreSharedKey.Text
}
@@ -587,26 +609,29 @@ func (s *serviceClient) onTrayReady() {
s.mAdminPanel = systray.AddMenuItem("Admin Panel", "Netbird Admin Panel")
systray.AddSeparator()
- s.mSettings = systray.AddMenuItem("Settings", "Settings of the application")
- s.mAllowSSH = s.mSettings.AddSubMenuItemCheckbox("Allow SSH", "Allow SSH connections", false)
- s.mAutoConnect = s.mSettings.AddSubMenuItemCheckbox("Connect on Startup", "Connect automatically when the service starts", false)
- s.mEnableRosenpass = s.mSettings.AddSubMenuItemCheckbox("Enable Quantum-Resistance", "Enable post-quantum security via Rosenpass", false)
- s.mNotifications = s.mSettings.AddSubMenuItemCheckbox("Notifications", "Enable notifications", false)
- s.mAdvancedSettings = s.mSettings.AddSubMenuItem("Advanced Settings", "Advanced settings of the application")
- s.mCreateDebugBundle = s.mSettings.AddSubMenuItem("Create Debug Bundle", "Create and open debug information bundle")
+ s.mSettings = systray.AddMenuItem("Settings", settingsMenuDescr)
+ s.mAllowSSH = s.mSettings.AddSubMenuItemCheckbox("Allow SSH", allowSSHMenuDescr, false)
+ s.mAutoConnect = s.mSettings.AddSubMenuItemCheckbox("Connect on Startup", autoConnectMenuDescr, false)
+ s.mEnableRosenpass = s.mSettings.AddSubMenuItemCheckbox("Enable Quantum-Resistance", quantumResistanceMenuDescr, false)
+ s.mNotifications = s.mSettings.AddSubMenuItemCheckbox("Notifications", notificationsMenuDescr, false)
+ s.mAdvancedSettings = s.mSettings.AddSubMenuItem("Advanced Settings", advancedSettingsMenuDescr)
+ s.mCreateDebugBundle = s.mSettings.AddSubMenuItem("Create Debug Bundle", debugBundleMenuDescr)
s.loadSettings()
s.exitNodeMu.Lock()
- s.mExitNode = systray.AddMenuItem("Exit Node", "Select exit node for routing traffic")
+ s.mExitNode = systray.AddMenuItem("Exit Node", exitNodeMenuDescr)
s.mExitNode.Disable()
s.exitNodeMu.Unlock()
- s.mNetworks = systray.AddMenuItem("Networks", "Open the networks management window")
+ s.mNetworks = systray.AddMenuItem("Networks", networksMenuDescr)
s.mNetworks.Disable()
systray.AddSeparator()
s.mAbout = systray.AddMenuItem("About", "About")
s.mAbout.SetIcon(s.icAbout)
+
+ s.mGitHub = s.mAbout.AddSubMenuItem("GitHub", "GitHub")
+
versionString := normalizedVersion(version.NetbirdVersion())
s.mVersionUI = s.mAbout.AddSubMenuItem(fmt.Sprintf("GUI: %s", versionString), fmt.Sprintf("GUI Version: %s", versionString))
s.mVersionUI.Disable()
@@ -615,11 +640,11 @@ func (s *serviceClient) onTrayReady() {
s.mVersionDaemon.Disable()
s.mVersionDaemon.Hide()
- s.mUpdate = s.mAbout.AddSubMenuItem("Download latest version", "Download latest version")
+ s.mUpdate = s.mAbout.AddSubMenuItem("Download latest version", latestVersionMenuDescr)
s.mUpdate.Hide()
systray.AddSeparator()
- s.mQuit = systray.AddMenuItem("Quit", "Quit the client app")
+ s.mQuit = systray.AddMenuItem("Quit", quitMenuDescr)
// update exit node menu in case service is already connected
go s.updateExitNodes()
@@ -717,6 +742,11 @@ func (s *serviceClient) onTrayReady() {
case <-s.mQuit.ClickedCh:
systray.Quit()
return
+ case <-s.mGitHub.ClickedCh:
+ err := openURL("https://github.com/netbirdio/netbird")
+ if err != nil {
+ log.Errorf("%s", err)
+ }
case <-s.mUpdate.ClickedCh:
err := openURL(version.DownloadUrl())
if err != nil {
diff --git a/client/ui/const.go b/client/ui/const.go
new file mode 100644
index 000000000..0253750d1
--- /dev/null
+++ b/client/ui/const.go
@@ -0,0 +1,15 @@
+package main
+
+const (
+ settingsMenuDescr = "Settings of the application"
+ allowSSHMenuDescr = "Allow SSH connections"
+ autoConnectMenuDescr = "Connect automatically when the service starts"
+ quantumResistanceMenuDescr = "Enable post-quantum security via Rosenpass"
+ notificationsMenuDescr = "Enable notifications"
+ advancedSettingsMenuDescr = "Advanced settings of the application"
+ debugBundleMenuDescr = "Create and open debug information bundle"
+ exitNodeMenuDescr = "Select exit node for routing traffic"
+ networksMenuDescr = "Open the networks management window"
+ latestVersionMenuDescr = "Download latest version"
+ quitMenuDescr = "Quit the client app"
+)
diff --git a/client/ui/icons.go b/client/ui/icons.go
index 6f3a9dbc9..e88fb9378 100644
--- a/client/ui/icons.go
+++ b/client/ui/icons.go
@@ -6,38 +6,38 @@ import (
_ "embed"
)
-//go:embed netbird.png
+//go:embed assets/netbird.png
var iconAbout []byte
-//go:embed netbird-systemtray-connected.png
+//go:embed assets/netbird-systemtray-connected.png
var iconConnected []byte
-//go:embed netbird-systemtray-connected-dark.png
+//go:embed assets/netbird-systemtray-connected-dark.png
var iconConnectedDark []byte
-//go:embed netbird-systemtray-disconnected.png
+//go:embed assets/netbird-systemtray-disconnected.png
var iconDisconnected []byte
-//go:embed netbird-systemtray-update-disconnected.png
+//go:embed assets/netbird-systemtray-update-disconnected.png
var iconUpdateDisconnected []byte
-//go:embed netbird-systemtray-update-disconnected-dark.png
+//go:embed assets/netbird-systemtray-update-disconnected-dark.png
var iconUpdateDisconnectedDark []byte
-//go:embed netbird-systemtray-update-connected.png
+//go:embed assets/netbird-systemtray-update-connected.png
var iconUpdateConnected []byte
-//go:embed netbird-systemtray-update-connected-dark.png
+//go:embed assets/netbird-systemtray-update-connected-dark.png
var iconUpdateConnectedDark []byte
-//go:embed netbird-systemtray-connecting.png
+//go:embed assets/netbird-systemtray-connecting.png
var iconConnecting []byte
-//go:embed netbird-systemtray-connecting-dark.png
+//go:embed assets/netbird-systemtray-connecting-dark.png
var iconConnectingDark []byte
-//go:embed netbird-systemtray-error.png
+//go:embed assets/netbird-systemtray-error.png
var iconError []byte
-//go:embed netbird-systemtray-error-dark.png
+//go:embed assets/netbird-systemtray-error-dark.png
var iconErrorDark []byte
diff --git a/client/ui/icons_windows.go b/client/ui/icons_windows.go
index a2a924763..2107d3852 100644
--- a/client/ui/icons_windows.go
+++ b/client/ui/icons_windows.go
@@ -1,41 +1,41 @@
package main
import (
- _ "embed"
+ _ "embed"
)
-//go:embed netbird.ico
+//go:embed assets/netbird.ico
var iconAbout []byte
-//go:embed netbird-systemtray-connected.ico
+//go:embed assets/netbird-systemtray-connected.ico
var iconConnected []byte
-//go:embed netbird-systemtray-connected-dark.ico
+//go:embed assets/netbird-systemtray-connected-dark.ico
var iconConnectedDark []byte
-//go:embed netbird-systemtray-disconnected.ico
+//go:embed assets/netbird-systemtray-disconnected.ico
var iconDisconnected []byte
-//go:embed netbird-systemtray-update-disconnected.ico
+//go:embed assets/netbird-systemtray-update-disconnected.ico
var iconUpdateDisconnected []byte
-//go:embed netbird-systemtray-update-disconnected-dark.ico
+//go:embed assets/netbird-systemtray-update-disconnected-dark.ico
var iconUpdateDisconnectedDark []byte
-//go:embed netbird-systemtray-update-connected.ico
+//go:embed assets/netbird-systemtray-update-connected.ico
var iconUpdateConnected []byte
-//go:embed netbird-systemtray-update-connected-dark.ico
+//go:embed assets/netbird-systemtray-update-connected-dark.ico
var iconUpdateConnectedDark []byte
-//go:embed netbird-systemtray-connecting.ico
+//go:embed assets/netbird-systemtray-connecting.ico
var iconConnecting []byte
-//go:embed netbird-systemtray-connecting-dark.ico
+//go:embed assets/netbird-systemtray-connecting-dark.ico
var iconConnectingDark []byte
-//go:embed netbird-systemtray-error.ico
+//go:embed assets/netbird-systemtray-error.ico
var iconError []byte
-//go:embed netbird-systemtray-error-dark.ico
+//go:embed assets/netbird-systemtray-error-dark.ico
var iconErrorDark []byte
diff --git a/client/ui/network.go b/client/ui/network.go
index 750788cf3..b21554f09 100644
--- a/client/ui/network.go
+++ b/client/ui/network.go
@@ -363,7 +363,7 @@ func (s *serviceClient) recreateExitNodeMenu(exitNodes []*proto.Network) {
if runtime.GOOS == "linux" || runtime.GOOS == "freebsd" {
s.mExitNode.Remove()
- s.mExitNode = systray.AddMenuItem("Exit Node", "Select exit node for routing traffic")
+ s.mExitNode = systray.AddMenuItem("Exit Node", exitNodeMenuDescr)
}
for _, node := range exitNodes {
diff --git a/client/ui/process.go b/client/ui/process/process.go
similarity index 90%
rename from client/ui/process.go
rename to client/ui/process/process.go
index bcb3dd879..f9a8a4fe9 100644
--- a/client/ui/process.go
+++ b/client/ui/process/process.go
@@ -1,4 +1,4 @@
-package main
+package process
import (
"os"
@@ -8,7 +8,7 @@ import (
"github.com/shirou/gopsutil/v3/process"
)
-func isAnotherProcessRunning() (bool, error) {
+func IsAnotherProcessRunning() (bool, error) {
processes, err := process.Processes()
if err != nil {
return false, err
diff --git a/client/ui/process_nonwindows.go b/client/ui/process/process_nonwindows.go
similarity index 96%
rename from client/ui/process_nonwindows.go
rename to client/ui/process/process_nonwindows.go
index 0d17be2be..cf9f6443d 100644
--- a/client/ui/process_nonwindows.go
+++ b/client/ui/process/process_nonwindows.go
@@ -1,6 +1,6 @@
//go:build !windows
-package main
+package process
import (
"os"
diff --git a/client/ui/process_windows.go b/client/ui/process/process_windows.go
similarity index 96%
rename from client/ui/process_windows.go
rename to client/ui/process/process_windows.go
index b15b0ed24..2d211d1a4 100644
--- a/client/ui/process_windows.go
+++ b/client/ui/process/process_windows.go
@@ -1,4 +1,4 @@
-package main
+package process
import (
"os/user"
diff --git a/formatter/formatter.go b/formatter/formatter.go
deleted file mode 100644
index 74de38603..000000000
--- a/formatter/formatter.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package formatter
-
-import (
- "fmt"
- "strings"
- "time"
-
- "github.com/sirupsen/logrus"
-)
-
-// TextFormatter formats logs into text with included source code's path
-type TextFormatter struct {
- timestampFormat string
- levelDesc []string
-}
-
-// SyslogFormatter formats logs into text
-type SyslogFormatter struct {
- levelDesc []string
-}
-
-var validLevelDesc = []string{"PANC", "FATL", "ERRO", "WARN", "INFO", "DEBG", "TRAC"}
-
-
-// NewTextFormatter create new MyTextFormatter instance
-func NewTextFormatter() *TextFormatter {
- return &TextFormatter{
- levelDesc: validLevelDesc,
- timestampFormat: time.RFC3339, // or RFC3339
- }
-}
-
-// NewSyslogFormatter create new MySyslogFormatter instance
-func NewSyslogFormatter() *SyslogFormatter {
- return &SyslogFormatter{
- levelDesc: validLevelDesc,
- }
-}
-
-// Format renders a single log entry
-func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) {
- var fields string
- keys := make([]string, 0, len(entry.Data))
- for k, v := range entry.Data {
- if k == "source" {
- continue
- }
- keys = append(keys, fmt.Sprintf("%s: %v", k, v))
- }
-
- if len(keys) > 0 {
- fields = fmt.Sprintf("[%s] ", strings.Join(keys, ", "))
- }
-
- level := f.parseLevel(entry.Level)
-
- return []byte(fmt.Sprintf("%s %s %s%s: %s\n", entry.Time.Format(f.timestampFormat), level, fields, entry.Data["source"], entry.Message)), nil
-}
-
-func (f *TextFormatter) parseLevel(level logrus.Level) string {
- if len(f.levelDesc) < int(level) {
- return ""
- }
-
- return f.levelDesc[level]
-}
-
-// Format renders a single log entry
-func (f *SyslogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
- var fields string
- keys := make([]string, 0, len(entry.Data))
- for k, v := range entry.Data {
- if k == "source" {
- continue
- }
- keys = append(keys, fmt.Sprintf("%s: %v", k, v))
- }
-
- if len(keys) > 0 {
- fields = fmt.Sprintf("[%s] ", strings.Join(keys, ", "))
- }
- return []byte(fmt.Sprintf("%s%s\n", fields, entry.Message)), nil
-}
diff --git a/formatter/hook/additional_empty.go b/formatter/hook/additional_empty.go
new file mode 100644
index 000000000..4f5069482
--- /dev/null
+++ b/formatter/hook/additional_empty.go
@@ -0,0 +1,9 @@
+//go:build !loggoroutine
+
+package hook
+
+import log "github.com/sirupsen/logrus"
+
+func additionalEntries(_ *log.Entry) {
+ // This function is empty and is used to demonstrate the use of additional hooks.
+}
diff --git a/formatter/hook/additional_goroutine.go b/formatter/hook/additional_goroutine.go
new file mode 100644
index 000000000..fb4e09f47
--- /dev/null
+++ b/formatter/hook/additional_goroutine.go
@@ -0,0 +1,12 @@
+//go:build loggoroutine
+
+package hook
+
+import (
+ "github.com/petermattis/goid"
+ log "github.com/sirupsen/logrus"
+)
+
+func additionalEntries(entry *log.Entry) {
+ entry.Data[EntryKeyGoroutineID] = goid.Get()
+}
diff --git a/formatter/hook.go b/formatter/hook/hook.go
similarity index 96%
rename from formatter/hook.go
rename to formatter/hook/hook.go
index 12f27e67d..1b6ceccc9 100644
--- a/formatter/hook.go
+++ b/formatter/hook/hook.go
@@ -1,4 +1,4 @@
-package formatter
+package hook
import (
"fmt"
@@ -41,7 +41,8 @@ func (hook ContextHook) Levels() []logrus.Level {
// Fire extend with the source information the entry.Data
func (hook ContextHook) Fire(entry *logrus.Entry) error {
src := hook.parseSrc(entry.Caller.File)
- entry.Data["source"] = fmt.Sprintf("%s:%v", src, entry.Caller.Line)
+ entry.Data[EntryKeySource] = fmt.Sprintf("%s:%v", src, entry.Caller.Line)
+ additionalEntries(entry)
if entry.Context == nil {
return nil
diff --git a/formatter/hook_test.go b/formatter/hook/hook_test.go
similarity index 98%
rename from formatter/hook_test.go
rename to formatter/hook/hook_test.go
index a4bcb0284..802163244 100644
--- a/formatter/hook_test.go
+++ b/formatter/hook/hook_test.go
@@ -1,4 +1,4 @@
-package formatter
+package hook
import (
"testing"
diff --git a/formatter/hook/keys.go b/formatter/hook/keys.go
new file mode 100644
index 000000000..09781a88b
--- /dev/null
+++ b/formatter/hook/keys.go
@@ -0,0 +1,6 @@
+package hook
+
+const (
+ EntryKeySource = "source"
+ EntryKeyGoroutineID = "goroutine_id"
+)
diff --git a/formatter/levels/levels.go b/formatter/levels/levels.go
new file mode 100644
index 000000000..41ae80db3
--- /dev/null
+++ b/formatter/levels/levels.go
@@ -0,0 +1,3 @@
+package levels
+
+var ValidLevelDesc = []string{"PANC", "FATL", "ERRO", "WARN", "INFO", "DEBG", "TRAC"}
diff --git a/formatter/logcat.go b/formatter/logcat/logcat.go
similarity index 63%
rename from formatter/logcat.go
rename to formatter/logcat/logcat.go
index e8f606229..c561d3283 100644
--- a/formatter/logcat.go
+++ b/formatter/logcat/logcat.go
@@ -1,26 +1,28 @@
-package formatter
+package logcat
import (
"fmt"
"strings"
"github.com/sirupsen/logrus"
+
+ "github.com/netbirdio/netbird/formatter/levels"
)
-// LogcatFormatter formats logs into text what is fit for logcat
-type LogcatFormatter struct {
+// Formatter formats logs into text what is fit for logcat
+type Formatter struct {
levelDesc []string
}
// NewLogcatFormatter create new LogcatFormatter instance
-func NewLogcatFormatter() *LogcatFormatter {
- return &LogcatFormatter{
- levelDesc: []string{"PANC", "FATL", "ERRO", "WARN", "INFO", "DEBG", "TRAC"},
+func NewLogcatFormatter() *Formatter {
+ return &Formatter{
+ levelDesc: levels.ValidLevelDesc,
}
}
// Format renders a single log entry
-func (f *LogcatFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) {
var fields string
keys := make([]string, 0, len(entry.Data))
for k, v := range entry.Data {
@@ -39,7 +41,7 @@ func (f *LogcatFormatter) Format(entry *logrus.Entry) ([]byte, error) {
return []byte(fmt.Sprintf("[%s] %s%s %s\n", level, fields, entry.Data["source"], entry.Message)), nil
}
-func (f *LogcatFormatter) parseLevel(level logrus.Level) string {
+func (f *Formatter) parseLevel(level logrus.Level) string {
if len(f.levelDesc) < int(level) {
return ""
}
diff --git a/formatter/logcat_test.go b/formatter/logcat/logcat_test.go
similarity index 97%
rename from formatter/logcat_test.go
rename to formatter/logcat/logcat_test.go
index 45ba5bc46..fd4d92881 100644
--- a/formatter/logcat_test.go
+++ b/formatter/logcat/logcat_test.go
@@ -1,4 +1,4 @@
-package formatter
+package logcat
import (
"testing"
@@ -25,4 +25,5 @@ func TestLogcatMessageFormat(t *testing.T) {
if parsedString != expectedString && parsedString != expectedStringVariant {
t.Errorf("The log messages don't match. Expected: '%s', got: '%s'", expectedString, parsedString)
}
+
}
diff --git a/formatter/set.go b/formatter/set.go
index 9dfea5a7f..a609e7b48 100644
--- a/formatter/set.go
+++ b/formatter/set.go
@@ -2,31 +2,37 @@ package formatter
import (
"github.com/sirupsen/logrus"
+
+ "github.com/netbirdio/netbird/formatter/hook"
+ "github.com/netbirdio/netbird/formatter/logcat"
+ "github.com/netbirdio/netbird/formatter/syslog"
+ "github.com/netbirdio/netbird/formatter/txt"
)
// SetTextFormatter set the text formatter for given logger.
func SetTextFormatter(logger *logrus.Logger) {
- logger.Formatter = NewTextFormatter()
+ logger.Formatter = txt.NewTextFormatter()
logger.ReportCaller = true
- logger.AddHook(NewContextHook())
+ logger.AddHook(hook.NewContextHook())
}
+
// SetSyslogFormatter set the text formatter for given logger.
func SetSyslogFormatter(logger *logrus.Logger) {
- logger.Formatter = NewSyslogFormatter()
+ logger.Formatter = syslog.NewSyslogFormatter()
logger.ReportCaller = true
- logger.AddHook(NewContextHook())
+ logger.AddHook(hook.NewContextHook())
}
// SetJSONFormatter set the JSON formatter for given logger.
func SetJSONFormatter(logger *logrus.Logger) {
logger.Formatter = &logrus.JSONFormatter{}
logger.ReportCaller = true
- logger.AddHook(NewContextHook())
+ logger.AddHook(hook.NewContextHook())
}
// SetLogcatFormatter set the logcat formatter for given logger.
func SetLogcatFormatter(logger *logrus.Logger) {
- logger.Formatter = NewLogcatFormatter()
+ logger.Formatter = logcat.NewLogcatFormatter()
logger.ReportCaller = true
- logger.AddHook(NewContextHook())
+ logger.AddHook(hook.NewContextHook())
}
diff --git a/formatter/syslog/formatter.go b/formatter/syslog/formatter.go
new file mode 100644
index 000000000..e72c30347
--- /dev/null
+++ b/formatter/syslog/formatter.go
@@ -0,0 +1,39 @@
+package syslog
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/sirupsen/logrus"
+
+ "github.com/netbirdio/netbird/formatter/levels"
+)
+
+// Formatter formats logs into text
+type Formatter struct {
+ levelDesc []string
+}
+
+// NewSyslogFormatter create new MySyslogFormatter instance
+func NewSyslogFormatter() *Formatter {
+ return &Formatter{
+ levelDesc: levels.ValidLevelDesc,
+ }
+}
+
+// Format renders a single log entry
+func (f *Formatter) Format(entry *logrus.Entry) ([]byte, error) {
+ var fields string
+ keys := make([]string, 0, len(entry.Data))
+ for k, v := range entry.Data {
+ if k == "source" {
+ continue
+ }
+ keys = append(keys, fmt.Sprintf("%s: %v", k, v))
+ }
+
+ if len(keys) > 0 {
+ fields = fmt.Sprintf("[%s] ", strings.Join(keys, ", "))
+ }
+ return []byte(fmt.Sprintf("%s%s\n", fields, entry.Message)), nil
+}
diff --git a/formatter/syslog/formatter_test.go b/formatter/syslog/formatter_test.go
new file mode 100644
index 000000000..110a3390b
--- /dev/null
+++ b/formatter/syslog/formatter_test.go
@@ -0,0 +1,26 @@
+package syslog
+
+import (
+ "testing"
+ "time"
+
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLogSyslogFormat(t *testing.T) {
+
+ someEntry := &logrus.Entry{
+ Data: logrus.Fields{"att1": 1, "att2": 2, "source": "some/fancy/path.go:46"},
+ Time: time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC),
+ Level: 3,
+ Message: "Some Message",
+ }
+
+ formatter := NewSyslogFormatter()
+ result, _ := formatter.Format(someEntry)
+
+ parsedString := string(result)
+ expectedString := "^\\[(att1: 1, att2: 2|att2: 2, att1: 1)\\] Some Message\\s+$"
+ assert.Regexp(t, expectedString, parsedString)
+}
diff --git a/formatter/txt/format.go b/formatter/txt/format.go
new file mode 100644
index 000000000..a88c41044
--- /dev/null
+++ b/formatter/txt/format.go
@@ -0,0 +1,31 @@
+//go:build !loggoroutine
+
+package txt
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/sirupsen/logrus"
+
+ "github.com/netbirdio/netbird/formatter/hook"
+)
+
+func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+ var fields string
+ keys := make([]string, 0, len(entry.Data))
+ for k, v := range entry.Data {
+ if k == hook.EntryKeySource {
+ continue
+ }
+ keys = append(keys, fmt.Sprintf("%s: %v", k, v))
+ }
+
+ if len(keys) > 0 {
+ fields = fmt.Sprintf("[%s] ", strings.Join(keys, ", "))
+ }
+
+ level := f.parseLevel(entry.Level)
+
+ return []byte(fmt.Sprintf("%s %s %s%s: %s\n", entry.Time.Format(f.timestampFormat), level, fields, entry.Data[hook.EntryKeySource], entry.Message)), nil
+}
diff --git a/formatter/txt/format_gorutines.go b/formatter/txt/format_gorutines.go
new file mode 100644
index 000000000..a39aee633
--- /dev/null
+++ b/formatter/txt/format_gorutines.go
@@ -0,0 +1,35 @@
+//go:build loggoroutine
+
+package txt
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/sirupsen/logrus"
+
+ "github.com/netbirdio/netbird/formatter/hook"
+)
+
+func (f *TextFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+ var fields string
+ keys := make([]string, 0, len(entry.Data))
+ for k, v := range entry.Data {
+ if k == hook.EntryKeySource {
+ continue
+ }
+
+ if k == hook.EntryKeyGoroutineID {
+ continue
+ }
+ keys = append(keys, fmt.Sprintf("%s: %v", k, v))
+ }
+
+ if len(keys) > 0 {
+ fields = fmt.Sprintf("[%s] ", strings.Join(keys, ", "))
+ }
+
+ level := f.parseLevel(entry.Level)
+
+ return []byte(fmt.Sprintf("%s %s %d %s%s: %s\n", entry.Time.Format(f.timestampFormat), level, entry.Data[hook.EntryKeyGoroutineID], fields, entry.Data[hook.EntryKeySource], entry.Message)), nil
+}
diff --git a/formatter/txt/formatter.go b/formatter/txt/formatter.go
new file mode 100644
index 000000000..3b2a3fb4d
--- /dev/null
+++ b/formatter/txt/formatter.go
@@ -0,0 +1,31 @@
+package txt
+
+import (
+ "time"
+
+ "github.com/sirupsen/logrus"
+
+ "github.com/netbirdio/netbird/formatter/levels"
+)
+
+// TextFormatter formats logs into text with included source code's path
+type TextFormatter struct {
+ timestampFormat string
+ levelDesc []string
+}
+
+// NewTextFormatter create new MyTextFormatter instance
+func NewTextFormatter() *TextFormatter {
+ return &TextFormatter{
+ levelDesc: levels.ValidLevelDesc,
+ timestampFormat: time.RFC3339, // or RFC3339
+ }
+}
+
+func (f *TextFormatter) parseLevel(level logrus.Level) string {
+ if len(f.levelDesc) < int(level) {
+ return ""
+ }
+
+ return f.levelDesc[level]
+}
diff --git a/formatter/formatter_test.go b/formatter/txt/formatter_test.go
similarity index 55%
rename from formatter/formatter_test.go
rename to formatter/txt/formatter_test.go
index 1ed207958..590af5d50 100644
--- a/formatter/formatter_test.go
+++ b/formatter/txt/formatter_test.go
@@ -1,4 +1,4 @@
-package formatter
+package txt
import (
"testing"
@@ -24,20 +24,3 @@ func TestLogTextFormat(t *testing.T) {
expectedString := "^2021-02-21T01:10:30Z WARN \\[(att1: 1, att2: 2|att2: 2, att1: 1)\\] some/fancy/path.go:46: Some Message\\s+$"
assert.Regexp(t, expectedString, parsedString)
}
-
-func TestLogSyslogFormat(t *testing.T) {
-
- someEntry := &logrus.Entry{
- Data: logrus.Fields{"att1": 1, "att2": 2, "source": "some/fancy/path.go:46"},
- Time: time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC),
- Level: 3,
- Message: "Some Message",
- }
-
- formatter := NewSyslogFormatter()
- result, _ := formatter.Format(someEntry)
-
- parsedString := string(result)
- expectedString := "^\\[(att1: 1, att2: 2|att2: 2, att1: 1)\\] Some Message\\s+$"
- assert.Regexp(t, expectedString, parsedString)
-}
diff --git a/go.mod b/go.mod
index 3d71e8eb1..76605e155 100644
--- a/go.mod
+++ b/go.mod
@@ -65,6 +65,7 @@ require (
github.com/okta/okta-sdk-golang/v2 v2.18.0
github.com/oschwald/maxminddb-golang v1.12.0
github.com/patrickmn/go-cache v2.1.0+incompatible
+ github.com/petermattis/goid v0.0.0-20250303134427-723919f7f203
github.com/pion/logging v0.2.2
github.com/pion/randutil v0.1.0
github.com/pion/stun/v2 v2.0.0
diff --git a/go.sum b/go.sum
index 36bca22d3..069b378d6 100644
--- a/go.sum
+++ b/go.sum
@@ -571,6 +571,8 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
+github.com/petermattis/goid v0.0.0-20250303134427-723919f7f203 h1:E7Kmf11E4K7B5hDti2K2NqPb1nlYlGYsu02S1JNd/Bs=
+github.com/petermattis/goid v0.0.0-20250303134427-723919f7f203/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/dtls/v2 v2.2.10 h1:u2Axk+FyIR1VFTPurktB+1zoEPGIW3bmyj3LEFrXjAA=
github.com/pion/dtls/v2 v2.2.10/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
diff --git a/management/cmd/management.go b/management/cmd/management.go
index 9712f04aa..e087a3d67 100644
--- a/management/cmd/management.go
+++ b/management/cmd/management.go
@@ -36,7 +36,7 @@ import (
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/encryption"
- "github.com/netbirdio/netbird/formatter"
+ "github.com/netbirdio/netbird/formatter/hook"
mgmtProto "github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/auth"
@@ -90,7 +90,7 @@ var (
flag.Parse()
//nolint
- ctx := context.WithValue(cmd.Context(), formatter.ExecutionContextKey, formatter.SystemSource)
+ ctx := context.WithValue(cmd.Context(), hook.ExecutionContextKey, hook.SystemSource)
err := util.InitLog(logLevel, logFile)
if err != nil {
@@ -136,7 +136,7 @@ var (
ctx, cancel := context.WithCancel(cmd.Context())
defer cancel()
//nolint
- ctx = context.WithValue(ctx, formatter.ExecutionContextKey, formatter.SystemSource)
+ ctx = context.WithValue(ctx, hook.ExecutionContextKey, hook.SystemSource)
err := handleRebrand(cmd)
if err != nil {
@@ -374,7 +374,7 @@ func unaryInterceptor(
) (interface{}, error) {
reqID := uuid.New().String()
//nolint
- ctx = context.WithValue(ctx, formatter.ExecutionContextKey, formatter.GRPCSource)
+ ctx = context.WithValue(ctx, hook.ExecutionContextKey, hook.GRPCSource)
//nolint
ctx = context.WithValue(ctx, nbContext.RequestIDKey, reqID)
return handler(ctx, req)
@@ -389,7 +389,7 @@ func streamInterceptor(
reqID := uuid.New().String()
wrapped := grpcMiddleware.WrapServerStream(ss)
//nolint
- ctx := context.WithValue(ss.Context(), formatter.ExecutionContextKey, formatter.GRPCSource)
+ ctx := context.WithValue(ss.Context(), hook.ExecutionContextKey, hook.GRPCSource)
//nolint
wrapped.WrappedContext = context.WithValue(ctx, nbContext.RequestIDKey, reqID)
return handler(srv, wrapped)
diff --git a/management/cmd/migration_up.go b/management/cmd/migration_up.go
index 183fc554d..de061dca2 100644
--- a/management/cmd/migration_up.go
+++ b/management/cmd/migration_up.go
@@ -8,7 +8,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
- "github.com/netbirdio/netbird/formatter"
+ "github.com/netbirdio/netbird/formatter/hook"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/util"
)
@@ -30,7 +30,7 @@ var upCmd = &cobra.Command{
}
//nolint
- ctx := context.WithValue(cmd.Context(), formatter.ExecutionContextKey, formatter.SystemSource)
+ ctx := context.WithValue(cmd.Context(), hook.ExecutionContextKey, hook.SystemSource)
if err := store.MigrateFileStoreToSqlite(ctx, mgmtDataDir); err != nil {
return err
diff --git a/management/server/management_proto_test.go b/management/server/management_proto_test.go
index 4d0630f0f..28f5abf57 100644
--- a/management/server/management_proto_test.go
+++ b/management/server/management_proto_test.go
@@ -20,7 +20,7 @@ import (
"google.golang.org/grpc/keepalive"
"github.com/netbirdio/netbird/encryption"
- "github.com/netbirdio/netbird/formatter"
+ "github.com/netbirdio/netbird/formatter/hook"
mgmtProto "github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/settings"
@@ -424,7 +424,7 @@ func startManagementForTest(t *testing.T, testFile string, config *Config) (*grp
peersUpdateManager := NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}
- ctx := context.WithValue(context.Background(), formatter.ExecutionContextKey, formatter.SystemSource) //nolint:staticcheck
+ ctx := context.WithValue(context.Background(), hook.ExecutionContextKey, hook.SystemSource) //nolint:staticcheck
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
diff --git a/management/server/peer.go b/management/server/peer.go
index c9b0fcfee..ff898f56c 100644
--- a/management/server/peer.go
+++ b/management/server/peer.go
@@ -400,6 +400,9 @@ func (am *DefaultAccountManager) DeletePeer(ctx context.Context, accountID, peer
eventsToStore, err = deletePeers(ctx, am, transaction, accountID, userID, []*nbpeer.Peer{peer})
return err
})
+ if err != nil {
+ return err
+ }
for _, storeEvent := range eventsToStore {
storeEvent()
diff --git a/management/server/telemetry/http_api_metrics.go b/management/server/telemetry/http_api_metrics.go
index 5ef9e6d02..ae27466d9 100644
--- a/management/server/telemetry/http_api_metrics.go
+++ b/management/server/telemetry/http_api_metrics.go
@@ -13,7 +13,7 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
- "github.com/netbirdio/netbird/formatter"
+ "github.com/netbirdio/netbird/formatter/hook"
nbContext "github.com/netbirdio/netbird/management/server/context"
)
@@ -167,7 +167,7 @@ func (m *HTTPMiddleware) Handler(h http.Handler) http.Handler {
reqStart := time.Now()
//nolint
- ctx := context.WithValue(r.Context(), formatter.ExecutionContextKey, formatter.HTTPSource)
+ ctx := context.WithValue(r.Context(), hook.ExecutionContextKey, hook.HTTPSource)
reqID := uuid.New().String()
//nolint