Merge remote-tracking branch 'origin/main' into feat-post_quantum_ml_kem

# Conflicts:
#	.github/workflows/wasm-build-validation.yml
This commit is contained in:
riccardom
2026-10-22 13:53:54 +02:00
281 changed files with 7752 additions and 5279 deletions
@@ -34,7 +34,7 @@ jobs:
while IFS= read -r dir; do
echo "=== Checking $dir ==="
# Search for problematic imports, excluding test files
RESULTS=$(grep -r "github.com/netbirdio/netbird/\(management\|signal\|relay\|proxy\)" "$dir" --include="*.go" 2>/dev/null | grep -v "_test.go" | grep -v "test_" | grep -v "/test/" | grep -v "tools/idp-migrate/" | grep -v "tools/mysql-migrate/" || true)
RESULTS=$(grep -r "github.com/netbirdio/netbird/\(management\|signal\|relay\|proxy\)" "$dir" --include="*.go" 2>/dev/null | grep -v "_test.go" | grep -v "test_" | grep -v "/test/" | grep -v "tools/idp-migrate/" || true)
if [ -n "$RESULTS" ]; then
echo "❌ Found problematic dependencies:"
echo "$RESULTS"
@@ -93,7 +93,7 @@ jobs:
IMPORTERS=$(go list -json -deps ./... 2>/dev/null | jq -r "select(.Imports[]? == \"$package\") | .ImportPath")
# Check if any importer is NOT in management/signal/relay
BSD_IMPORTER=$(echo "$IMPORTERS" | grep -v "github.com/netbirdio/netbird/\(management\|signal\|relay\|proxy\|combined\|tools/idp-migrate\|tools/mysql-migrate\)" | head -1)
BSD_IMPORTER=$(echo "$IMPORTERS" | grep -v "github.com/netbirdio/netbird/\(management\|signal\|relay\|proxy\|combined\|tools/idp-migrate\)" | head -1)
if [ -n "$BSD_IMPORTER" ]; then
echo "❌ $package ($license) is imported by BSD-licensed code: $BSD_IMPORTER"
+82
View File
@@ -3,9 +3,16 @@ name: Post release topic on Discourse
on:
release:
types: [published]
workflow_run:
workflows: [Release]
types: [completed]
permissions:
contents: read
jobs:
post:
if: github.event_name == 'release'
runs-on: ubuntu-latest
steps:
- uses: roots/discourse-topic-github-release-action@557d74ea05b6cc0c47f555c1d5d28a89d904005b # v1.1.0
@@ -15,3 +22,78 @@ jobs:
discourse-author-username: NetBird
discourse-category: 17
discourse-tags: releases
# goreleaser publishes the release of a pushed tag with GITHUB_TOKEN, and events
# raised by that token start no workflow, so the release event above never fires.
post-tag-release:
if: >-
github.event_name == 'workflow_run' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.conclusion == 'success' &&
startsWith(github.event.workflow_run.head_branch, 'v') &&
!contains(github.event.workflow_run.head_branch, '-')
runs-on: ubuntu-latest
steps:
- name: Post the release topic
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ github.event.workflow_run.head_branch }}
DISCOURSE_API_KEY: ${{ secrets.DISCOURSE_RELEASES_API_KEY }}
DISCOURSE_URL: https://forum.netbird.io
DISCOURSE_USER: NetBird
DISCOURSE_CATEGORY: "17"
run: |
set -euo pipefail
if ! release=$(gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${TAG}" 2>gh.err); then
if grep -q 'HTTP 404' gh.err; then
echo "no release for ${TAG}, nothing to announce"
exit 0
fi
cat gh.err >&2
exit 1
fi
# A release a person published already fired the release event above.
author=$(jq -r '.author.login' <<<"$release")
if [ "$author" != "github-actions[bot]" ] || [ "$(jq -r '.draft' <<<"$release")" = "true" ]; then
echo "${TAG} published by ${author}, announced by the release event"
exit 0
fi
# Same title and body as the action above, so both paths read alike.
title="${GITHUB_REPOSITORY#*/} ${TAG} released"
# A re-run of a successful Release run fires this event again. A failed lookup
# stops the step rather than risk a duplicate topic.
search=$(curl -fsS --retry 3 --proto '=https' --proto-redir '=https' -G \
-H "Api-Key: ${DISCOURSE_API_KEY}" \
-H "Api-Username: ${DISCOURSE_USER}" \
"${DISCOURSE_URL}/search.json" \
--data-urlencode "q=\"${title}\" in:title")
existing=$(jq -r --arg t "$title" --argjson c "$DISCOURSE_CATEGORY" \
'[(.topics // [])[] | select(.category_id == $c and (.title | ascii_downcase) == ($t | ascii_downcase))] | length' \
<<<"$search")
if [ "$existing" != "0" ]; then
echo "topic '${title}' already exists"
exit 0
fi
# goreleaser leaves the body empty; the notes are written on the release page afterwards.
url=$(jq -r '.html_url' <<<"$release")
body=$(jq -r '.body // ""' <<<"$release")
if [ -z "${body//[[:space:]]/}" ]; then
body="Release notes and downloads: ${url}"
fi
jq -n \
--arg title "$title" \
--arg raw "$(printf '[%s](%s)\n\n%s' "$title" "$url" "$body")" \
--argjson category "$DISCOURSE_CATEGORY" \
'{title: $title, raw: $raw, category: $category, tags: ["releases"]}' \
| curl -fsS -X POST "${DISCOURSE_URL}/posts.json" \
-H "Api-Key: ${DISCOURSE_API_KEY}" \
-H "Api-Username: ${DISCOURSE_USER}" \
-H "Content-Type: application/json" \
--data @- \
| jq -r '"posted \(env.DISCOURSE_URL)/t/\(.topic_slug)/\(.topic_id)"'
+2 -6
View File
@@ -307,7 +307,7 @@ jobs:
sh -c ' \
apk update; apk add --no-cache \
ca-certificates iptables ip6tables dbus dbus-dev libpcap-dev build-base; \
go test -buildvcs=false -tags "devcert privileged" -v -timeout 10m -p 1 $(go list -e -buildvcs=false ./... | grep -v -e /management -e /signal -e /relay -e /proxy -e /combined -e /client/ui -e /upload-server -e /client/testutil/privileged -e tools/mysql-migrate)
go test -buildvcs=false -tags "devcert privileged" -v -timeout 10m -p 1 $(go list -e -buildvcs=false ./... | grep -v -e /management -e /signal -e /relay -e /proxy -e /combined -e /client/ui -e /upload-server -e /client/testutil/privileged)
'
test_relay:
@@ -498,7 +498,7 @@ jobs:
fail-fast: false
matrix:
arch: ["amd64"]
store: ["sqlite", "postgres", "mysql"]
store: ["sqlite", "postgres"]
runs-on: ubuntu-22.04
steps:
- name: Checkout code
@@ -547,10 +547,6 @@ jobs:
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
run: echo "$DOCKER_TOKEN" | sudo docker login --username "$DOCKER_USER" --password-stdin
- name: download mysql image
if: matrix.store == 'mysql'
run: docker pull mlsmaycon/warmed-mysql:8
# The -json stream goes through tools/gotestsummary so the log shows one
# line per test, the output of failed tests, the head of a timeout panic
# with the still-running tests, and the slowest tests per package.
+1 -1
View File
@@ -76,7 +76,7 @@ jobs:
# resolve; the Where-Object pipeline then drops the broken package by
# path. Without -e, go list aborts with empty stdout.
run: |
$packages = go list -e ./... | Where-Object { $_ -notmatch '/management' } | Where-Object { $_ -notmatch '/relay' } | Where-Object { $_ -notmatch '/signal' } | Where-Object { $_ -notmatch '/proxy' } | Where-Object { $_ -notmatch '/combined' } | Where-Object { $_ -notmatch '/client/ui$' } | Where-Object { $_ -notmatch '/tools/mysql-migrate' }
$packages = go list -e ./... | Where-Object { $_ -notmatch '/management' } | Where-Object { $_ -notmatch '/relay' } | Where-Object { $_ -notmatch '/signal' } | Where-Object { $_ -notmatch '/proxy' } | Where-Object { $_ -notmatch '/combined' } | Where-Object { $_ -notmatch '/client/ui$' }
$goExe = "C:\hostedtoolcache\windows\go\${{ steps.go.outputs.go-version }}\x64\bin\go.exe"
$cmd = "$goExe test -tags `"devcert privileged`" -timeout 10m -p 1 $($packages -join ' ') > test-out.txt 2>&1"
Set-Content -Path "${{ github.workspace }}\run-tests.cmd" -Value $cmd
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
store: ["sqlite", "postgres", "mysql"]
store: ["sqlite", "postgres"]
services:
postgres:
image: ${{ (matrix.store == 'postgres') && 'postgres' || '' }}
@@ -35,19 +35,6 @@ jobs:
--health-timeout 5s
ports:
- 5432:5432
mysql:
image: ${{ (matrix.store == 'mysql') && 'mysql' || '' }}
env:
MYSQL_USER: netbird
MYSQL_PASSWORD: mysql
MYSQL_ROOT_PASSWORD: mysqlroot
MYSQL_DATABASE: netbird
options: >-
--health-cmd "mysqladmin ping --silent"
--health-interval 10s
--health-timeout 5s
ports:
- 3306:3306
steps:
- name: Set Database Connection String
run: |
@@ -56,11 +43,6 @@ jobs:
else
echo "NETBIRD_STORE_ENGINE_POSTGRES_DSN==" >> $GITHUB_ENV
fi
if [ "${{ matrix.store }}" == "mysql" ]; then
echo "NETBIRD_STORE_ENGINE_MYSQL_DSN=netbird:mysql@tcp($(hostname -I | awk '{print $1}'):3306)/netbird" >> $GITHUB_ENV
else
echo "NETBIRD_STORE_ENGINE_MYSQL_DSN==" >> $GITHUB_ENV
fi
- name: Install jq
run: sudo apt-get install -y jq
@@ -86,15 +68,6 @@ jobs:
restore-keys: |
${{ runner.os }}-go-
- name: Setup MySQL privileges
if: matrix.store == 'mysql'
run: |
sleep 10
mysql -h 127.0.0.1 -u root -pmysqlroot -e "
GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO 'netbird'@'%';
FLUSH PRIVILEGES;
"
- name: cp setup.env
run: cp infrastructure_files/tests/setup.env infrastructure_files/
@@ -114,7 +87,6 @@ jobs:
CI_NETBIRD_AUTH_SUPPORTED_SCOPES: "openid profile email offline_access api email_verified"
CI_NETBIRD_STORE_CONFIG_ENGINE: ${{ matrix.store }}
NETBIRD_STORE_ENGINE_POSTGRES_DSN: ${{ env.NETBIRD_STORE_ENGINE_POSTGRES_DSN }}
NETBIRD_STORE_ENGINE_MYSQL_DSN: ${{ env.NETBIRD_STORE_ENGINE_MYSQL_DSN }}
CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false
- name: check values
@@ -143,7 +115,6 @@ jobs:
CI_NETBIRD_SIGNAL_PORT: 12345
CI_NETBIRD_STORE_CONFIG_ENGINE: ${{ matrix.store }}
NETBIRD_STORE_ENGINE_POSTGRES_DSN: "${{ env.NETBIRD_STORE_ENGINE_POSTGRES_DSN }}$"
NETBIRD_STORE_ENGINE_MYSQL_DSN: "${{ env.NETBIRD_STORE_ENGINE_MYSQL_DSN }}$"
CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false
CI_NETBIRD_TURN_EXTERNAL_IP: "1.2.3.4"
CI_NETBIRD_MGMT_DISABLE_DEFAULT_POLICY: false
@@ -182,7 +153,6 @@ jobs:
grep -A 10 PKCEAuthorizationFlow management.json | grep -A 10 ProviderConfig | grep Scope | grep "$CI_NETBIRD_AUTH_SUPPORTED_SCOPES"
grep -A 10 PKCEAuthorizationFlow management.json | grep -A 10 ProviderConfig | grep -A 3 RedirectURLs | grep "http://localhost:53000"
grep "external-ip" turnserver.conf | grep $CI_NETBIRD_TURN_EXTERNAL_IP
grep "NETBIRD_STORE_ENGINE_MYSQL_DSN=$NETBIRD_STORE_ENGINE_MYSQL_DSN" docker-compose.yml
grep NETBIRD_STORE_ENGINE_POSTGRES_DSN docker-compose.yml | egrep "$NETBIRD_STORE_ENGINE_POSTGRES_DSN"
# check relay values
grep "NB_EXPOSED_ADDRESS=rels://$CI_NETBIRD_DOMAIN:33445" docker-compose.yml
+2 -2
View File
@@ -66,7 +66,7 @@ jobs:
echo "Size: ${SIZE} bytes (${SIZE_MB} MB)"
if [ ${SIZE} -gt 67108864 ]; then
echo "Wasm binary size (${SIZE_MB}MB) exceeds 64MB limit!"
if [ ${SIZE} -gt 73400320 ]; then
echo "Wasm binary size (${SIZE_MB}MB) exceeds 70MB limit!"
exit 1
fi
-26
View File
@@ -280,28 +280,6 @@ builds:
- -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
mod_timestamp: "{{ .CommitTimestamp }}"
- id: netbird-mysql-migrate
dir: tools/mysql-migrate
env:
- CGO_ENABLED=1
- >-
{{- if eq .Runtime.Goos "linux" }}
{{- if eq .Arch "arm64"}}CC=aarch64-linux-gnu-gcc{{- end }}
{{- if eq .Arch "arm"}}CC=arm-linux-gnueabihf-gcc{{- end }}
{{- end }}
binary: netbird-mysql-migrate
goos:
- linux
goarch:
- amd64
- arm64
- arm
goarm:
- 7
ldflags:
- -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
mod_timestamp: "{{ .CommitTimestamp }}"
universal_binaries:
- id: netbird
@@ -318,10 +296,6 @@ archives:
builds:
- netbird-idp-migrate
name_template: "netbird-idp-migrate_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
- id: netbird-mysql-migrate
builds:
- netbird-mysql-migrate
name_template: "netbird-mysql-migrate_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
- id: nblink
builds:
- nblink
+8 -14
View File
@@ -58,23 +58,17 @@ func (p *Preferences) SetManagementURL(url string) {
p.configInput.ManagementURL = url
}
// GetAdminURL reads URL from config file
// GetAdminURL returns the default admin panel URL.
//
// Deprecated: the admin URL is no longer stored or used by the client.
func (p *Preferences) GetAdminURL() (string, error) {
if p.configInput.AdminURL != "" {
return p.configInput.AdminURL, nil
}
cfg, err := profilemanager.ReadConfigOrDefault(p.configInput.ConfigPath)
if err != nil {
return "", err
}
return cfg.AdminURL.String(), err
return profilemanager.DefaultAdminURL, nil
}
// SetAdminURL stores the given URL and waits for commit
func (p *Preferences) SetAdminURL(url string) {
p.configInput.AdminURL = url
}
// SetAdminURL is a no-op kept for API compatibility.
//
// Deprecated: the admin URL is no longer stored or used by the client.
func (p *Preferences) SetAdminURL(_ string) {}
// HasPreSharedKey reports whether a pre-shared key is staged, persisted, or
// enforced by MDM; the key itself is never handed to the native layer.
+3 -2
View File
@@ -43,13 +43,14 @@ func TestPreferences_ReadUncommitedValues(t *testing.T) {
cfgFile := filepath.Join(t.TempDir(), "netbird.json")
p := NewPreferences(cfgFile)
// The admin URL is deprecated: setting it is a no-op.
p.SetAdminURL(exampleString)
resp, err := p.GetAdminURL()
if err != nil {
t.Fatalf("failed to read admin url: %s", err)
}
if resp != exampleString {
if resp != profilemanager.DefaultAdminURL {
t.Errorf("unexpected admin url: %s", resp)
}
@@ -95,7 +96,7 @@ func TestPreferences_Commit(t *testing.T) {
t.Fatalf("failed to read admin url: %s", err)
}
if resp != exampleURL {
if resp != profilemanager.DefaultAdminURL {
t.Errorf("unexpected admin url: %s", resp)
}
+2 -1
View File
@@ -157,7 +157,8 @@ func init() {
rootCmd.PersistentFlags().StringVar(&daemonAddr, "daemon-addr", defaultDaemonAddr, "Daemon service address to serve CLI requests [unix|tcp|npipe]://[path|host:port|name]")
rootCmd.PersistentFlags().StringVarP(&managementURL, "management-url", "m", "", fmt.Sprintf("Management Service URL [http|https]://[host]:[port] (default \"%s\")", profilemanager.DefaultManagementURL))
rootCmd.PersistentFlags().StringVar(&adminURL, "admin-url", "", fmt.Sprintf("Admin Panel URL [http|https]://[host]:[port] (default \"%s\")", profilemanager.DefaultAdminURL))
rootCmd.PersistentFlags().StringVar(&adminURL, "admin-url", "", "(DEPRECATED) Admin Panel URL - This flag is no longer functional")
_ = rootCmd.PersistentFlags().MarkDeprecated("admin-url", "the admin-url flag is no longer functional and will be removed in a future version")
rootCmd.PersistentFlags().StringVarP(&logLevel, "log-level", "l", "info", "sets NetBird log level")
rootCmd.PersistentFlags().StringSliceVar(&logFiles, "log-file", []string{defaultLogFile}, "sets NetBird log paths written to simultaneously. If `console` is specified the log will be output to stdout. If `syslog` is specified the log will be sent to syslog daemon. You can pass the flag multiple times or separate entries by `,` character")
rootCmd.PersistentFlags().StringVarP(&setupKey, "setup-key", "k", "", "Setup key obtained from the Management Service Dashboard (used to register peer)")
-1
View File
@@ -493,7 +493,6 @@ func setupSetConfigReq(customDNSAddressConverted []byte, cmd *cobra.Command, pro
req.Username = username
req.ManagementUrl = managementURL
req.AdminURL = adminURL
req.NatExternalIPs = natExternalIPs
req.CustomDNSAddress = customDNSAddressConverted
req.ExtraIFaceBlacklist = extraIFaceBlackList
+4 -5
View File
@@ -12,11 +12,10 @@ import (
"sync"
"github.com/sirupsen/logrus"
wgdevice "golang.zx2c4.com/wireguard/device"
wgnetstack "golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal"
"github.com/netbirdio/netbird/client/internal/auth"
"github.com/netbirdio/netbird/client/internal/peer"
@@ -256,10 +255,10 @@ func New(opts Options) (*Client, error) {
}
if opts.Performance.PreallocatedBuffersPerPool != nil {
wgdevice.SetPreallocatedBuffersPerPool(*opts.Performance.PreallocatedBuffersPerPool)
wgbackend.SetPreallocatedBuffersPerPool(*opts.Performance.PreallocatedBuffersPerPool)
}
if opts.Performance.MaxBatchSize != nil {
wgdevice.SetMaxBatchSizeOverride(*opts.Performance.MaxBatchSize)
wgbackend.SetMaxBatchSizeOverride(*opts.Performance.MaxBatchSize)
}
return &Client{
@@ -651,7 +650,7 @@ func (c *Client) getEngine() (*internal.Engine, error) {
return engine, nil
}
func (c *Client) getNet() (*wgnetstack.Net, netip.Addr, error) {
func (c *Client) getNet() (*wgbackend.NetstackNet, netip.Addr, error) {
engine, err := c.getEngine()
if err != nil {
return nil, netip.Addr{}, err
+2 -3
View File
@@ -1,10 +1,9 @@
package firewall
import (
wgdevice "golang.zx2c4.com/wireguard/device"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// EnvForceUserspaceFirewall forces the use of the userspace packet filter even when
@@ -20,5 +19,5 @@ type IFaceMapper interface {
IsUserspaceBind() bool
SetFilter(device.PacketFilter) error
GetDevice() *device.FilteredDevice
GetWGDevice() *wgdevice.Device
GetWGDevice() *wgbackend.Device
}
+2 -2
View File
@@ -19,7 +19,6 @@ import (
"github.com/google/uuid"
"github.com/hashicorp/go-multierror"
log "github.com/sirupsen/logrus"
wgdevice "golang.zx2c4.com/wireguard/device"
nberrors "github.com/netbirdio/netbird/client/errors"
"github.com/netbirdio/netbird/client/firewall/firewalld"
@@ -31,6 +30,7 @@ import (
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbid "github.com/netbirdio/netbird/client/internal/acl/id"
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
"github.com/netbirdio/netbird/client/internal/statemanager"
@@ -121,7 +121,7 @@ type Iface interface {
Name() string
Address() wgaddr.Address
SetFilter(device.PacketFilter) error
GetWGDevice() *wgdevice.Device
GetWGDevice() *wgbackend.Device
}
// InterfaceAllower opens the NetBird interface in the host firewall so it
@@ -5,17 +5,17 @@ import (
"net/netip"
"testing"
"go.uber.org/mock/gomock"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/stretchr/testify/require"
wgdevice "golang.zx2c4.com/wireguard/device"
"go.uber.org/mock/gomock"
fw "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/mocks"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/shared/management/domain"
)
@@ -791,10 +791,10 @@ func setupRoutedManager(tb testing.TB, network string) *Manager {
}
},
GetDeviceFunc: func() *device.FilteredDevice {
return &device.FilteredDevice{Device: dev}
return &device.FilteredDevice{TUN: dev}
},
GetWGDeviceFunc: func() *wgdevice.Device {
return &wgdevice.Device{}
GetWGDeviceFunc: func() *wgbackend.Device {
return &wgbackend.Device{}
},
}
@@ -4,17 +4,17 @@ import (
"net/netip"
"testing"
"go.uber.org/mock/gomock"
"github.com/google/gopacket/layers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
wgdevice "golang.zx2c4.com/wireguard/device"
"go.uber.org/mock/gomock"
fw "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/mocks"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// TestAddRouteFilteringReturnsExistingRule verifies that adding the same route
@@ -180,10 +180,10 @@ func TestBlockInvalidRoutedDualStack(t *testing.T) {
}
},
GetDeviceFunc: func() *device.FilteredDevice {
return &device.FilteredDevice{Device: dev}
return &device.FilteredDevice{TUN: dev}
},
GetWGDeviceFunc: func() *wgdevice.Device {
return &wgdevice.Device{}
GetWGDeviceFunc: func() *wgbackend.Device {
return &wgbackend.Device{}
},
}
@@ -228,10 +228,10 @@ func TestBlockInvalidRoutedIdempotent(t *testing.T) {
}
},
GetDeviceFunc: func() *device.FilteredDevice {
return &device.FilteredDevice{Device: dev}
return &device.FilteredDevice{TUN: dev}
},
GetWGDeviceFunc: func() *wgdevice.Device {
return &wgdevice.Device{}
GetWGDeviceFunc: func() *wgbackend.Device {
return &wgbackend.Device{}
},
}
@@ -291,10 +291,10 @@ func TestBlockRuleNotAccumulatedOnRepeatedEnableRouting(t *testing.T) {
}
},
GetDeviceFunc: func() *device.FilteredDevice {
return &device.FilteredDevice{Device: dev}
return &device.FilteredDevice{TUN: dev}
},
GetWGDeviceFunc: func() *wgdevice.Device {
return &wgdevice.Device{}
GetWGDeviceFunc: func() *wgbackend.Device {
return &wgbackend.Device{}
},
}
@@ -410,10 +410,10 @@ func setupTestManager(t *testing.T) *Manager {
}
},
GetDeviceFunc: func() *device.FilteredDevice {
return &device.FilteredDevice{Device: dev}
return &device.FilteredDevice{TUN: dev}
},
GetWGDeviceFunc: func() *wgdevice.Device {
return &wgdevice.Device{}
GetWGDeviceFunc: func() *wgbackend.Device {
return &wgbackend.Device{}
},
}
+3 -3
View File
@@ -14,7 +14,6 @@ import (
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
wgdevice "golang.zx2c4.com/wireguard/device"
fw "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
@@ -22,6 +21,7 @@ import (
nbiface "github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/netflow"
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
"github.com/netbirdio/netbird/shared/management/domain"
@@ -34,7 +34,7 @@ type IFaceMock struct {
NameFunc func() string
SetFilterFunc func(device.PacketFilter) error
AddressFunc func() wgaddr.Address
GetWGDeviceFunc func() *wgdevice.Device
GetWGDeviceFunc func() *wgbackend.Device
GetDeviceFunc func() *device.FilteredDevice
}
@@ -45,7 +45,7 @@ func (i *IFaceMock) Name() string {
return i.NameFunc()
}
func (i *IFaceMock) GetWGDevice() *wgdevice.Device {
func (i *IFaceMock) GetWGDevice() *wgbackend.Device {
if i.GetWGDeviceFunc == nil {
return nil
}
@@ -5,12 +5,12 @@ import (
"strconv"
"sync/atomic"
wgdevice "golang.zx2c4.com/wireguard/device"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
nblog "github.com/netbirdio/netbird/client/firewall/uspfilter/log"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// PacketCapture captures raw packets for debugging. Implementations must be
@@ -23,7 +23,7 @@ type PacketCapture interface {
type endpoint struct {
logger *nblog.Logger
dispatcher stack.NetworkDispatcher
device *wgdevice.Device
device *wgbackend.Device
mtu atomic.Uint32
capture atomic.Pointer[PacketCapture]
}
@@ -12,7 +12,6 @@ import (
"time"
log "github.com/sirupsen/logrus"
wgdevice "golang.zx2c4.com/wireguard/device"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -26,6 +25,7 @@ import (
"github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
nblog "github.com/netbirdio/netbird/client/firewall/uspfilter/log"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
)
@@ -43,7 +43,7 @@ const (
// IFace provides the WireGuard device and overlay addresses the forwarder needs.
type IFace interface {
GetWGDevice() *wgdevice.Device
GetWGDevice() *wgbackend.Device
Address() wgaddr.Address
}
+2 -3
View File
@@ -1,8 +1,7 @@
package bind
import (
wireguard "golang.zx2c4.com/wireguard/conn"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbnet "github.com/netbirdio/netbird/client/net"
)
@@ -10,6 +9,6 @@ import (
func init() {
listener := nbnet.NewListener()
if listener.ListenConfig.Control != nil {
*wireguard.ControlFns = append(*wireguard.ControlFns, listener.ListenConfig.Control)
*wgbackend.ControlFns = append(*wgbackend.ControlFns, listener.ListenConfig.Control)
}
}
+2 -2
View File
@@ -3,10 +3,10 @@ package bind
import (
"net"
wgConn "golang.zx2c4.com/wireguard/conn"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
type Endpoint = wgConn.StdNetEndpoint
type Endpoint = wgbackend.StdNetEndpoint
func EndpointToUDPAddr(e Endpoint) *net.UDPAddr {
return &net.UDPAddr{
+15 -15
View File
@@ -15,10 +15,10 @@ import (
"github.com/pion/transport/v3"
log "github.com/sirupsen/logrus"
"golang.org/x/net/ipv6"
wgConn "golang.zx2c4.com/wireguard/conn"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbnet "github.com/netbirdio/netbird/client/net"
)
@@ -36,7 +36,7 @@ type receiverCreator struct {
iceBind *ICEBind
}
func (rc receiverCreator) CreateReceiverFn(pc wgConn.BatchReader, conn *net.UDPConn, rxOffload bool, msgPool *sync.Pool) wgConn.ReceiveFunc {
func (rc receiverCreator) CreateReceiverFn(pc wgbackend.BatchReader, conn *net.UDPConn, rxOffload bool, msgPool *sync.Pool) wgbackend.ReceiveFunc {
return rc.iceBind.createReceiverFn(pc, conn, rxOffload, msgPool)
}
@@ -48,7 +48,7 @@ func (rc receiverCreator) CreateReceiverFn(pc wgConn.BatchReader, conn *net.UDPC
// without port, in the format of 127.1.x.x where x.x is the last two octets of the peer address. We try to avoid to
// use the port because in the Send function the wgConn.Endpoint the port info is not exported.
type ICEBind struct {
*wgConn.StdNetBind
*wgbackend.StdNetBind
transportNet transport.Net
address wgaddr.Address
@@ -74,7 +74,7 @@ type ICEBind struct {
}
func NewICEBind(transportNet transport.Net, address wgaddr.Address, mtu uint16) *ICEBind {
b, _ := wgConn.NewStdNetBind().(*wgConn.StdNetBind)
b, _ := wgbackend.NewStdNetBind().(*wgbackend.StdNetBind)
ib := &ICEBind{
StdNetBind: b,
transportNet: transportNet,
@@ -90,11 +90,11 @@ func NewICEBind(transportNet transport.Net, address wgaddr.Address, mtu uint16)
rc := receiverCreator{
ib,
}
ib.StdNetBind = wgConn.NewStdNetBindWithReceiverCreator(rc)
ib.StdNetBind = wgbackend.NewStdNetBindWithReceiverCreator(rc)
return ib
}
func (s *ICEBind) Open(uport uint16) ([]wgConn.ReceiveFunc, uint16, error) {
func (s *ICEBind) Open(uport uint16) ([]wgbackend.ReceiveFunc, uint16, error) {
s.closedChanMu.Lock()
defer s.closedChanMu.Unlock()
@@ -190,7 +190,7 @@ func (b *ICEBind) ReceiveFromEndpoint(ctx context.Context, ep *Endpoint, buf []b
}
}
func (b *ICEBind) Send(bufs [][]byte, ep wgConn.Endpoint) error {
func (b *ICEBind) Send(bufs [][]byte, ep wgbackend.Endpoint) error {
b.endpointsMu.Lock()
conn, ok := b.endpoints[ep.DstIP()]
b.endpointsMu.Unlock()
@@ -206,7 +206,7 @@ func (b *ICEBind) Send(bufs [][]byte, ep wgConn.Endpoint) error {
return nil
}
func (s *ICEBind) createReceiverFn(pc wgConn.BatchReader, conn *net.UDPConn, rxOffload bool, msgsPool *sync.Pool) wgConn.ReceiveFunc {
func (s *ICEBind) createReceiverFn(pc wgbackend.BatchReader, conn *net.UDPConn, rxOffload bool, msgsPool *sync.Pool) wgbackend.ReceiveFunc {
s.muUDPMux.Lock()
defer s.muUDPMux.Unlock()
@@ -218,7 +218,7 @@ func (s *ICEBind) createReceiverFn(pc wgConn.BatchReader, conn *net.UDPConn, rxO
}
s.createOrUpdateMux()
return func(bufs [][]byte, sizes []int, eps []wgConn.Endpoint) (n int, err error) {
return func(bufs [][]byte, sizes []int, eps []wgbackend.Endpoint) (n int, err error) {
msgs := getMessages(msgsPool)
for i := range bufs {
(*msgs)[i].Buffers[0] = bufs[i]
@@ -229,13 +229,13 @@ func (s *ICEBind) createReceiverFn(pc wgConn.BatchReader, conn *net.UDPConn, rxO
var numMsgs int
if runtime.GOOS == "linux" || runtime.GOOS == "android" {
if rxOffload {
readAt := len(*msgs) - (wgConn.IdealBatchSize / wgConn.UdpSegmentMaxDatagrams)
readAt := len(*msgs) - (wgbackend.IdealBatchSize / wgbackend.UdpSegmentMaxDatagrams)
//nolint:staticcheck
_, err = pc.ReadBatch((*msgs)[readAt:], 0)
if err != nil {
return 0, err
}
numMsgs, err = wgConn.SplitCoalescedMessages(*msgs, readAt, wgConn.GetGSOSize)
numMsgs, err = wgbackend.SplitCoalescedMessages(*msgs, readAt, wgbackend.GetGSOSize)
if err != nil {
return 0, err
}
@@ -278,8 +278,8 @@ func (s *ICEBind) createReceiverFn(pc wgConn.BatchReader, conn *net.UDPConn, rxO
s.activityRecorder.record(addrPort)
}
ep := &wgConn.StdNetEndpoint{AddrPort: addrPort} // TODO: remove allocation
wgConn.GetSrcFromControl(msg.OOB[:msg.NN], ep)
ep := &wgbackend.StdNetEndpoint{AddrPort: addrPort} // TODO: remove allocation
wgbackend.GetSrcFromControl(msg.OOB[:msg.NN], ep)
eps[i] = ep
}
return numMsgs, nil
@@ -363,7 +363,7 @@ func (s *ICEBind) parseSTUNMessage(raw []byte) (*stun.Message, error) {
// receiveRelayed is a receive function that is used to receive packets from the relayed connection and forward to the
// WireGuard. Critical part is do not block if the Closed() has been called.
func (c *ICEBind) receiveRelayed(buffs [][]byte, sizes []int, eps []wgConn.Endpoint) (int, error) {
func (c *ICEBind) receiveRelayed(buffs [][]byte, sizes []int, eps []wgbackend.Endpoint) (int, error) {
closedChan := c.currentClosedChan()
select {
@@ -375,7 +375,7 @@ func (c *ICEBind) receiveRelayed(buffs [][]byte, sizes []int, eps []wgConn.Endpo
}
copy(buffs[0], msg.Buffer)
sizes[0] = len(msg.Buffer)
eps[0] = wgConn.Endpoint(msg.Endpoint)
eps[0] = wgbackend.Endpoint(msg.Endpoint)
if isTransportPkg(buffs, sizes[0]) {
if ep, ok := eps[0].(*Endpoint); ok {
+6 -5
View File
@@ -6,7 +6,8 @@ import (
"time"
"github.com/stretchr/testify/require"
wgConn "golang.zx2c4.com/wireguard/conn"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// startReceivers runs every receive function the way wireguard-go's device
@@ -14,7 +15,7 @@ import (
// calling Bind.Close, closeBindLocked waits on exactly that WaitGroup while
// holding device.net, so a receive function that never returns wedges the
// device and every goroutine that needs the same lock.
func startReceivers(fns []wgConn.ReceiveFunc) *sync.WaitGroup {
func startReceivers(fns []wgbackend.ReceiveFunc) *sync.WaitGroup {
wg, _ := startReceiversEntered(fns)
return wg
}
@@ -23,18 +24,18 @@ func startReceivers(fns []wgConn.ReceiveFunc) *sync.WaitGroup {
// function has been called at least once. A receive function that has been
// entered is either inside its blocking receive or about to be, which is a
// stronger signal to synchronise on than a bare sleep.
func startReceiversEntered(fns []wgConn.ReceiveFunc) (*sync.WaitGroup, <-chan struct{}) {
func startReceiversEntered(fns []wgbackend.ReceiveFunc) (*sync.WaitGroup, <-chan struct{}) {
var wg sync.WaitGroup
var entered sync.WaitGroup
wg.Add(len(fns))
entered.Add(len(fns))
for i := range fns {
go func(fn wgConn.ReceiveFunc) {
go func(fn wgbackend.ReceiveFunc) {
defer wg.Done()
buffs := [][]byte{make([]byte, 1500)}
sizes := make([]int, 1)
eps := make([]wgConn.Endpoint, 1)
eps := make([]wgbackend.Endpoint, 1)
first := true
for {
if first {
+4 -5
View File
@@ -16,10 +16,9 @@ import (
"github.com/stretchr/testify/require"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
func TestICEBind_CreatesReceiverForBothIPv4AndIPv6(t *testing.T) {
@@ -370,16 +369,16 @@ func TestICEBind_OpenDoesNotDiscoverInterfaces(t *testing.T) {
}
func TestICEBind_DeviceUpDoesNotWaitOnInterfaceDiscovery(t *testing.T) {
tunDev, _, err := netstack.CreateNetTUN([]netip.Addr{netip.MustParseAddr("100.64.0.1")}, nil, 1280)
tunDev, _, err := wgbackend.CreateNetTUN([]netip.Addr{netip.MustParseAddr("100.64.0.1")}, nil, 1280)
require.NoError(t, err)
var dev *device.Device
var dev *wgbackend.Device
n := newCountingNet()
n.before = func() {
_ = dev.IpcGetOperation(io.Discard)
}
iceBind := NewICEBind(n, testAddress(), 1280)
dev = device.NewDevice(tunDev, iceBind, device.NewLogger(device.LogLevelSilent, ""))
dev = wgbackend.NewDevice(tunDev, iceBind, wgbackend.NewLogger(wgbackend.LogLevelSilent, ""))
upDone := make(chan error, 1)
go func() {
+7 -7
View File
@@ -7,15 +7,15 @@ import (
"sync"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/conn"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// RelayBindJS is a conn.Bind implementation for WebAssembly environments.
// Do not limit to build only js, because we want to be able to run tests
type RelayBindJS struct {
*conn.StdNetBind
*wgbackend.StdNetBind
recvChan chan recvMessage
endpoints map[netip.Addr]net.Conn
@@ -34,12 +34,12 @@ func NewRelayBindJS() *RelayBindJS {
}
// Open creates a receive function for handling relay packets in WASM.
func (s *RelayBindJS) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) {
func (s *RelayBindJS) Open(uport uint16) ([]wgbackend.ReceiveFunc, uint16, error) {
log.Debugf("Open: creating receive function for port %d", uport)
s.ctx, s.cancel = context.WithCancel(context.Background())
receiveFn := func(bufs [][]byte, sizes []int, eps []conn.Endpoint) (int, error) {
receiveFn := func(bufs [][]byte, sizes []int, eps []wgbackend.Endpoint) (int, error) {
select {
case <-s.ctx.Done():
return 0, net.ErrClosed
@@ -49,13 +49,13 @@ func (s *RelayBindJS) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) {
}
copy(bufs[0], msg.Buffer)
sizes[0] = len(msg.Buffer)
eps[0] = conn.Endpoint(msg.Endpoint)
eps[0] = wgbackend.Endpoint(msg.Endpoint)
return 1, nil
}
}
log.Debugf("Open: receive function created, returning port %d", uport)
return []conn.ReceiveFunc{receiveFn}, uport, nil
return []wgbackend.ReceiveFunc{receiveFn}, uport, nil
}
func (s *RelayBindJS) Close() error {
@@ -78,7 +78,7 @@ func (s *RelayBindJS) ReceiveFromEndpoint(ctx context.Context, ep *Endpoint, buf
}
// Send forwards packets through the relay connection for WASM.
func (s *RelayBindJS) Send(bufs [][]byte, ep conn.Endpoint) error {
func (s *RelayBindJS) Send(bufs [][]byte, ep wgbackend.Endpoint) error {
if ep == nil {
return nil
}
+3 -2
View File
@@ -12,7 +12,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/net/ipv4"
wgConn "golang.zx2c4.com/wireguard/conn"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// magicCookieBytes is the STUN magic cookie as it appears on the wire. In a
@@ -165,7 +166,7 @@ func TestReceiveFn_ClearsSizeOfConsumedPacket(t *testing.T) {
// A leftover size from an earlier read, which is what makes the missing reset
// observable.
sizes := []int{148}
eps := make([]wgConn.Endpoint, 1)
eps := make([]wgbackend.Endpoint, 1)
n, err := recvFn(bufs, sizes, eps)
require.NoError(t, err)
+3 -3
View File
@@ -5,16 +5,16 @@ package configurer
import (
"net"
"golang.zx2c4.com/wireguard/ipc"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
func openUAPI(deviceName string) (net.Listener, error) {
uapiSock, err := ipc.UAPIOpen(deviceName)
uapiSock, err := wgbackend.UAPIOpen(deviceName)
if err != nil {
return nil, err
}
listener, err := ipc.UAPIListen(deviceName, uapiSock)
listener, err := wgbackend.UAPIListen(deviceName, uapiSock)
if err != nil {
_ = uapiSock.Close()
return nil, err
+2 -2
View File
@@ -3,9 +3,9 @@ package configurer
import (
"net"
"golang.zx2c4.com/wireguard/ipc"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
func openUAPI(deviceName string) (net.Listener, error) {
return ipc.UAPIListen(deviceName)
return wgbackend.UAPIListen(deviceName)
}
+17 -18
View File
@@ -10,11 +10,10 @@ import (
"time"
log "github.com/sirupsen/logrus"
wgconn "golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbnet "github.com/netbirdio/netbird/client/net"
"github.com/netbirdio/netbird/monotime"
)
@@ -22,7 +21,7 @@ import (
var ErrAllowedIPNotFound = fmt.Errorf("allowed IP not found")
type WGUSPConfigurer struct {
device *device.Device
device *wgbackend.Device
deviceName string
activityRecorder *bind.ActivityRecorder
statsCache *statsCache
@@ -31,14 +30,14 @@ type WGUSPConfigurer struct {
}
// NewUSPConfigurer creates a userspace configurer and starts its UAPI listener.
func NewUSPConfigurer(device *device.Device, deviceName string, activityRecorder *bind.ActivityRecorder) *WGUSPConfigurer {
func NewUSPConfigurer(device *wgbackend.Device, deviceName string, activityRecorder *bind.ActivityRecorder) *WGUSPConfigurer {
wgCfg := NewUSPConfigurerNoUAPI(device, deviceName, activityRecorder)
wgCfg.startUAPI()
return wgCfg
}
// NewUSPConfigurerNoUAPI creates a userspace configurer without a UAPI listener.
func NewUSPConfigurerNoUAPI(device *device.Device, deviceName string, activityRecorder *bind.ActivityRecorder) *WGUSPConfigurer {
func NewUSPConfigurerNoUAPI(device *wgbackend.Device, deviceName string, activityRecorder *bind.ActivityRecorder) *WGUSPConfigurer {
wgCfg := &WGUSPConfigurer{
device: device,
deviceName: deviceName,
@@ -59,7 +58,7 @@ func (c *WGUSPConfigurer) ConfigureInterface(privateKey string, port int) error
return fmt.Errorf("invalid listen port %d", port)
}
if err := c.device.SetPrivateKey(device.NoisePrivateKey(key)); err != nil {
if err := c.device.SetPrivateKey(wgbackend.NoisePrivateKey(key)); err != nil {
return fmt.Errorf("set private key: %w", err)
}
if err := c.device.SetListenPort(uint16(port)); err != nil {
@@ -80,8 +79,8 @@ func (c *WGUSPConfigurer) SetPresharedKey(peerKey string, psk wgtypes.Key, updat
return err
}
presharedKey := device.NoisePresharedKey(psk)
return c.device.ConfigurePeer(device.NoisePublicKey(parsedPeerKey), device.PeerConfig{
presharedKey := wgbackend.NoisePresharedKey(psk)
return c.device.ConfigurePeer(wgbackend.NoisePublicKey(parsedPeerKey), wgbackend.PeerConfig{
PresharedKey: &presharedKey,
UpdateOnly: updateOnly,
})
@@ -99,7 +98,7 @@ func (c *WGUSPConfigurer) UpdatePeer(peerKey string, allowedIps []netip.Prefix,
return err
}
cfg := device.PeerConfig{
cfg := wgbackend.PeerConfig{
PersistentKeepalive: &keepAlive,
AllowedIPs: prefixes,
}
@@ -116,11 +115,11 @@ func (c *WGUSPConfigurer) UpdatePeer(peerKey string, allowedIps []netip.Prefix,
}
if preSharedKey != nil {
psk := device.NoisePresharedKey(*preSharedKey)
psk := wgbackend.NoisePresharedKey(*preSharedKey)
cfg.PresharedKey = &psk
}
if err := c.device.ConfigurePeer(device.NoisePublicKey(peerKeyParsed), cfg); err != nil {
if err := c.device.ConfigurePeer(wgbackend.NoisePublicKey(peerKeyParsed), cfg); err != nil {
return fmt.Errorf("configure peer: %w", err)
}
@@ -138,7 +137,7 @@ func (c *WGUSPConfigurer) RemoveEndpointAddress(peerKey string) error {
return fmt.Errorf("parse peer key: %w", err)
}
peer := c.device.LookupPeer(device.NoisePublicKey(peerKeyParsed))
peer := c.device.LookupPeer(wgbackend.NoisePublicKey(peerKeyParsed))
if peer == nil {
return ErrPeerNotFound
}
@@ -147,7 +146,7 @@ func (c *WGUSPConfigurer) RemoveEndpointAddress(peerKey string) error {
peer.ZeroAndFlushAll()
var noKeepAlive time.Duration
if err := c.device.ConfigurePeer(device.NoisePublicKey(peerKeyParsed), device.PeerConfig{
if err := c.device.ConfigurePeer(wgbackend.NoisePublicKey(peerKeyParsed), wgbackend.PeerConfig{
PersistentKeepalive: &noKeepAlive,
UpdateOnly: true,
}); err != nil {
@@ -163,7 +162,7 @@ func (c *WGUSPConfigurer) RemovePeer(peerKey string) error {
return err
}
c.device.RemovePeer(device.NoisePublicKey(peerKeyParsed))
c.device.RemovePeer(wgbackend.NoisePublicKey(peerKeyParsed))
c.activityRecorder.Remove(peerKey)
return nil
}
@@ -178,7 +177,7 @@ func (c *WGUSPConfigurer) AddAllowedIP(peerKey string, allowedIP netip.Prefix) e
return fmt.Errorf("invalid allowed IP %v", allowedIP)
}
peer := c.device.LookupPeer(device.NoisePublicKey(peerKeyParsed))
peer := c.device.LookupPeer(wgbackend.NoisePublicKey(peerKeyParsed))
if peer == nil {
return nil
}
@@ -198,7 +197,7 @@ func (c *WGUSPConfigurer) RemoveAllowedIP(peerKey string, allowedIP netip.Prefix
return ErrAllowedIPNotFound
}
peer := c.device.LookupPeer(device.NoisePublicKey(peerKeyParsed))
peer := c.device.LookupPeer(wgbackend.NoisePublicKey(peerKeyParsed))
if peer == nil {
return ErrPeerNotFound
}
@@ -284,7 +283,7 @@ func (c *WGUSPConfigurer) fetchStats() (map[string]WGStats, error) {
return stats, nil
}
func peerStats(peer *device.Peer) Peer {
func peerStats(peer *wgbackend.Peer) Peer {
stats := Peer{
PublicKey: wgtypes.Key(peer.PublicKey()).String(),
AllowedIPs: prefixesToIPNets(peer.AllowedIPs()),
@@ -299,7 +298,7 @@ func peerStats(peer *device.Peer) Peer {
return stats
}
func endpointAddrPort(endpoint wgconn.Endpoint) (netip.AddrPort, bool) {
func endpointAddrPort(endpoint wgbackend.Endpoint) (netip.AddrPort, bool) {
if endpoint == nil {
return netip.AddrPort{}, false
}
@@ -8,11 +8,10 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
wgconn "golang.zx2c4.com/wireguard/conn"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// newTestUSPConfigurer builds a configurer over a real wireguard-go device backed by an
@@ -20,7 +19,7 @@ import (
func newTestUSPConfigurer(t *testing.T) *WGUSPConfigurer {
t.Helper()
dev := wgdevice.NewDevice(newIdleTUN(), wgconn.NewDefaultBind(), wgdevice.NewLogger(wgdevice.LogLevelSilent, ""))
dev := wgbackend.NewDevice(newIdleTUN(), wgbackend.NewDefaultBind(), wgbackend.NewLogger(wgbackend.LogLevelSilent, ""))
t.Cleanup(dev.Close)
c := NewUSPConfigurerNoUAPI(dev, "wgtest0", bind.NewActivityRecorder())
+7 -7
View File
@@ -11,20 +11,20 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// idleTUN never reports an up event, so the wireguard-go device stays down: no socket is
// opened, no peer goroutine starts, and the configuration state is all the tests observe.
type idleTUN struct {
events chan tun.Event
events chan wgbackend.TUNEvent
closed chan struct{}
}
func newIdleTUN() *idleTUN {
return &idleTUN{events: make(chan tun.Event), closed: make(chan struct{})}
return &idleTUN{events: make(chan wgbackend.TUNEvent), closed: make(chan struct{})}
}
func (t *idleTUN) File() *os.File { return nil }
@@ -40,7 +40,7 @@ func (t *idleTUN) MTU() (int, error) { return 1420, nil }
func (t *idleTUN) Name() (string, error) { return "idle", nil }
func (t *idleTUN) Events() <-chan tun.Event { return t.events }
func (t *idleTUN) Events() <-chan wgbackend.TUNEvent { return t.events }
func (t *idleTUN) Close() error {
close(t.closed)
@@ -142,12 +142,12 @@ func TestRemoveEndpointAddressKeepsThePeerAndStopsTheKeepalive(t *testing.T) {
endpoint := &net.UDPAddr{IP: net.IPv4(192, 0, 2, 10), Port: 51820}
prefixes := []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")}
require.NoError(t, c.UpdatePeer(peerKey, prefixes, 25*time.Second, endpoint, nil), "add peer")
before := c.device.LookupPeer(wgdevice.NoisePublicKey(priv.PublicKey()))
before := c.device.LookupPeer(wgbackend.NoisePublicKey(priv.PublicKey()))
require.NotNil(t, before, "the peer must be on the device")
require.NoError(t, c.RemoveEndpointAddress(peerKey), "remove endpoint address")
assert.Same(t, before, c.device.LookupPeer(wgdevice.NoisePublicKey(priv.PublicKey())),
assert.Same(t, before, c.device.LookupPeer(wgbackend.NoisePublicKey(priv.PublicKey())),
"the peer object must survive the endpoint removal")
stats, err := c.FullStats()
require.NoError(t, err, "read device stats")
+3 -6
View File
@@ -3,13 +3,10 @@
package iface
import (
"golang.zx2c4.com/wireguard/tun/netstack"
wgdevice "golang.zx2c4.com/wireguard/device"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
type WGTunDevice interface {
@@ -21,7 +18,7 @@ type WGTunDevice interface {
DeviceName() string
Close() error
FilteredDevice() *device.FilteredDevice
Device() *wgdevice.Device
GetNet() *netstack.Net
Device() *wgbackend.Device
GetNet() *wgbackend.NetstackNet
GetICEBind() device.EndpointManager
}
+7 -9
View File
@@ -8,14 +8,12 @@ import (
log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// WGTunDevice ignore the WGTunDevice interface on Android because the creation of the tun device is different on this platform
@@ -30,7 +28,7 @@ type WGTunDevice struct {
disableDNS bool
name string
device *device.Device
device *wgbackend.Device
filteredDevice *FilteredDevice
udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer
@@ -74,7 +72,7 @@ func (t *WGTunDevice) Create(routes []string, dns string, searchDomains []string
return nil, err
}
unmonitoredTUN, name, err := tun.CreateUnmonitoredTUNFromFD(fd)
unmonitoredTUN, name, err := wgbackend.CreateUnmonitoredTUNFromFD(fd)
if err != nil {
_ = unix.Close(fd)
log.Errorf("failed to create Android interface: %s", err)
@@ -87,7 +85,7 @@ func (t *WGTunDevice) Create(routes []string, dns string, searchDomains []string
t.filteredDevice = newDeviceFilter(t.renewableTun)
log.Debugf("attaching to interface %v", name)
t.device = device.NewDevice(t.filteredDevice, t.iceBind, device.NewLogger(wgLogLevel(), "[netbird] "))
t.device = wgbackend.NewDevice(t.filteredDevice, t.iceBind, wgbackend.NewLogger(wgLogLevel(), "[netbird] "))
// without this property mobile devices can discover remote endpoints if the configured one was wrong.
// this helps with support for the older NetBird clients that had a hardcoded direct mode
// t.device.DisableSomeRoamingForBrokenMobileSemantics()
@@ -121,7 +119,7 @@ func (t *WGTunDevice) RenewTun(fd int) error {
return fmt.Errorf("device not initialized")
}
unmonitoredTUN, _, err := tun.CreateUnmonitoredTUNFromFD(fd)
unmonitoredTUN, _, err := wgbackend.CreateUnmonitoredTUNFromFD(fd)
if err != nil {
_ = unix.Close(fd)
log.Errorf("failed to renew Android interface: %s", err)
@@ -155,7 +153,7 @@ func (t *WGTunDevice) Close() error {
return nil
}
func (t *WGTunDevice) Device() *device.Device {
func (t *WGTunDevice) Device() *wgbackend.Device {
return t.device
}
@@ -175,7 +173,7 @@ func (t *WGTunDevice) FilteredDevice() *FilteredDevice {
return t.filteredDevice
}
func (t *WGTunDevice) GetNet() *netstack.Net {
func (t *WGTunDevice) GetNet() *wgbackend.NetstackNet {
return nil
}
+7 -9
View File
@@ -7,14 +7,12 @@ import (
"os/exec"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
type TunDevice struct {
@@ -25,7 +23,7 @@ type TunDevice struct {
mtu uint16
iceBind *bind.ICEBind
device *device.Device
device *wgbackend.Device
filteredDevice *FilteredDevice
udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer
@@ -43,17 +41,17 @@ func NewTunDevice(name string, address wgaddr.Address, port int, key string, mtu
}
func (t *TunDevice) Create() (WGConfigurer, error) {
tunDevice, err := tun.CreateTUN(t.name, int(t.mtu))
tunDevice, err := wgbackend.CreateTUN(t.name, int(t.mtu))
if err != nil {
return nil, fmt.Errorf("error creating tun device: %s", err)
}
t.filteredDevice = newDeviceFilter(tunDevice)
// We need to create a wireguard-go device and listen to configuration requests
t.device = device.NewDevice(
t.device = wgbackend.NewDevice(
t.filteredDevice,
t.iceBind,
device.NewLogger(wgLogLevel(), "[netbird] "),
wgbackend.NewLogger(wgLogLevel(), "[netbird] "),
)
err = t.assignAddr()
@@ -125,7 +123,7 @@ func (t *TunDevice) FilteredDevice() *FilteredDevice {
}
// Device returns the wireguard device
func (t *TunDevice) Device() *device.Device {
func (t *TunDevice) Device() *wgbackend.Device {
return t.device
}
@@ -160,7 +158,7 @@ func (t *TunDevice) assignAddr() error {
return nil
}
func (t *TunDevice) GetNet() *netstack.Net {
func (t *TunDevice) GetNet() *wgbackend.NetstackNet {
return nil
}
+8 -7
View File
@@ -8,7 +8,8 @@ import (
"sync/atomic"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// PacketFilter interface for firewall abilities
@@ -42,7 +43,7 @@ type PacketCapture interface {
// FilteredDevice to override Read or Write of packets
type FilteredDevice struct {
tun.Device
wgbackend.TUN
filter PacketFilter
capture atomic.Pointer[PacketCapture]
@@ -54,9 +55,9 @@ type FilteredDevice struct {
}
// newDeviceFilter constructor function
func newDeviceFilter(device tun.Device) *FilteredDevice {
func newDeviceFilter(device wgbackend.TUN) *FilteredDevice {
return &FilteredDevice{
Device: device,
TUN: device,
}
}
@@ -66,7 +67,7 @@ func newDeviceFilter(device tun.Device) *FilteredDevice {
func (d *FilteredDevice) Close() error {
var err error
d.closeOnce.Do(func() {
err = d.Device.Close()
err = d.TUN.Close()
})
if err != nil {
return err
@@ -142,14 +143,14 @@ func (d *FilteredDevice) Write(bufs [][]byte, offset int) (int, error) {
// wintun read path and converting them into errors.
func (d *FilteredDevice) deviceRead(bufs [][]byte, sizes []int, offset int) (n int, err error) {
defer d.recoverFromPanic("read", &n, &err)
return d.Device.Read(bufs, sizes, offset)
return d.TUN.Read(bufs, sizes, offset)
}
// deviceWrite calls the underlying device Write, recovering from panics in the
// wintun write path and converting them into errors.
func (d *FilteredDevice) deviceWrite(bufs [][]byte, offset int) (n int, err error) {
defer d.recoverFromPanic("write", &n, &err)
return d.Device.Write(bufs, offset)
return d.TUN.Write(bufs, offset)
}
// recoverFromPanic converts a panic in the underlying device into a regular
+7 -9
View File
@@ -6,14 +6,12 @@ import (
log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
type TunDevice struct {
@@ -25,7 +23,7 @@ type TunDevice struct {
iceBind *bind.ICEBind
tunFd int
device *device.Device
device *wgbackend.Device
filteredDevice *FilteredDevice
udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer
@@ -50,7 +48,7 @@ var ErrInvalidTunnelFD = fmt.Errorf("invalid tunnel file descriptor: fd is 0 (Sw
func (t *TunDevice) Create() (WGConfigurer, error) {
log.Infof("create tun interface")
var tunDevice tun.Device
var tunDevice wgbackend.TUN
var err error
// Validate the tunnel file descriptor.
@@ -79,7 +77,7 @@ func (t *TunDevice) Create() (WGConfigurer, error) {
_ = unix.Close(dupTunFd)
return nil, err
}
tunDevice, err = tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0)
tunDevice, err = wgbackend.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0)
if err != nil {
log.Errorf("Unable to create new tun device from fd: %v", err)
_ = unix.Close(dupTunFd)
@@ -88,7 +86,7 @@ func (t *TunDevice) Create() (WGConfigurer, error) {
t.filteredDevice = newDeviceFilter(tunDevice)
log.Debug("Attaching to interface")
t.device = device.NewDevice(t.filteredDevice, t.iceBind, device.NewLogger(wgLogLevel(), "[netbird] "))
t.device = wgbackend.NewDevice(t.filteredDevice, t.iceBind, wgbackend.NewLogger(wgLogLevel(), "[netbird] "))
// without this property mobile devices can discover remote endpoints if the configured one was wrong.
// this helps with support for the older NetBird clients that had a hardcoded direct mode
// t.device.DisableSomeRoamingForBrokenMobileSemantics()
@@ -118,7 +116,7 @@ func (t *TunDevice) Up() (*udpmux.UniversalUDPMuxDefault, error) {
return udpMux, nil
}
func (t *TunDevice) Device() *device.Device {
func (t *TunDevice) Device() *wgbackend.Device {
return t.device
}
@@ -163,7 +161,7 @@ func (t *TunDevice) FilteredDevice() *FilteredDevice {
return t.filteredDevice
}
func (t *TunDevice) GetNet() *netstack.Net {
func (t *TunDevice) GetNet() *wgbackend.NetstackNet {
return nil
}
+3 -4
View File
@@ -9,12 +9,11 @@ import (
"github.com/pion/transport/v3"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbnet "github.com/netbirdio/netbird/client/net"
"github.com/netbirdio/netbird/sharedsock"
)
@@ -170,7 +169,7 @@ func (t *TunKernelDevice) DeviceName() string {
}
// Device returns the wireguard device, not applicable for kernel devices
func (t *TunKernelDevice) Device() *device.Device {
func (t *TunKernelDevice) Device() *wgbackend.Device {
return nil
}
@@ -183,7 +182,7 @@ func (t *TunKernelDevice) assignAddr() error {
return t.link.assignAddr(&t.address)
}
func (t *TunKernelDevice) GetNet() *netstack.Net {
func (t *TunKernelDevice) GetNet() *wgbackend.NetstackNet {
return nil
}
+8 -10
View File
@@ -6,20 +6,18 @@ import (
"net/netip"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/configurer"
nbnetstack "github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbnet "github.com/netbirdio/netbird/client/net"
)
type Bind interface {
conn.Bind
wgbackend.Bind
GetICEMux() (*udpmux.UniversalUDPMuxDefault, error)
ActivityRecorder() *bind.ActivityRecorder
EndpointManager
@@ -34,13 +32,13 @@ type TunNetstackDevice struct {
listenAddress string
bind Bind
device *device.Device
device *wgbackend.Device
filteredDevice *FilteredDevice
nsTun *nbnetstack.NetStackTun
udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer
net *netstack.Net
net *wgbackend.NetstackNet
}
func NewNetstackDevice(name string, address wgaddr.Address, wgPort int, key string, mtu uint16, bind Bind, listenAddress string) *TunNetstackDevice {
@@ -78,10 +76,10 @@ func (t *TunNetstackDevice) create() (WGConfigurer, error) {
t.filteredDevice = newDeviceFilter(tunIface)
t.net = net
t.device = device.NewDevice(
t.device = wgbackend.NewDevice(
t.filteredDevice,
t.bind,
device.NewLogger(wgLogLevel(), "[netbird] "),
wgbackend.NewLogger(wgLogLevel(), "[netbird] "),
)
t.configurer = configurer.NewUSPConfigurerNoUAPI(t.device, t.name, t.bind.ActivityRecorder())
@@ -156,11 +154,11 @@ func (t *TunNetstackDevice) FilteredDevice() *FilteredDevice {
}
// Device returns the wireguard device
func (t *TunNetstackDevice) Device() *device.Device {
func (t *TunNetstackDevice) Device() *wgbackend.Device {
return t.device
}
func (t *TunNetstackDevice) GetNet() *netstack.Net {
func (t *TunNetstackDevice) GetNet() *wgbackend.NetstackNet {
return t.net
}
+7 -9
View File
@@ -6,14 +6,12 @@ import (
"fmt"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
type TunDevice struct {
@@ -24,7 +22,7 @@ type TunDevice struct {
mtu uint16
iceBind *bind.ICEBind
device *device.Device
device *wgbackend.Device
filteredDevice *FilteredDevice
udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer
@@ -45,7 +43,7 @@ func NewTunDevice(name string, address wgaddr.Address, port int, key string, mtu
func (t *TunDevice) Create() (WGConfigurer, error) {
log.Info("create tun interface")
tunIface, err := tun.CreateTUN(t.name, int(t.mtu))
tunIface, err := wgbackend.CreateTUN(t.name, int(t.mtu))
if err != nil {
log.Debugf("failed to create tun interface (%s, %d): %s", t.name, int(t.mtu), err)
return nil, fmt.Errorf("error creating tun device: %s", err)
@@ -53,10 +51,10 @@ func (t *TunDevice) Create() (WGConfigurer, error) {
t.filteredDevice = newDeviceFilter(tunIface)
// We need to create a wireguard-go device and listen to configuration requests
t.device = device.NewDevice(
t.device = wgbackend.NewDevice(
t.filteredDevice,
t.iceBind,
device.NewLogger(wgLogLevel(), "[netbird] "),
wgbackend.NewLogger(wgLogLevel(), "[netbird] "),
)
err = t.assignAddr()
@@ -132,7 +130,7 @@ func (t *TunDevice) FilteredDevice() *FilteredDevice {
}
// Device returns the wireguard device
func (t *TunDevice) Device() *device.Device {
func (t *TunDevice) Device() *wgbackend.Device {
return t.device
}
@@ -143,7 +141,7 @@ func (t *TunDevice) assignAddr() error {
return link.assignAddr(&t.address)
}
func (t *TunDevice) GetNet() *netstack.Net {
func (t *TunDevice) GetNet() *wgbackend.NetstackNet {
return nil
}
+9 -11
View File
@@ -6,15 +6,13 @@ import (
log "github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
const defaultWindowsGUIDSTring = "{f2f29e61-d91f-4d76-8151-119b20c4bdeb}"
@@ -27,8 +25,8 @@ type TunDevice struct {
mtu uint16
iceBind *bind.ICEBind
device *device.Device
nativeTunDevice *tun.NativeTun
device *wgbackend.Device
nativeTunDevice *wgbackend.NativeTUN
filteredDevice *FilteredDevice
udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer
@@ -60,18 +58,18 @@ func (t *TunDevice) Create() (WGConfigurer, error) {
return nil, err
}
log.Info("create tun interface")
tunDevice, err := tun.CreateTUNWithRequestedGUID(t.name, &guid, int(t.mtu))
tunDevice, err := wgbackend.CreateTUNWithRequestedGUID(t.name, &guid, int(t.mtu))
if err != nil {
return nil, fmt.Errorf("error creating tun device: %s", err)
}
t.nativeTunDevice = tunDevice.(*tun.NativeTun)
t.nativeTunDevice = tunDevice.(*wgbackend.NativeTUN)
t.filteredDevice = newDeviceFilter(tunDevice)
// We need to create a wireguard-go device and listen to configuration requests
t.device = device.NewDevice(
t.device = wgbackend.NewDevice(
t.filteredDevice,
t.iceBind,
device.NewLogger(wgLogLevel(), "[netbird] "),
wgbackend.NewLogger(wgLogLevel(), "[netbird] "),
)
luid := winipcfg.LUID(t.nativeTunDevice.LUID())
@@ -172,7 +170,7 @@ func (t *TunDevice) FilteredDevice() *FilteredDevice {
}
// Device returns the wireguard device
func (t *TunDevice) Device() *device.Device {
func (t *TunDevice) Device() *wgbackend.Device {
return t.device
}
@@ -209,7 +207,7 @@ func (t *TunDevice) assignAddr() error {
return luid.SetIPAddresses([]netip.Prefix{v4Prefix})
}
func (t *TunDevice) GetNet() *netstack.Net {
func (t *TunDevice) GetNet() *wgbackend.NetstackNet {
return nil
}
+13 -12
View File
@@ -10,7 +10,8 @@ import (
"time"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// closeAwareDevice wraps a tun.Device along with a flag
@@ -23,15 +24,15 @@ import (
// goroutine is awaited and closed only once.
type closeAwareDevice struct {
isClosed atomic.Bool
tun.Device
wgbackend.TUN
closeEventCh chan struct{}
wg sync.WaitGroup
closeOnce sync.Once
}
func newClosableDevice(tunDevice tun.Device) *closeAwareDevice {
func newClosableDevice(tunDevice wgbackend.TUN) *closeAwareDevice {
return &closeAwareDevice{
Device: tunDevice,
TUN: tunDevice,
isClosed: atomic.Bool{},
closeEventCh: make(chan struct{}),
}
@@ -39,18 +40,18 @@ func newClosableDevice(tunDevice tun.Device) *closeAwareDevice {
// redirectEvents redirects the Events() method of the underlying tun.Device
// to the given channel (RenewableTUN's events channel).
func (c *closeAwareDevice) redirectEvents(out chan tun.Event) {
func (c *closeAwareDevice) redirectEvents(out chan wgbackend.TUNEvent) {
c.wg.Add(1)
go func() {
defer c.wg.Done()
for {
select {
case ev, ok := <-c.Device.Events():
case ev, ok := <-c.TUN.Events():
if !ok {
return
}
if ev == tun.EventDown {
if ev == wgbackend.TUNEventDown {
continue
}
@@ -72,7 +73,7 @@ func (c *closeAwareDevice) Close() (err error) {
c.closeOnce.Do(func() {
c.isClosed.Store(true)
close(c.closeEventCh)
err = c.Device.Close()
err = c.TUN.Close()
c.wg.Wait()
})
@@ -87,7 +88,7 @@ type RenewableTUN struct {
devices []*closeAwareDevice
mu sync.Mutex
cond *sync.Cond
events chan tun.Event
events chan wgbackend.TUNEvent
closed atomic.Bool
}
@@ -95,7 +96,7 @@ func NewRenewableTUN() *RenewableTUN {
r := &RenewableTUN{
devices: make([]*closeAwareDevice, 0),
mu: sync.Mutex{},
events: make(chan tun.Event, 16),
events: make(chan wgbackend.TUNEvent, 16),
}
r.cond = sync.NewCond(&r.mu)
return r
@@ -225,7 +226,7 @@ func (r *RenewableTUN) Name() (string, error) {
// Events returns a channel that is fed events from the underlying tun.Device's events channel
// once it is added.
func (r *RenewableTUN) Events() <-chan tun.Event {
func (r *RenewableTUN) Events() <-chan wgbackend.TUNEvent {
return r.events
}
@@ -259,7 +260,7 @@ func (r *RenewableTUN) BatchSize() int {
return 1
}
func (r *RenewableTUN) AddDevice(device tun.Device) {
func (r *RenewableTUN) AddDevice(device wgbackend.TUN) {
r.mu.Lock()
if r.closed.Load() {
r.mu.Unlock()
+3 -3
View File
@@ -3,13 +3,13 @@ package device
import (
"os"
"golang.zx2c4.com/wireguard/device"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
func wgLogLevel() int {
if os.Getenv("NB_WG_DEBUG") == "true" {
return device.LogLevelVerbose
return wgbackend.LogLevelVerbose
} else {
return device.LogLevelSilent
return wgbackend.LogLevelSilent
}
}
+3 -6
View File
@@ -1,13 +1,10 @@
package iface
import (
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
type WGTunDevice interface {
@@ -19,8 +16,8 @@ type WGTunDevice interface {
DeviceName() string
Close() error
FilteredDevice() *device.FilteredDevice
Device() *wgdevice.Device
GetNet() *netstack.Net
Device() *wgbackend.Device
GetNet() *wgbackend.NetstackNet
RenewTun(fd int) error
GetICEBind() device.EndpointManager
}
+3 -5
View File
@@ -10,17 +10,15 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/pion/transport/v3"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
wgdevice "golang.zx2c4.com/wireguard/device"
"github.com/netbirdio/netbird/client/errors"
"github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/device"
nbnetstack "github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/iface/wgproxy"
"github.com/netbirdio/netbird/monotime"
)
@@ -271,7 +269,7 @@ func (w *WGIface) GetDevice() *device.FilteredDevice {
}
// GetWGDevice returns the WireGuard device
func (w *WGIface) GetWGDevice() *wgdevice.Device {
func (w *WGIface) GetWGDevice() *wgbackend.Device {
return w.tun.Device()
}
@@ -344,7 +342,7 @@ func (w *WGIface) waitUntilRemoved() error {
}
// GetNet returns the netstack.Net for the netstack device
func (w *WGIface) GetNet() *netstack.Net {
func (w *WGIface) GetNet() *wgbackend.NetstackNet {
w.mu.Lock()
defer w.mu.Unlock()
+17 -11
View File
@@ -8,12 +8,11 @@ import (
"testing"
"time"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device"
nbnetstack "github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/iface/wgproxy"
)
@@ -34,14 +33,14 @@ func (f *fakeTunDevice) Create() (device.WGConfigurer, error) {
func (f *fakeTunDevice) Up() (*udpmux.UniversalUDPMuxDefault, error) {
return nil, errors.New("not implemented")
}
func (f *fakeTunDevice) UpdateAddr(wgaddr.Address) error { return nil }
func (f *fakeTunDevice) WgAddress() wgaddr.Address { return wgaddr.Address{} }
func (f *fakeTunDevice) MTU() uint16 { return DefaultMTU }
func (f *fakeTunDevice) DeviceName() string { return "nb-close-test" }
func (f *fakeTunDevice) UpdateAddr(wgaddr.Address) error { return nil }
func (f *fakeTunDevice) WgAddress() wgaddr.Address { return wgaddr.Address{} }
func (f *fakeTunDevice) MTU() uint16 { return DefaultMTU }
func (f *fakeTunDevice) DeviceName() string { return "nb-close-test" }
func (f *fakeTunDevice) FilteredDevice() *device.FilteredDevice { return nil }
func (f *fakeTunDevice) Device() *wgdevice.Device { return nil }
func (f *fakeTunDevice) GetNet() *netstack.Net { return nil }
func (f *fakeTunDevice) GetICEBind() device.EndpointManager { return nil }
func (f *fakeTunDevice) Device() *wgbackend.Device { return nil }
func (f *fakeTunDevice) GetNet() *wgbackend.NetstackNet { return nil }
func (f *fakeTunDevice) GetICEBind() device.EndpointManager { return nil }
func (f *fakeTunDevice) Close() error {
close(f.closeStarted)
@@ -62,6 +61,10 @@ func (fakeProxyFactory) Free() error { return nil }
// WGIface.GetDevice() which needs the same mutex. The fix is to drop
// the lock before tun.Close() returns control.
func TestWGIface_CloseReleasesMutexBeforeTunClose(t *testing.T) {
// Netstack mode skips the platform interface cleanup in Close, so only
// the mutex and tun close behavior is exercised.
t.Setenv(nbnetstack.EnvUseNetstackMode, "true")
tun := &fakeTunDevice{
closeStarted: make(chan struct{}),
unblockClose: make(chan struct{}),
@@ -105,7 +108,10 @@ func TestWGIface_CloseReleasesMutexBeforeTunClose(t *testing.T) {
close(tun.unblockClose)
select {
case <-closeDone:
case err := <-closeDone:
if err != nil {
t.Errorf("WGIface.Close() returned an error: %v", err)
}
case <-time.After(2 * time.Second):
t.Fatal("WGIface.Close() never returned after the tun was unblocked")
}
+2 -2
View File
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: golang.zx2c4.com/wireguard/tun (interfaces: Device)
// Source: github.com/netbirdio/wireguard-go/tun (interfaces: Device)
// Package mocks is a generated GoMock package.
package mocks
@@ -8,8 +8,8 @@ import (
os "os"
reflect "reflect"
tun "github.com/netbirdio/wireguard-go/tun"
gomock "go.uber.org/mock/gomock"
tun "golang.zx2c4.com/wireguard/tun"
)
// MockDevice is a mock of Device interface.
+4 -3
View File
@@ -5,7 +5,8 @@ import (
"net"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
type Dialer interface {
@@ -13,10 +14,10 @@ type Dialer interface {
}
type NSDialer struct {
net *netstack.Net
net *wgbackend.NetstackNet
}
func NewNSDialer(net *netstack.Net) *NSDialer {
func NewNSDialer(net *wgbackend.NetstackNet) *NSDialer {
return &NSDialer{
net: net,
}
+5 -5
View File
@@ -6,8 +6,8 @@ import (
"strconv"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
const EnvSkipProxy = "NB_NETSTACK_SKIP_PROXY"
@@ -19,7 +19,7 @@ type NetStackTun struct { //nolint:revive
listenAddress string
proxy *Proxy
tundev tun.Device
tundev wgbackend.TUN
}
func NewNetStackTun(listenAddress string, addresses []netip.Addr, dnsAddress netip.Addr, mtu int) *NetStackTun {
@@ -31,8 +31,8 @@ func NewNetStackTun(listenAddress string, addresses []netip.Addr, dnsAddress net
}
}
func (t *NetStackTun) Create() (tun.Device, *netstack.Net, error) {
nsTunDev, tunNet, err := netstack.CreateNetTUN(
func (t *NetStackTun) Create() (wgbackend.TUN, *wgbackend.NetstackNet, error) {
nsTunDev, tunNet, err := wgbackend.CreateNetTUN(
t.addresses,
[]netip.Addr{t.dnsAddress},
t.mtu)
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"strings"
"sync"
"github.com/pion/ice/v4"
"github.com/netbirdio/ice/v4"
"github.com/pion/logging"
"github.com/pion/stun/v3"
"github.com/pion/transport/v3"
+27
View File
@@ -0,0 +1,27 @@
package wgbackend
import "github.com/netbirdio/wireguard-go/conn"
type (
BatchReader = conn.BatchReader
Bind = conn.Bind
Endpoint = conn.Endpoint
ReceiveFunc = conn.ReceiveFunc
StdNetBind = conn.StdNetBind
StdNetEndpoint = conn.StdNetEndpoint
)
const (
IdealBatchSize = conn.IdealBatchSize
UdpSegmentMaxDatagrams = conn.UdpSegmentMaxDatagrams
)
var (
ControlFns = conn.ControlFns
GetGSOSize = conn.GetGSOSize
GetSrcFromControl = conn.GetSrcFromControl
NewDefaultBind = conn.NewDefaultBind
NewStdNetBind = conn.NewStdNetBind
NewStdNetBindWithReceiverCreator = conn.NewStdNetBindWithReceiverCreator
SplitCoalescedMessages = conn.SplitCoalescedMessages
)
+24
View File
@@ -0,0 +1,24 @@
package wgbackend
import "github.com/netbirdio/wireguard-go/device"
type (
Device = device.Device
NoisePresharedKey = device.NoisePresharedKey
NoisePrivateKey = device.NoisePrivateKey
NoisePublicKey = device.NoisePublicKey
Peer = device.Peer
PeerConfig = device.PeerConfig
)
const (
LogLevelSilent = device.LogLevelSilent
LogLevelVerbose = device.LogLevelVerbose
)
var (
NewDevice = device.NewDevice
NewLogger = device.NewLogger
SetMaxBatchSizeOverride = device.SetMaxBatchSizeOverride
SetPreallocatedBuffersPerPool = device.SetPreallocatedBuffersPerPool
)
+5
View File
@@ -0,0 +1,5 @@
// Package wgbackend is the single place in the client's hand-written code that names
// the WireGuard implementation. Other packages import it instead of the wireguard-go
// packages, so swapping the implementation only touches the aliases here and the
// generated mocks.
package wgbackend
+10
View File
@@ -0,0 +1,10 @@
//go:build linux || darwin || freebsd || openbsd
package wgbackend
import "github.com/netbirdio/wireguard-go/ipc"
var (
UAPIListen = ipc.UAPIListen
UAPIOpen = ipc.UAPIOpen
)
+5
View File
@@ -0,0 +1,5 @@
package wgbackend
import "github.com/netbirdio/wireguard-go/ipc"
var UAPIListen = ipc.UAPIListen
+19
View File
@@ -0,0 +1,19 @@
package wgbackend
import (
"github.com/netbirdio/wireguard-go/tun"
"github.com/netbirdio/wireguard-go/tun/netstack"
)
type (
// TUN is the packet device the WireGuard device reads from and writes to.
TUN = tun.Device
// TUNEvent is a status change reported by a TUN.
TUNEvent = tun.Event
// NetstackNet is the userspace network stack a netstack TUN is attached to.
NetstackNet = netstack.Net
)
const TUNEventDown = tun.EventDown
var CreateNetTUN = netstack.CreateNetTUN
+5
View File
@@ -0,0 +1,5 @@
package wgbackend
import "github.com/netbirdio/wireguard-go/tun"
var CreateUnmonitoredTUNFromFD = tun.CreateUnmonitoredTUNFromFD
+10
View File
@@ -0,0 +1,10 @@
//go:build linux || darwin || freebsd || openbsd
package wgbackend
import "github.com/netbirdio/wireguard-go/tun"
var (
CreateTUN = tun.CreateTUN
CreateTUNFromFile = tun.CreateTUNFromFile
)
+7
View File
@@ -0,0 +1,7 @@
package wgbackend
import "github.com/netbirdio/wireguard-go/tun"
type NativeTUN = tun.NativeTun
var CreateTUNWithRequestedGUID = tun.CreateTUNWithRequestedGUID
+9
View File
@@ -198,6 +198,15 @@ FunctionEnd
######################################################################
Section -MainProgram
${INSTALL_TYPE}
; The previous uninstaller stops the service and the UI only when its uninstall key
; is found and endpoint protection lets the unsigned uninstaller run. Extraction
; fails on any exe that is still running.
DetailPrint "Stopping Netbird service..."
ExecWait '"$INSTDIR\${MAIN_APP_EXE}" service stop'
DetailPrint "Terminating ${UI_APP_NAME} process..."
ExecWait '"$SYSDIR\taskkill.exe" /im ${UI_APP_EXE}.exe /f'
; Both return before the processes release their image files.
Sleep 3000
# SetOverwrite ifnewer
SetOutPath "$INSTDIR"
!ifndef ARCH
+3 -3
View File
@@ -8,10 +8,10 @@ import (
reflect "reflect"
gomock "go.uber.org/mock/gomock"
wgdevice "golang.zx2c4.com/wireguard/device"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// MockIFaceMapper is a mock of IFaceMapper interface.
@@ -108,10 +108,10 @@ func (mr *MockIFaceMapperMockRecorder) GetDevice() *gomock.Call {
}
// GetWGDevice mocks base method.
func (m *MockIFaceMapper) GetWGDevice() *wgdevice.Device {
func (m *MockIFaceMapper) GetWGDevice() *wgbackend.Device {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetWGDevice")
ret0, _ := ret[0].(*wgdevice.Device)
ret0, _ := ret[0].(*wgbackend.Device)
return ret0
}
-7
View File
@@ -175,7 +175,6 @@ The IP addresses in the interfaces file are anonymized using the same process as
Configuration
The config.txt file contains anonymized configuration information of the NetBird client. Sensitive information such as private keys and SSH keys are excluded. The following fields are anonymized:
- ManagementURL
- AdminURL
- NATExternalIPs
- CustomDNSAddress
@@ -603,9 +602,6 @@ func (g *BundleGenerator) addConfig() error {
if g.internalConfig.ManagementURL != nil {
configContent.WriteString(fmt.Sprintf("ManagementURL: %s\n", g.anonymizer.AnonymizeURI(g.internalConfig.ManagementURL.String())))
}
if g.internalConfig.AdminURL != nil {
configContent.WriteString(fmt.Sprintf("AdminURL: %s\n", g.anonymizer.AnonymizeURI(g.internalConfig.AdminURL.String())))
}
configContent.WriteString(fmt.Sprintf("NATExternalIPs: %v\n", anonymizeNATExternalIPs(g.internalConfig.NATExternalIPs, g.anonymizer)))
if g.internalConfig.CustomDNSAddress != "" {
configContent.WriteString(fmt.Sprintf("CustomDNSAddress: %s\n", g.anonymizer.AnonymizeString(g.internalConfig.CustomDNSAddress)))
@@ -614,9 +610,6 @@ func (g *BundleGenerator) addConfig() error {
if g.internalConfig.ManagementURL != nil {
configContent.WriteString(fmt.Sprintf("ManagementURL: %s\n", g.internalConfig.ManagementURL.String()))
}
if g.internalConfig.AdminURL != nil {
configContent.WriteString(fmt.Sprintf("AdminURL: %s\n", g.internalConfig.AdminURL.String()))
}
configContent.WriteString(fmt.Sprintf("NATExternalIPs: %v\n", g.internalConfig.NATExternalIPs))
if g.internalConfig.CustomDNSAddress != "" {
configContent.WriteString(fmt.Sprintf("CustomDNSAddress: %s\n", g.internalConfig.CustomDNSAddress))
+1 -9
View File
@@ -856,14 +856,12 @@ func TestAddConfig_AllFieldsCovered(t *testing.T) {
}
mURL, _ := url.Parse("https://api.example.com:443")
aURL, _ := url.Parse("https://admin.example.com:443")
bTrue := true
iVal := 42
cfg := &profilemanager.Config{
PrivateKey: "priv",
PreSharedKey: "psk",
ManagementURL: mURL,
AdminURL: aURL,
WgIface: "wt0",
WgPort: 51820,
NetworkMonitor: &bTrue,
@@ -947,7 +945,7 @@ func TestAddConfig_AllFieldsCovered(t *testing.T) {
}
// renderAddConfigSpecific renders the fields handled by the anonymize/non-anonymize
// branches in addConfig (ManagementURL, AdminURL, NATExternalIPs, CustomDNSAddress).
// branches in addConfig (ManagementURL, NATExternalIPs, CustomDNSAddress).
// addCommonConfigFields covers the rest. Keeping this in the test mirrors the
// production shape without needing to write an actual zip.
func renderAddConfigSpecific(g *BundleGenerator) string {
@@ -956,9 +954,6 @@ func renderAddConfigSpecific(g *BundleGenerator) string {
if g.internalConfig.ManagementURL != nil {
sb.WriteString("ManagementURL: " + g.anonymizer.AnonymizeURI(g.internalConfig.ManagementURL.String()) + "\n")
}
if g.internalConfig.AdminURL != nil {
sb.WriteString("AdminURL: " + g.anonymizer.AnonymizeURI(g.internalConfig.AdminURL.String()) + "\n")
}
sb.WriteString("NATExternalIPs: x\n")
if g.internalConfig.CustomDNSAddress != "" {
sb.WriteString("CustomDNSAddress: " + g.anonymizer.AnonymizeString(g.internalConfig.CustomDNSAddress) + "\n")
@@ -967,9 +962,6 @@ func renderAddConfigSpecific(g *BundleGenerator) string {
if g.internalConfig.ManagementURL != nil {
sb.WriteString("ManagementURL: " + g.internalConfig.ManagementURL.String() + "\n")
}
if g.internalConfig.AdminURL != nil {
sb.WriteString("AdminURL: " + g.internalConfig.AdminURL.String() + "\n")
}
sb.WriteString("NATExternalIPs: x\n")
if g.internalConfig.CustomDNSAddress != "" {
sb.WriteString("CustomDNSAddress: " + g.internalConfig.CustomDNSAddress + "\n")
+64 -50
View File
@@ -35,11 +35,7 @@ func parseBackupResolvConf() (*resolvConf, error) {
}
func parseResolvConfFile(resolvConfFile string) (*resolvConf, error) {
rconf := &resolvConf{
searchDomains: make([]string, 0),
nameServers: make([]netip.Addr, 0),
others: make([]string, 0),
}
rconf := newResolvConf()
file, err := os.Open(resolvConfFile)
if err != nil {
@@ -56,56 +52,74 @@ func parseResolvConfFile(resolvConfFile string) (*resolvConf, error) {
return rconf, fmt.Errorf("failed to read %s file: %w", resolvConfFile, err)
}
return parseResolvConf(cur)
}
// parseResolvConf parses resolv.conf content. The returned config is never nil, so callers can
// fall back to its empty lists on error.
func parseResolvConf(cur []byte) (*resolvConf, error) {
rconf := newResolvConf()
if len(cur) == 0 {
return rconf, fmt.Errorf("file is empty")
}
for _, line := range strings.Split(string(cur), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "#") {
continue
}
if strings.HasPrefix(line, "domain") {
continue
}
if strings.HasPrefix(line, "options") && strings.Contains(line, "rotate") {
line = strings.ReplaceAll(line, "rotate", "")
splitLines := strings.Fields(line)
if len(splitLines) == 1 {
continue
}
line = strings.Join(splitLines, " ")
}
if strings.HasPrefix(line, "search") {
splitLines := strings.Fields(line)
if len(splitLines) < 2 {
continue
}
rconf.searchDomains = splitLines[1:]
continue
}
if strings.HasPrefix(line, "nameserver") {
splitLines := strings.Fields(line)
if len(splitLines) != 2 {
continue
}
if addr, err := netip.ParseAddr(splitLines[1]); err == nil {
rconf.nameServers = append(rconf.nameServers, addr.Unmap())
} else {
log.Warnf("invalid nameserver address in resolv.conf: %s, skipping", splitLines[1])
}
continue
}
if line != "" {
rconf.others = append(rconf.others, line)
}
parseResolvConfLine(rconf, strings.TrimSpace(line))
}
return rconf, nil
}
func parseResolvConfLine(rconf *resolvConf, line string) {
if strings.HasPrefix(line, "#") {
return
}
if strings.HasPrefix(line, "domain") {
return
}
if strings.HasPrefix(line, "options") && strings.Contains(line, "rotate") {
line = strings.ReplaceAll(line, "rotate", "")
splitLines := strings.Fields(line)
if len(splitLines) == 1 {
return
}
line = strings.Join(splitLines, " ")
}
if strings.HasPrefix(line, "search") {
splitLines := strings.Fields(line)
if len(splitLines) < 2 {
return
}
rconf.searchDomains = splitLines[1:]
return
}
if strings.HasPrefix(line, "nameserver") {
splitLines := strings.Fields(line)
if len(splitLines) != 2 {
return
}
if addr, err := netip.ParseAddr(splitLines[1]); err == nil {
rconf.nameServers = append(rconf.nameServers, addr.Unmap())
} else {
log.Warnf("invalid nameserver address in resolv.conf: %s, skipping", splitLines[1])
}
return
}
if line != "" {
rconf.others = append(rconf.others, line)
}
}
func newResolvConf() *resolvConf {
return &resolvConf{
searchDomains: make([]string, 0),
nameServers: make([]netip.Addr, 0),
others: make([]string, 0),
}
}
+69 -38
View File
@@ -3,7 +3,9 @@
package dns
import (
"bytes"
"net/netip"
"os"
"path"
"path/filepath"
"sync"
@@ -23,7 +25,8 @@ var (
}
)
type repairConfFn func([]string, netip.Addr, *resolvConf, *statemanager.Manager) error
// repairConfFn writes the NetBird-managed resolv.conf and returns the exact content it wrote.
type repairConfFn func([]string, netip.Addr, *resolvConf, *statemanager.Manager) ([]byte, error)
type repair struct {
operationFile string
@@ -43,7 +46,9 @@ func newRepair(operationFile string, updateFn repairConfFn) *repair {
}
}
func (f *repair) watchFileChanges(nbSearchDomains []string, nbNameserverIP netip.Addr, stateManager *statemanager.Manager) {
// watchFileChanges keeps resolv.conf pointing at NetBird. written is the content NetBird last
// wrote to the file, used to recognize the echo of its own writes.
func (f *repair) watchFileChanges(nbSearchDomains []string, nbNameserverIP netip.Addr, written []byte, stateManager *statemanager.Manager) {
if f.inotify != nil {
return
}
@@ -54,54 +59,80 @@ func (f *repair) watchFileChanges(nbSearchDomains []string, nbNameserverIP netip
log.Errorf("failed to start inotify watcher for resolv.conf: %s", err)
return
}
if err := inotify.Add(f.watchDir); err != nil {
log.Errorf("failed to add inotify watch for resolv.conf: %s", err)
if err := inotify.Close(); err != nil {
log.Debugf("failed to close resolv.conf inotify: %v", err)
}
return
}
f.inotify = inotify
f.inotifyWg.Add(1)
go func() {
defer f.inotifyWg.Done()
for event := range f.inotify.Events {
if !f.isEventRelevant(event) {
continue
}
log.Tracef("%s changed, check if it is broken", f.operationFile)
rConf, err := parseResolvConfFile(f.operationFile)
if err != nil {
log.Warnf("failed to parse resolv conf: %s", err)
continue
}
log.Debugf("check resolv.conf parameters: %s", rConf)
if !isNbParamsMissing(nbSearchDomains, nbNameserverIP, rConf) {
log.Tracef("resolv.conf still correct, skip the update")
continue
}
log.Info("broken params in resolv.conf, repairing it...")
err = f.inotify.Remove(f.watchDir)
if err != nil {
log.Errorf("failed to rm inotify watch for resolv.conf: %s", err)
}
err = f.updateFn(nbSearchDomains, nbNameserverIP, rConf, stateManager)
if err != nil {
log.Errorf("failed to repair resolv.conf: %v", err)
}
err = f.inotify.Add(f.watchDir)
if err != nil {
log.Errorf("failed to re-add inotify watch for resolv.conf: %s", err)
return
// A change that landed before the watch was armed produced no event.
lastWritten := f.checkAndRepair(nbSearchDomains, nbNameserverIP, written, stateManager)
for {
select {
case event, ok := <-inotify.Events:
if !ok {
return
}
if !f.isEventRelevant(event) {
continue
}
case err, ok := <-inotify.Errors:
if !ok {
return
}
// fsnotify stops delivering events until an error is received, and an
// error such as a queue overflow means events were dropped.
log.Warnf("resolv.conf inotify error, checking the file: %v", err)
}
lastWritten = f.checkAndRepair(nbSearchDomains, nbNameserverIP, lastWritten, stateManager)
}
}()
}
err = f.inotify.Add(f.watchDir)
// checkAndRepair rewrites resolv.conf when NetBird's parameters are missing from it and returns
// the content NetBird last wrote. The watch stays armed while repairing, so a third-party write
// racing the repair still raises an event; the echo of NetBird's own write is skipped by content.
func (f *repair) checkAndRepair(nbSearchDomains []string, nbNameserverIP netip.Addr, lastWritten []byte, stateManager *statemanager.Manager) []byte {
content, err := os.ReadFile(f.operationFile)
if err != nil {
log.Errorf("failed to add inotify watch for resolv.conf: %s", err)
return
log.Warnf("failed to read resolv.conf: %s", err)
return lastWritten
}
if bytes.Equal(content, lastWritten) {
log.Tracef("%s still holds the NetBird managed content, skip the check", f.operationFile)
return lastWritten
}
log.Tracef("%s changed, check if it is broken", f.operationFile)
rConf, err := parseResolvConf(content)
if err != nil {
log.Warnf("failed to parse resolv conf: %s", err)
return lastWritten
}
log.Debugf("check resolv.conf parameters: %s", rConf)
if !isNbParamsMissing(nbSearchDomains, nbNameserverIP, rConf) {
log.Tracef("resolv.conf still correct, skip the update")
return lastWritten
}
log.Info("broken params in resolv.conf, repairing it...")
written, err := f.updateFn(nbSearchDomains, nbNameserverIP, rConf, stateManager)
if err != nil {
log.Errorf("failed to repair resolv.conf: %v", err)
return lastWritten
}
return written
}
func (f *repair) stopWatchFileChanges() {
+159 -6
View File
@@ -4,12 +4,19 @@ package dns
import (
"context"
"fmt"
"net/netip"
"os"
"path/filepath"
"strings"
"sync/atomic"
"testing"
"time"
"github.com/fsnotify/fsnotify"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/client/internal/statemanager"
"github.com/netbirdio/netbird/util"
)
@@ -106,14 +113,14 @@ nameserver 8.8.8.8`,
var changed bool
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
updateFn := func([]string, netip.Addr, *resolvConf, *statemanager.Manager) error {
updateFn := func([]string, netip.Addr, *resolvConf, *statemanager.Manager) ([]byte, error) {
changed = true
cancel()
return nil
return nil, nil
}
r := newRepair(operationFile, updateFn)
r.watchFileChanges([]string{"netbird.cloud"}, netip.MustParseAddr("10.0.0.1"), nil)
r.watchFileChanges([]string{"netbird.cloud"}, netip.MustParseAddr("10.0.0.1"), nil, nil)
err = os.WriteFile(operationFile, []byte(tt.touchedConfContent), 0755)
if err != nil {
@@ -153,14 +160,14 @@ searchdomain netbird.cloud something`
var changed bool
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
updateFn := func([]string, netip.Addr, *resolvConf, *statemanager.Manager) error {
updateFn := func([]string, netip.Addr, *resolvConf, *statemanager.Manager) ([]byte, error) {
changed = true
cancel()
return nil
return nil, nil
}
r := newRepair(tmpLink, updateFn)
r.watchFileChanges([]string{"netbird.cloud"}, netip.MustParseAddr("10.0.0.1"), nil)
r.watchFileChanges([]string{"netbird.cloud"}, netip.MustParseAddr("10.0.0.1"), nil, nil)
err = os.WriteFile(tmpLink, []byte(modifyContent), 0755)
if err != nil {
@@ -175,3 +182,149 @@ searchdomain netbird.cloud something`
t.Errorf("unexpected result: want: %v, got: %v", true, false)
}
}
// systemd-resolved can rewrite resolv.conf twice in a row, e.g. on restart. When the second write
// lands right after NetBird's repair, the watcher must still see it and repair again; otherwise the
// host silently stays on the upstream resolver until the next unrelated change of the file.
func Test_repairCatchesWriteRacingRepair(t *testing.T) {
good := "nameserver 10.0.0.1\nsearch netbird.cloud\n"
upstream := "nameserver 8.8.8.8\nsearch .\n"
operationFile := filepath.Join(t.TempDir(), "resolv.conf")
require.NoError(t, os.WriteFile(operationFile, []byte(good), 0644))
var calls atomic.Int32
updateFn := func([]string, netip.Addr, *resolvConf, *statemanager.Manager) ([]byte, error) {
n := calls.Add(1)
if err := os.WriteFile(operationFile, []byte(good), 0644); err != nil {
return nil, err
}
if n == 1 {
// the third party's second write lands right after the repair
if err := writeFileAtomic(operationFile, upstream); err != nil {
return nil, err
}
}
return []byte(good), nil
}
r := newRepair(operationFile, updateFn)
r.watchFileChanges([]string{"netbird.cloud"}, netip.MustParseAddr("10.0.0.1"), []byte(good), nil)
t.Cleanup(r.stopWatchFileChanges)
require.NoError(t, writeFileAtomic(operationFile, upstream))
assert.Eventually(t, func() bool {
content, err := os.ReadFile(operationFile)
return err == nil && string(content) == good
}, 2*time.Second, 10*time.Millisecond, "resolv.conf should be repaired after the racing write")
assert.GreaterOrEqual(t, calls.Load(), int32(2), "the racing write should trigger a second repair")
}
// A change made between NetBird's write and the arming of the watch produces no event, so the
// watcher must check the file as soon as it starts.
func Test_repairChecksFileWhenArmed(t *testing.T) {
good := "nameserver 10.0.0.1\nsearch netbird.cloud\n"
operationFile := filepath.Join(t.TempDir(), "resolv.conf")
require.NoError(t, os.WriteFile(operationFile, []byte("nameserver 8.8.8.8\n"), 0644))
repaired := make(chan struct{}, 1)
updateFn := func([]string, netip.Addr, *resolvConf, *statemanager.Manager) ([]byte, error) {
select {
case repaired <- struct{}{}:
default:
}
return []byte(good), os.WriteFile(operationFile, []byte(good), 0644)
}
r := newRepair(operationFile, updateFn)
r.watchFileChanges([]string{"netbird.cloud"}, netip.MustParseAddr("10.0.0.1"), []byte(good), nil)
t.Cleanup(r.stopWatchFileChanges)
select {
case <-repaired:
case <-time.After(2 * time.Second):
t.Fatal("watcher should repair a file changed before it was armed")
}
}
// When NetBird has more search domains than resolv.conf can hold, its own write lacks some of them
// and fails the parameter check. The watcher must recognize that write as its own and not repair
// it again in a loop.
func Test_repairNoLoopOnTruncatedSearchDomains(t *testing.T) {
nbDomains := make([]string, 0, fileMaxNumberOfSearchDomains+2)
for i := range fileMaxNumberOfSearchDomains + 2 {
nbDomains = append(nbDomains, fmt.Sprintf("d%d.netbird.cloud", i))
}
truncated := fmt.Sprintf("nameserver 10.0.0.1\nsearch %s\n", strings.Join(nbDomains[:fileMaxNumberOfSearchDomains], " "))
operationFile := filepath.Join(t.TempDir(), "resolv.conf")
require.NoError(t, os.WriteFile(operationFile, []byte(truncated), 0644))
var calls atomic.Int32
updateFn := func([]string, netip.Addr, *resolvConf, *statemanager.Manager) ([]byte, error) {
calls.Add(1)
return []byte(truncated), os.WriteFile(operationFile, []byte(truncated), 0644)
}
r := newRepair(operationFile, updateFn)
r.watchFileChanges(nbDomains, netip.MustParseAddr("10.0.0.1"), []byte(truncated), nil)
t.Cleanup(r.stopWatchFileChanges)
require.NoError(t, writeFileAtomic(operationFile, "nameserver 8.8.8.8\n"))
require.Eventually(t, func() bool { return calls.Load() >= 1 }, 2*time.Second, 10*time.Millisecond,
"the third-party write should be repaired")
time.Sleep(500 * time.Millisecond)
assert.Equal(t, int32(1), calls.Load(), "NetBird's own truncated write should not trigger another repair")
}
// fsnotify blocks its reader until an error is received, and an error such as a queue overflow
// means events were dropped. The watcher must consume errors and check the file, so it keeps working
// and catches a change whose event was lost.
func Test_repairChecksFileOnWatcherError(t *testing.T) {
good := "nameserver 10.0.0.1\nsearch netbird.cloud\n"
operationFile := filepath.Join(t.TempDir(), "resolv.conf")
require.NoError(t, os.WriteFile(operationFile, []byte(good), 0644))
// A hard link in another directory changes the file without an event in the watched one,
// standing in for an event the kernel dropped.
alias := filepath.Join(t.TempDir(), "alias")
require.NoError(t, os.Link(operationFile, alias))
repaired := make(chan struct{}, 1)
updateFn := func([]string, netip.Addr, *resolvConf, *statemanager.Manager) ([]byte, error) {
select {
case repaired <- struct{}{}:
default:
}
return []byte(good), os.WriteFile(operationFile, []byte(good), 0644)
}
r := newRepair(operationFile, updateFn)
r.watchFileChanges([]string{"netbird.cloud"}, netip.MustParseAddr("10.0.0.1"), []byte(good), nil)
t.Cleanup(r.stopWatchFileChanges)
require.NoError(t, os.WriteFile(alias, []byte("nameserver 8.8.8.8\n"), 0644))
select {
case r.inotify.Errors <- fsnotify.ErrEventOverflow:
case <-time.After(2 * time.Second):
t.Fatal("watcher should consume inotify errors")
}
select {
case <-repaired:
case <-time.After(2 * time.Second):
t.Fatal("watcher should check the file after an inotify error")
}
}
// writeFileAtomic replaces the file the way systemd-resolved does: write a temporary file in the same
// directory, then rename it over the target.
func writeFileAtomic(target, content string) error {
tmp := target + ".tmp"
if err := os.WriteFile(tmp, []byte(content), 0644); err != nil {
return err
}
return os.Rename(tmp, target)
}
+5 -5
View File
@@ -61,11 +61,11 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig, stateManager *st
f.repair.stopWatchFileChanges()
err = f.updateConfig(nbSearchDomains, f.nbNameserverIP, resolvConf, stateManager)
written, err := f.updateConfig(nbSearchDomains, f.nbNameserverIP, resolvConf, stateManager)
if err != nil {
return err
}
f.repair.watchFileChanges(nbSearchDomains, f.nbNameserverIP, stateManager)
f.repair.watchFileChanges(nbSearchDomains, f.nbNameserverIP, written, stateManager)
return nil
}
@@ -74,7 +74,7 @@ func (f *fileConfigurator) getOriginalNameservers() []netip.Addr {
return f.originalNameservers
}
func (f *fileConfigurator) updateConfig(nbSearchDomains []string, nbNameserverIP netip.Addr, cfg *resolvConf, stateManager *statemanager.Manager) error {
func (f *fileConfigurator) updateConfig(nbSearchDomains []string, nbNameserverIP netip.Addr, cfg *resolvConf, stateManager *statemanager.Manager) ([]byte, error) {
searchDomainList := mergeSearchDomains(nbSearchDomains, cfg.searchDomains)
buf := prepareResolvConfContent(
@@ -90,7 +90,7 @@ func (f *fileConfigurator) updateConfig(nbSearchDomains []string, nbNameserverIP
if restoreErr != nil {
log.Errorf("attempt to restore default file failed with error: %s", err)
}
return fmt.Errorf("creating resolver file %s. Error: %w", defaultResolvConfPath, err)
return nil, fmt.Errorf("creating resolver file %s. Error: %w", defaultResolvConfPath, err)
}
log.Infof("created a NetBird managed %s file with the DNS settings. Added %d search domains. Search list: %s", defaultResolvConfPath, len(searchDomainList), searchDomainList)
@@ -100,7 +100,7 @@ func (f *fileConfigurator) updateConfig(nbSearchDomains []string, nbNameserverIP
log.Errorf("failed to create unclean shutdown resolv.conf backup: %s", err)
}
return nil
return buf.Bytes(), nil
}
func (f *fileConfigurator) restoreHostDNS() error {
+3 -2
View File
@@ -7,14 +7,15 @@ import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/miekg/dns"
"golang.zx2c4.com/wireguard/tun"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
type responseWriter struct {
local net.Addr
remote net.Addr
packet gopacket.Packet
device tun.Device
device wgbackend.TUN
}
// LocalAddr returns the net.Addr of the server
+2 -2
View File
@@ -15,7 +15,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/firewall/uspfilter"
@@ -23,6 +22,7 @@ import (
"github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/dns/local"
"github.com/netbirdio/netbird/client/internal/dns/test"
"github.com/netbirdio/netbird/client/internal/dns/types"
@@ -83,7 +83,7 @@ func (w *mocWGIface) GetStats(_ string) (configurer.WGStats, error) {
return configurer.WGStats{}, nil
}
func (w *mocWGIface) GetNet() *netstack.Net {
func (w *mocWGIface) GetNet() *wgbackend.NetstackNet {
return nil
}
+2 -2
View File
@@ -108,7 +108,7 @@ func (s *ServiceViaMemory) filterDNSTraffic() error {
if s.tcpDNS == nil {
if dev := s.wgInterface.GetDevice(); dev != nil {
// MTU only affects TCP segment sizing; DNS messages are small so this has no practical impact.
s.tcpDNS = newTCPDNSServer(s.dnsMux, dev.Device, s.runtimeIP, uint16(s.runtimePort), iface.DefaultMTU)
s.tcpDNS = newTCPDNSServer(s.dnsMux, dev.TUN, s.runtimeIP, uint16(s.runtimePort), iface.DefaultMTU)
}
}
@@ -143,7 +143,7 @@ func (s *ServiceViaMemory) filterDNSTraffic() error {
writer := &responseWriter{
remote: remoteAddrFromPacket(packet),
packet: packet,
device: dev.Device,
device: dev.TUN,
}
go s.dnsMux.ServeDNS(writer, msg)
return true
+5 -4
View File
@@ -12,7 +12,6 @@ import (
"github.com/miekg/dns"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun"
"gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
@@ -21,6 +20,8 @@ import (
"gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/waiter"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
const (
@@ -38,7 +39,7 @@ type tcpDNSServer struct {
s *stack.Stack
ep *dnsEndpoint
mux *dns.ServeMux
tunDev tun.Device
tunDev wgbackend.TUN
ip netip.Addr
port uint16
mtu uint16
@@ -49,7 +50,7 @@ type tcpDNSServer struct {
timer *time.Timer
}
func newTCPDNSServer(mux *dns.ServeMux, tunDev tun.Device, ip netip.Addr, port uint16, mtu uint16) *tcpDNSServer {
func newTCPDNSServer(mux *dns.ServeMux, tunDev wgbackend.TUN, ip netip.Addr, port uint16, mtu uint16) *tcpDNSServer {
return &tcpDNSServer{
mux: mux,
tunDev: tunDev,
@@ -270,7 +271,7 @@ func (t *tcpDNSServer) handleTCPDNS(r *tcp.ForwarderRequest) {
// dnsEndpoint implements stack.LinkEndpoint for writing packets back via the tun device.
type dnsEndpoint struct {
dispatcher stack.NetworkDispatcher
tunDev tun.Device
tunDev wgbackend.TUN
mtu atomic.Uint32
}
+3 -3
View File
@@ -44,10 +44,10 @@ import (
"github.com/miekg/dns"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/dns/resutil"
"github.com/netbirdio/netbird/client/internal/dns/types"
"github.com/netbirdio/netbird/client/internal/peer"
@@ -714,7 +714,7 @@ func toTCPClient(c *dns.Client) *dns.Client {
// ExchangeWithNetstack performs a DNS exchange using netstack for dialing.
// This is needed when netstack is enabled to reach peer IPs through the tunnel.
func ExchangeWithNetstack(ctx context.Context, nsNet *netstack.Net, r *dns.Msg, upstream string) (*dns.Msg, error) {
func ExchangeWithNetstack(ctx context.Context, nsNet *wgbackend.NetstackNet, r *dns.Msg, upstream string) (*dns.Msg, error) {
// If request came in over TCP, go straight to TCP upstream
if dnsProtocolFromContext(ctx) == protoTCP {
rm, err := netstackExchange(ctx, nsNet, r, upstream, protoTCP)
@@ -758,7 +758,7 @@ func ExchangeWithNetstack(ctx context.Context, nsNet *netstack.Net, r *dns.Msg,
return reply, nil
}
func netstackExchange(ctx context.Context, nsNet *netstack.Net, r *dns.Msg, upstream, network string) (*dns.Msg, error) {
func netstackExchange(ctx context.Context, nsNet *wgbackend.NetstackNet, r *dns.Msg, upstream, network string) (*dns.Msg, error) {
conn, err := nsNet.DialContext(ctx, network, upstream)
if err != nil {
return nil, fmt.Errorf("with %s: %w", network, err)
+2 -2
View File
@@ -9,15 +9,15 @@ import (
"time"
"github.com/miekg/dns"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/shared/management/domain"
)
type upstreamResolver struct {
*upstreamResolverBase
nsNet *netstack.Net
nsNet *wgbackend.NetstackNet
}
func newUpstreamResolver(
+2 -2
View File
@@ -13,10 +13,10 @@ import (
"github.com/miekg/dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/dns/test"
)
@@ -128,7 +128,7 @@ func (m *mockNetstackProvider) ToInterface() *net.Interface { return nil }
func (m *mockNetstackProvider) IsUserspaceBind() bool { return false }
func (m *mockNetstackProvider) GetFilter() device.PacketFilter { return nil }
func (m *mockNetstackProvider) GetDevice() *device.FilteredDevice { return nil }
func (m *mockNetstackProvider) GetNet() *netstack.Net { return nil }
func (m *mockNetstackProvider) GetNet() *wgbackend.NetstackNet { return nil }
func (m *mockNetstackProvider) GetInterfaceGUIDString() (string, error) {
return "", nil
}
+2 -3
View File
@@ -5,10 +5,9 @@ package dns
import (
"net"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// WGIface defines subset methods of interface required for manager
@@ -19,5 +18,5 @@ type WGIface interface {
IsUserspaceBind() bool
GetFilter() device.PacketFilter
GetDevice() *device.FilteredDevice
GetNet() *netstack.Net
GetNet() *wgbackend.NetstackNet
}
+2 -3
View File
@@ -1,10 +1,9 @@
package dns
import (
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
// WGIface defines subset methods of interface required for manager
@@ -14,6 +13,6 @@ type WGIface interface {
IsUserspaceBind() bool
GetFilter() device.PacketFilter
GetDevice() *device.FilteredDevice
GetNet() *netstack.Net
GetNet() *wgbackend.NetstackNet
GetInterfaceGUIDString() (string, error)
}
+4 -4
View File
@@ -14,10 +14,10 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/miekg/dns"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun/netstack"
nberrors "github.com/netbirdio/netbird/client/errors"
firewall "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/dns/resutil"
"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/route"
@@ -90,7 +90,7 @@ func NewDNSForwarder(listenAddress netip.AddrPort, ttl uint32, firewall firewall
}
func (f *DNSForwarder) Listen(entries []*ForwarderEntry) error {
var netstackNet *netstack.Net
var netstackNet *wgbackend.NetstackNet
if f.wgIface != nil {
netstackNet = f.wgIface.GetNet()
}
@@ -153,7 +153,7 @@ func (f *DNSForwarder) Listen(entries []*ForwarderEntry) error {
return <-errCh
}
func (f *DNSForwarder) createUDPListener(netstackNet *netstack.Net) (net.PacketConn, error) {
func (f *DNSForwarder) createUDPListener(netstackNet *wgbackend.NetstackNet) (net.PacketConn, error) {
if netstackNet != nil {
return netstackNet.ListenUDPAddrPort(f.listenAddress)
}
@@ -161,7 +161,7 @@ func (f *DNSForwarder) createUDPListener(netstackNet *netstack.Net) (net.PacketC
return net.ListenUDP("udp", net.UDPAddrFromAddrPort(f.listenAddress))
}
func (f *DNSForwarder) createTCPListener(netstackNet *netstack.Net) (net.Listener, error) {
func (f *DNSForwarder) createTCPListener(netstackNet *wgbackend.NetstackNet) (net.Listener, error) {
if netstackNet != nil {
return netstackNet.ListenTCPAddrPort(f.listenAddress)
}
+2 -2
View File
@@ -9,11 +9,11 @@ import (
"github.com/hashicorp/go-multierror"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun/netstack"
nberrors "github.com/netbirdio/netbird/client/errors"
firewall "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
"github.com/netbirdio/netbird/client/internal/peer"
nbdns "github.com/netbirdio/netbird/dns"
@@ -28,7 +28,7 @@ const (
// wgIface defines the interface for WireGuard interface operations needed by the DNS forwarder.
type wgIface interface {
GetNet() *netstack.Net
GetNet() *wgbackend.NetstackNet
Address() wgaddr.Address
}
+4 -5
View File
@@ -19,11 +19,9 @@ import (
"time"
"github.com/hashicorp/go-multierror"
"github.com/pion/ice/v4"
"github.com/netbirdio/ice/v4"
"github.com/pion/stun/v3"
log "github.com/sirupsen/logrus"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/anonymize"
@@ -37,6 +35,7 @@ import (
nbnetstack "github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/acl"
"github.com/netbirdio/netbird/client/internal/certproof"
"github.com/netbirdio/netbird/client/internal/debug"
@@ -254,7 +253,7 @@ type Engine struct {
// wgInterface. Reaching the device through wgInterface requires
// syncMsgMux, which handleSync holds while it adds and removes peers;
// SetPerformance must stay reachable exactly when that work is stuck.
wgDevice atomic.Pointer[wgdevice.Device]
wgDevice atomic.Pointer[wgbackend.Device]
udpMux *udpmux.UniversalUDPMuxDefault
@@ -2788,7 +2787,7 @@ func (e *Engine) stopDNSForwarder() {
e.dnsForwardMgr = nil
}
func (e *Engine) GetNet() (*netstack.Net, error) {
func (e *Engine) GetNet() (*wgbackend.NetstackNet, error) {
e.syncMsgMux.Lock()
intf := e.wgInterface
e.syncMsgMux.Unlock()
+5 -6
View File
@@ -14,10 +14,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/stdnet"
"github.com/netbirdio/netbird/client/iface"
@@ -61,11 +60,11 @@ type MockWGIface struct {
SetFilterFunc func(filter device.PacketFilter) error
GetFilterFunc func() device.PacketFilter
GetDeviceFunc func() *device.FilteredDevice
GetWGDeviceFunc func() *wgdevice.Device
GetWGDeviceFunc func() *wgbackend.Device
GetStatsFunc func() (map[string]configurer.WGStats, error)
GetInterfaceGUIDStringFunc func() (string, error)
GetProxyFunc func() wgproxy.Proxy
GetNetFunc func() *netstack.Net
GetNetFunc func() *wgbackend.NetstackNet
LastActivitiesFunc func() map[string]monotime.Time
}
@@ -149,7 +148,7 @@ func (m *MockWGIface) GetDevice() *device.FilteredDevice {
return m.GetDeviceFunc()
}
func (m *MockWGIface) GetWGDevice() *wgdevice.Device {
func (m *MockWGIface) GetWGDevice() *wgbackend.Device {
return m.GetWGDeviceFunc()
}
@@ -161,7 +160,7 @@ func (m *MockWGIface) GetProxy() wgproxy.Proxy {
return m.GetProxyFunc()
}
func (m *MockWGIface) GetNet() *netstack.Net {
func (m *MockWGIface) GetNet() *wgbackend.NetstackNet {
return m.GetNetFunc()
}
+3 -4
View File
@@ -5,14 +5,13 @@ import (
"net/netip"
"time"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/iface/wgproxy"
"github.com/netbirdio/netbird/monotime"
)
@@ -37,9 +36,9 @@ type wgIfaceBase interface {
SetFilter(filter device.PacketFilter) error
GetFilter() device.PacketFilter
GetDevice() *device.FilteredDevice
GetWGDevice() *wgdevice.Device
GetWGDevice() *wgbackend.Device
GetStats() (map[string]configurer.WGStats, error)
GetNet() *netstack.Net
GetNet() *wgbackend.NetstackNet
FullStats() (*configurer.Stats, error)
LastActivities() map[string]monotime.Time
SetPresharedKey(peerKey string, psk wgtypes.Key, updateOnly bool) error
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"sync"
"time"
"github.com/pion/ice/v4"
"github.com/netbirdio/ice/v4"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"sync"
"time"
"github.com/pion/ice/v4"
"github.com/netbirdio/ice/v4"
log "github.com/sirupsen/logrus"
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"sync"
"time"
"github.com/pion/ice/v4"
"github.com/netbirdio/ice/v4"
"github.com/pion/logging"
"github.com/pion/randutil"
log "github.com/sirupsen/logrus"
+1 -1
View File
@@ -1,7 +1,7 @@
package ice
import (
"github.com/pion/ice/v4"
"github.com/netbirdio/ice/v4"
"github.com/netbirdio/netbird/client/internal/stdnet"
)
+1 -1
View File
@@ -1,7 +1,7 @@
package peer
import (
"github.com/pion/ice/v4"
"github.com/netbirdio/ice/v4"
log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"sync"
"time"
"github.com/pion/ice/v4"
"github.com/netbirdio/ice/v4"
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/iface"
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"testing"
"time"
"github.com/pion/ice/v4"
"github.com/netbirdio/ice/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
+3 -47
View File
@@ -36,7 +36,8 @@ const (
DefaultManagementURL = "https://api.netbird.io:443"
// oldDefaultManagementURL points to the NetBird's old cloud management endpoint
oldDefaultManagementURL = "https://api.wiretrustee.com:443"
// DefaultAdminURL points to NetBird's cloud management console
// DefaultAdminURL points to NetBird's cloud management console. It is only
// reported by the deprecated mobile admin URL getters.
DefaultAdminURL = "https://app.netbird.io:443"
)
@@ -60,7 +61,6 @@ var DefaultInterfaceBlacklist = []string{
// ConfigInput carries configuration changes to the client
type ConfigInput struct {
ManagementURL string
AdminURL string
ConfigPath string
StateFilePath string
PreSharedKey *string
@@ -114,7 +114,6 @@ type Config struct {
PrivateKey string
PreSharedKey string
ManagementURL *url.URL
AdminURL *url.URL
WgIface string
WgPort int
NetworkMonitor *bool
@@ -508,29 +507,6 @@ func (config *Config) apply(input ConfigInput) (updated bool, err error) {
}
}
if config.AdminURL == nil {
log.Infof("using default Admin URL %s", DefaultAdminURL)
config.AdminURL, err = parseURL("Admin URL", DefaultAdminURL)
if err != nil {
return false, err
}
}
// The admin panel is opened, not dialed, so unlike the Management URL its
// path is part of what identifies it: a panel served under /netbird is not
// the one served at the root.
if input.AdminURL != "" {
newURL, err := parseURL("Admin Panel URL", input.AdminURL)
if err != nil {
return updated, err
}
if !SameServiceURLIncludingPath(newURL, config.AdminURL) {
log.Infof("new Admin Panel URL provided, updated to %#v (old value %#v)",
newURL.String(), config.AdminURL.String())
config.AdminURL = newURL
updated = true
}
}
if input.WireguardPort != nil && *input.WireguardPort != config.WgPort {
log.Infof("updating Wireguard port %d (old value %d)",
*input.WireguardPort, config.WgPort)
@@ -1027,8 +1003,7 @@ func ParseServiceURL(serviceName, serviceURL string) (*url.URL, error) {
// matches only another nil one.
//
// The path plays no part: a management URL is dialed, and only its host and
// port are. util.SameServiceURL is this comparison plus the path, which is
// what SameServiceURLIncludingPath needs and delegates to.
// port are. util.SameServiceURL is this comparison plus the path.
func SameServiceURL(a, b *url.URL) bool {
if a == nil || b == nil {
return a == b
@@ -1039,25 +1014,6 @@ func SameServiceURL(a, b *url.URL) bool {
util.ServiceURLPort(a) == util.ServiceURLPort(b)
}
// SameServiceURLIncludingPath is SameServiceURL plus everything a URL carries
// past its endpoint: path, query, fragment and userinfo.
//
// Use it for a URL that gets opened rather than dialed. The admin panel can
// live under a path, so two URLs with the same endpoint and different paths are
// two different panels — where for a URL the client dials over gRPC only the
// endpoint is ever used. Equivalent spellings still compare equal: a missing
// path and "/" are the same root, and so is a trailing slash on any path.
func SameServiceURLIncludingPath(a, b *url.URL) bool {
if a == nil || b == nil {
return a == b
}
return util.SameServiceURL(a, b) &&
a.RawQuery == b.RawQuery &&
a.Fragment == b.Fragment &&
a.User.String() == b.User.String()
}
func parseURL(serviceName, serviceURL string) (*url.URL, error) {
parsedMgmtURL, err := url.ParseRequestURI(serviceURL)
if err != nil {
@@ -36,17 +36,14 @@ func TestGetConfig(t *testing.T) {
}
assert.Equal(t, config.ManagementURL.String(), DefaultManagementURL)
assert.Equal(t, config.AdminURL.String(), DefaultAdminURL)
managementURL := "https://test.management.url:33071"
adminURL := "https://app.admin.url:443"
path := filepath.Join(t.TempDir(), "config.json")
preSharedKey := "preSharedKey"
// case 2: new config has to be generated
config, err = UpdateOrCreateConfig(ConfigInput{
ManagementURL: managementURL,
AdminURL: adminURL,
ConfigPath: path,
PreSharedKey: &preSharedKey,
})
@@ -64,7 +61,6 @@ func TestGetConfig(t *testing.T) {
// case 3: existing config -> fetch it
config, err = UpdateOrCreateConfig(ConfigInput{
ManagementURL: managementURL,
AdminURL: adminURL,
ConfigPath: path,
PreSharedKey: &preSharedKey,
})
@@ -79,7 +75,6 @@ func TestGetConfig(t *testing.T) {
newManagementURL := "https://test.newManagement.url:33071"
config, err = UpdateOrCreateConfig(ConfigInput{
ManagementURL: newManagementURL,
AdminURL: adminURL,
ConfigPath: path,
PreSharedKey: &preSharedKey,
})
@@ -164,7 +159,6 @@ func TestNewProfileDefaults(t *testing.T) {
require.NoError(t, err, "should create new config")
assert.Equal(t, DefaultManagementURL, config.ManagementURL.String(), "ManagementURL should have default")
assert.Equal(t, DefaultAdminURL, config.AdminURL.String(), "AdminURL should have default")
assert.NotEmpty(t, config.PrivateKey, "PrivateKey should be generated")
assert.NotEmpty(t, config.SSHKey, "SSHKey should be generated")
assert.Equal(t, iface.WgInterfaceDefault, config.WgIface, "WgIface should have default")
@@ -334,40 +334,34 @@ func TestCreateInMemoryConfigCarriesAnIdentity(t *testing.T) {
require.NotEmpty(t, cfg.SSHKey)
}
// The admin panel is opened, not dialed, so its path identifies it. Comparing
// it as a bare endpoint left a custom panel URL unable to change.
func TestAdminURLPathIsPartOfTheIdentity(t *testing.T) {
path := filepath.Join(t.TempDir(), "panel.json")
_, err := UpdateOrCreateConfig(ConfigInput{
ConfigPath: path,
AdminURL: "https://app.example.com/netbird",
})
// Profiles written before the admin URL was dropped still carry an AdminURL key.
// It must not break loading, and the next write must drop it.
func TestLegacyAdminURLIsIgnored(t *testing.T) {
path := filepath.Join(t.TempDir(), "legacy.json")
_, err := UpdateOrCreateConfig(ConfigInput{ConfigPath: path})
require.NoError(t, err)
cfg, err := GetExistingConfig(path)
raw, err := os.ReadFile(path)
require.NoError(t, err)
require.Equal(t, "https://app.example.com:443/netbird", cfg.AdminURL.String())
// Equivalent spellings of the same panel are still not a change.
for _, same := range []string{
"https://app.example.com/netbird",
"https://app.example.com:443/netbird",
"https://app.example.com/netbird/",
"https://APP.example.com/netbird",
} {
changed, err := cfg.WouldChange(ConfigInput{AdminURL: same})
require.NoError(t, err)
require.False(t, changed, "%q is the stored panel written differently", same)
}
// A different path is a different panel, and it must be persisted.
changed, err := cfg.WouldChange(ConfigInput{AdminURL: "https://app.example.com/other"})
var stored map[string]json.RawMessage
require.NoError(t, json.Unmarshal(raw, &stored))
stored["AdminURL"] = json.RawMessage(`{"Scheme":"https","Host":"panel.example.com:443","Path":"/netbird"}`)
rewritten, err := json.Marshal(stored)
require.NoError(t, err)
require.True(t, changed, "a different panel path is a change")
require.NoError(t, os.WriteFile(path, rewritten, 0600))
updated, err := UpdateConfig(ConfigInput{ConfigPath: path, AdminURL: "https://app.example.com/other"})
_, err = GetExistingConfig(path)
require.NoError(t, err, "a legacy AdminURL key must not prevent loading the profile")
port := 51999
_, err = UpdateConfig(ConfigInput{ConfigPath: path, WireguardPort: &port})
require.NoError(t, err)
require.Equal(t, "https://app.example.com:443/other", updated.AdminURL.String(), "the new panel path was not persisted")
raw, err = os.ReadFile(path)
require.NoError(t, err)
stored = nil
require.NoError(t, json.Unmarshal(raw, &stored))
require.NotContains(t, stored, "AdminURL", "the legacy AdminURL key should be dropped on write")
}
// unsetOnDisk rewrites the stored config so the named fields carry a JSON null,
@@ -4,10 +4,9 @@ import (
"net"
"net/netip"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
)
type wgIfaceBase interface {
@@ -20,5 +19,5 @@ type wgIfaceBase interface {
IsUserspaceBind() bool
GetFilter() device.PacketFilter
GetDevice() *device.FilteredDevice
GetNet() *netstack.Net
GetNet() *wgbackend.NetstackNet
}
@@ -10,10 +10,10 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/routemanager/refcounter"
)
@@ -48,7 +48,7 @@ func (m *reconcileWGMock) ToInterface() *net.Interface { return n
func (m *reconcileWGMock) IsUserspaceBind() bool { return false }
func (m *reconcileWGMock) GetFilter() device.PacketFilter { return nil }
func (m *reconcileWGMock) GetDevice() *device.FilteredDevice { return nil }
func (m *reconcileWGMock) GetNet() *netstack.Net { return nil }
func (m *reconcileWGMock) GetNet() *wgbackend.NetstackNet { return nil }
// TestReconcilePeerAllowedIPs verifies the declarative reconcile re-applies every routed prefix
// tracked for the peer (self-heal, independent of refcount level) and stays scoped to that peer.
+8 -14
View File
@@ -61,23 +61,17 @@ func (p *Preferences) SetManagementURL(url string) {
p.configInput.ManagementURL = url
}
// GetAdminURL read url from config file
// GetAdminURL returns the default admin panel URL.
//
// Deprecated: the admin URL is no longer stored or used by the client.
func (p *Preferences) GetAdminURL() (string, error) {
if p.configInput.AdminURL != "" {
return p.configInput.AdminURL, nil
}
cfg, err := profilemanager.ReadConfigOrDefault(p.configInput.ConfigPath)
if err != nil {
return "", err
}
return cfg.AdminURL.String(), err
return profilemanager.DefaultAdminURL, nil
}
// SetAdminURL store the given url and wait for commit
func (p *Preferences) SetAdminURL(url string) {
p.configInput.AdminURL = url
}
// SetAdminURL is a no-op kept for API compatibility.
//
// Deprecated: the admin URL is no longer stored or used by the client.
func (p *Preferences) SetAdminURL(_ string) {}
// HasPreSharedKey reports whether a pre-shared key is staged, persisted, or
// enforced by MDM; the key itself is never handed to the native layer.
+3 -2
View File
@@ -47,13 +47,14 @@ func TestPreferences_ReadUncommitedValues(t *testing.T) {
stateFile := filepath.Join(t.TempDir(), "state.json")
p := NewPreferences(cfgFile, stateFile)
// The admin URL is deprecated: setting it is a no-op.
p.SetAdminURL(exampleString)
resp, err := p.GetAdminURL()
if err != nil {
t.Fatalf("failed to read admin url: %s", err)
}
if resp != exampleString {
if resp != profilemanager.DefaultAdminURL {
t.Errorf("unexpected admin url: %s", resp)
}
@@ -100,7 +101,7 @@ func TestPreferences_Commit(t *testing.T) {
t.Fatalf("failed to read admin url: %s", err)
}
if resp != exampleURL {
if resp != profilemanager.DefaultAdminURL {
t.Errorf("unexpected admin url: %s", resp)
}
-5
View File
@@ -595,9 +595,6 @@ func (s *Server) setConfigInputFromRequest(msg *proto.SetConfigRequest) (profile
if msg.ManagementUrl != "" {
config.ManagementURL = msg.ManagementUrl
}
if msg.AdminURL != "" {
config.AdminURL = msg.AdminURL
}
if msg.InterfaceName != nil {
config.InterfaceName = msg.InterfaceName
}
@@ -2413,7 +2410,6 @@ func (s *Server) GetConfig(ctx context.Context, req *proto.GetConfigRequest) (*p
// overlay (Loader lives outside profilemanager).
cfg.ApplyMDMPolicy(s.mdmLoader.Load())
managementURL := cfg.ManagementURL
adminURL := cfg.AdminURL
var preSharedKey = cfg.PreSharedKey
if preSharedKey != "" {
@@ -2469,7 +2465,6 @@ func (s *Server) GetConfig(ctx context.Context, req *proto.GetConfigRequest) (*p
return &proto.GetConfigResponse{
ManagementUrl: managementURL.String(),
PreSharedKey: preSharedKey,
AdminURL: adminURL.String(),
InterfaceName: cfg.WgIface,
WireguardPort: int64(cfg.WgPort),
Mtu: int64(cfg.MTU),
+7 -3
View File
@@ -1,6 +1,7 @@
package server
import (
"os"
"os/user"
"path/filepath"
"reflect"
@@ -129,7 +130,10 @@ func TestSetConfig_AllFieldsSaved(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "https://new-api.netbird.io:443", cfg.ManagementURL.String())
require.Equal(t, "https://new-admin.netbird.io:443", cfg.AdminURL.String())
// Older clients still send the deprecated admin URL; it must be ignored.
raw, err := os.ReadFile(cfgPath)
require.NoError(t, err)
require.NotContains(t, string(raw), "new-admin.netbird.io", "deprecated AdminURL must not be persisted")
require.Equal(t, rosenpassEnabled, cfg.RosenpassEnabled)
require.Equal(t, rosenpassPermissive, cfg.RosenpassPermissive)
require.NotNil(t, cfg.ServerSSHAllowed)
@@ -182,11 +186,11 @@ func verifyAllFieldsCovered(t *testing.T, req *proto.SetConfigRequest) {
"CleanNATExternalIPs": true, // control flag for clearing
"CleanDNSLabels": true, // control flag for clearing
"LazyConnectionEnabled": true, // deprecated: proto field retained for compat, no longer applied
"AdminURL": true, // deprecated: proto field retained for compat, no longer applied
}
expectedFields := map[string]bool{
"ManagementUrl": true,
"AdminURL": true,
"RosenpassEnabled": true,
"RosenpassPermissive": true,
"ServerSSHAllowed": true,
@@ -249,7 +253,6 @@ func TestCLIFlags_MappedToSetConfig(t *testing.T) {
// This map must be updated when adding new config-related CLI flags.
flagToField := map[string]string{
"management-url": "ManagementUrl",
"admin-url": "AdminURL",
"enable-rosenpass": "RosenpassEnabled",
"rosenpass-permissive": "RosenpassPermissive",
"allow-server-ssh": "ServerSSHAllowed",
@@ -286,6 +289,7 @@ func TestCLIFlags_MappedToSetConfig(t *testing.T) {
fieldsWithoutCLIFlags := map[string]bool{
"DisableNotifications": true, // Only settable via UI
"LazyConnectionEnabled": true, // deprecated: no longer settable (managed by server + NB_LAZY_CONN)
"AdminURL": true, // deprecated: the admin-url flag is no longer functional
}
// Get all SetConfigRequest fields to verify our map is complete.
+4 -2
View File
@@ -166,7 +166,8 @@ func (c *Client) ExecuteCommandWithIO(ctx context.Context, command string) error
case <-ctx.Done():
_ = session.Signal(ssh.SIGTERM)
select {
case <-done:
case err := <-done:
log.Debugf("session ended after SIGTERM: %v", err)
return ctx.Err()
case <-time.After(100 * time.Millisecond):
return ctx.Err()
@@ -205,7 +206,8 @@ func (c *Client) ExecuteCommandWithPTY(ctx context.Context, command string) erro
case <-ctx.Done():
_ = session.Signal(ssh.SIGTERM)
select {
case <-done:
case err := <-done:
log.Debugf("session ended after SIGTERM: %v", err)
return ctx.Err()
case <-time.After(100 * time.Millisecond):
return ctx.Err()
+3 -3
View File
@@ -20,9 +20,9 @@ import (
log "github.com/sirupsen/logrus"
cryptossh "golang.org/x/crypto/ssh"
"golang.org/x/exp/maps"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
sshauth "github.com/netbirdio/netbird/client/ssh/auth"
"github.com/netbirdio/netbird/client/ssh/detection"
"github.com/netbirdio/netbird/shared/auth"
@@ -167,7 +167,7 @@ type Server struct {
allowSFTP bool
jwtEnabled bool
netstackNet *netstack.Net
netstackNet *wgbackend.NetstackNet
wgAddress wgaddr.Address
@@ -445,7 +445,7 @@ func (s *Server) buildSessionInfo(state *sessionState) SessionInfo {
}
// SetNetstackNet sets the netstack network for userspace networking
func (s *Server) SetNetstackNet(net *netstack.Net) {
func (s *Server) SetNetstackNet(net *wgbackend.NetstackNet) {
s.mu.Lock()
defer s.mu.Unlock()
s.netstackNet = net
+1
View File
@@ -8,5 +8,6 @@
"arrowParens": "always",
"endOfLine": "lf",
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindStylesheet": "./src/globals.css",
"tailwindFunctions": ["cn", "clsx", "cva", "tw"]
}
+3 -3
View File
@@ -110,7 +110,7 @@ Settings.SetConfig(p: SetConfigParams): Promise<void> // partial update
Settings.GetFeatures(): Promise<Features> // operator-disabled UI sections
```
`SetConfig` is a partial update: only fields you set are pushed to the daemon. `profileName` + `username` are always required; the typed fields in `SetConfigParams` are optional (`field?: T | null`). `managementUrl` and `adminUrl` are always-string for historical reasons.
`SetConfig` is a partial update: only fields you set are pushed to the daemon. `profileName` + `username` are always required; the typed fields in `SetConfigParams` are optional (`field?: T | null`). `managementUrl` is always-string for historical reasons.
**PSK mask quirk:** `GetConfig` returns existing pre-shared keys as `"**********"`. If you send the mask back, `wgtypes.ParseKey` fails on the next connect. `SettingsContext.save` drops the field when it equals `"**********"`. See `modules/settings/SettingsContext.tsx`.
@@ -242,7 +242,7 @@ The tray also reads a tray-only synthetic `"Error"` for icon purposes; the front
`Config` (read-only mirror, all required):
```ts
{ managementUrl, adminUrl, configFile, logFile, preSharedKey, interfaceName: string;
{ managementUrl, configFile, logFile, preSharedKey, interfaceName: string;
wireguardPort, mtu, sshJwtCacheTtl: number;
disableAutoConnect, serverSshAllowed,
rosenpassEnabled, rosenpassPermissive,
@@ -254,7 +254,7 @@ The tray also reads a tray-only synthetic `"Error"` for icon purposes; the front
disableSshAuth: boolean; }
```
`SetConfigParams` has all `Config` fields as `field?: T | null` (partial update), plus the write-only `disableFirewall?: boolean | null`, plus `profileName` / `username` / `managementUrl` / `adminUrl` as required strings.
`SetConfigParams` has all `Config` fields as `field?: T | null` (partial update), plus the write-only `disableFirewall?: boolean | null`, plus `profileName` / `username` / `managementUrl` as required strings.
`Features`: `{ disableProfiles, disableUpdateSettings, disableNetworks: boolean }`.

Some files were not shown because too many files have changed in this diff Show More