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 while IFS= read -r dir; do
echo "=== Checking $dir ===" echo "=== Checking $dir ==="
# Search for problematic imports, excluding test files # 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 if [ -n "$RESULTS" ]; then
echo "❌ Found problematic dependencies:" echo "❌ Found problematic dependencies:"
echo "$RESULTS" echo "$RESULTS"
@@ -93,7 +93,7 @@ jobs:
IMPORTERS=$(go list -json -deps ./... 2>/dev/null | jq -r "select(.Imports[]? == \"$package\") | .ImportPath") IMPORTERS=$(go list -json -deps ./... 2>/dev/null | jq -r "select(.Imports[]? == \"$package\") | .ImportPath")
# Check if any importer is NOT in management/signal/relay # 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 if [ -n "$BSD_IMPORTER" ]; then
echo "❌ $package ($license) is imported by BSD-licensed code: $BSD_IMPORTER" 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: on:
release: release:
types: [published] types: [published]
workflow_run:
workflows: [Release]
types: [completed]
permissions:
contents: read
jobs: jobs:
post: post:
if: github.event_name == 'release'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: roots/discourse-topic-github-release-action@557d74ea05b6cc0c47f555c1d5d28a89d904005b # v1.1.0 - uses: roots/discourse-topic-github-release-action@557d74ea05b6cc0c47f555c1d5d28a89d904005b # v1.1.0
@@ -15,3 +22,78 @@ jobs:
discourse-author-username: NetBird discourse-author-username: NetBird
discourse-category: 17 discourse-category: 17
discourse-tags: releases 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 ' \ sh -c ' \
apk update; apk add --no-cache \ apk update; apk add --no-cache \
ca-certificates iptables ip6tables dbus dbus-dev libpcap-dev build-base; \ 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: test_relay:
@@ -498,7 +498,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
arch: ["amd64"] arch: ["amd64"]
store: ["sqlite", "postgres", "mysql"] store: ["sqlite", "postgres"]
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Checkout code - name: Checkout code
@@ -547,10 +547,6 @@ jobs:
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }} DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
run: echo "$DOCKER_TOKEN" | sudo docker login --username "$DOCKER_USER" --password-stdin 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 # 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 # 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. # 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 # resolve; the Where-Object pipeline then drops the broken package by
# path. Without -e, go list aborts with empty stdout. # path. Without -e, go list aborts with empty stdout.
run: | 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" $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" $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 Set-Content -Path "${{ github.workspace }}\run-tests.cmd" -Value $cmd
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
store: ["sqlite", "postgres", "mysql"] store: ["sqlite", "postgres"]
services: services:
postgres: postgres:
image: ${{ (matrix.store == 'postgres') && 'postgres' || '' }} image: ${{ (matrix.store == 'postgres') && 'postgres' || '' }}
@@ -35,19 +35,6 @@ jobs:
--health-timeout 5s --health-timeout 5s
ports: ports:
- 5432:5432 - 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: steps:
- name: Set Database Connection String - name: Set Database Connection String
run: | run: |
@@ -56,11 +43,6 @@ jobs:
else else
echo "NETBIRD_STORE_ENGINE_POSTGRES_DSN==" >> $GITHUB_ENV echo "NETBIRD_STORE_ENGINE_POSTGRES_DSN==" >> $GITHUB_ENV
fi 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 - name: Install jq
run: sudo apt-get install -y jq run: sudo apt-get install -y jq
@@ -86,15 +68,6 @@ jobs:
restore-keys: | restore-keys: |
${{ runner.os }}-go- ${{ 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 - name: cp setup.env
run: cp infrastructure_files/tests/setup.env infrastructure_files/ 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_AUTH_SUPPORTED_SCOPES: "openid profile email offline_access api email_verified"
CI_NETBIRD_STORE_CONFIG_ENGINE: ${{ matrix.store }} CI_NETBIRD_STORE_CONFIG_ENGINE: ${{ matrix.store }}
NETBIRD_STORE_ENGINE_POSTGRES_DSN: ${{ env.NETBIRD_STORE_ENGINE_POSTGRES_DSN }} 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_MGMT_IDP_SIGNKEY_REFRESH: false
- name: check values - name: check values
@@ -143,7 +115,6 @@ jobs:
CI_NETBIRD_SIGNAL_PORT: 12345 CI_NETBIRD_SIGNAL_PORT: 12345
CI_NETBIRD_STORE_CONFIG_ENGINE: ${{ matrix.store }} CI_NETBIRD_STORE_CONFIG_ENGINE: ${{ matrix.store }}
NETBIRD_STORE_ENGINE_POSTGRES_DSN: "${{ env.NETBIRD_STORE_ENGINE_POSTGRES_DSN }}$" 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_MGMT_IDP_SIGNKEY_REFRESH: false
CI_NETBIRD_TURN_EXTERNAL_IP: "1.2.3.4" CI_NETBIRD_TURN_EXTERNAL_IP: "1.2.3.4"
CI_NETBIRD_MGMT_DISABLE_DEFAULT_POLICY: false 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 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 -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 "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" grep NETBIRD_STORE_ENGINE_POSTGRES_DSN docker-compose.yml | egrep "$NETBIRD_STORE_ENGINE_POSTGRES_DSN"
# check relay values # check relay values
grep "NB_EXPOSED_ADDRESS=rels://$CI_NETBIRD_DOMAIN:33445" docker-compose.yml 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)" echo "Size: ${SIZE} bytes (${SIZE_MB} MB)"
if [ ${SIZE} -gt 67108864 ]; then if [ ${SIZE} -gt 73400320 ]; then
echo "Wasm binary size (${SIZE_MB}MB) exceeds 64MB limit!" echo "Wasm binary size (${SIZE_MB}MB) exceeds 70MB limit!"
exit 1 exit 1
fi 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 - -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 }}" 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: universal_binaries:
- id: netbird - id: netbird
@@ -318,10 +296,6 @@ archives:
builds: builds:
- netbird-idp-migrate - netbird-idp-migrate
name_template: "netbird-idp-migrate_{{ .Version }}_{{ .Os }}_{{ .Arch }}" 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 - id: nblink
builds: builds:
- nblink - nblink
+8 -14
View File
@@ -58,23 +58,17 @@ func (p *Preferences) SetManagementURL(url string) {
p.configInput.ManagementURL = url 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) { func (p *Preferences) GetAdminURL() (string, error) {
if p.configInput.AdminURL != "" { return profilemanager.DefaultAdminURL, nil
return p.configInput.AdminURL, nil
}
cfg, err := profilemanager.ReadConfigOrDefault(p.configInput.ConfigPath)
if err != nil {
return "", err
}
return cfg.AdminURL.String(), err
} }
// SetAdminURL stores the given URL and waits for commit // SetAdminURL is a no-op kept for API compatibility.
func (p *Preferences) SetAdminURL(url string) { //
p.configInput.AdminURL = url // 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 // HasPreSharedKey reports whether a pre-shared key is staged, persisted, or
// enforced by MDM; the key itself is never handed to the native layer. // 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") cfgFile := filepath.Join(t.TempDir(), "netbird.json")
p := NewPreferences(cfgFile) p := NewPreferences(cfgFile)
// The admin URL is deprecated: setting it is a no-op.
p.SetAdminURL(exampleString) p.SetAdminURL(exampleString)
resp, err := p.GetAdminURL() resp, err := p.GetAdminURL()
if err != nil { if err != nil {
t.Fatalf("failed to read admin url: %s", err) t.Fatalf("failed to read admin url: %s", err)
} }
if resp != exampleString { if resp != profilemanager.DefaultAdminURL {
t.Errorf("unexpected admin url: %s", resp) 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) t.Fatalf("failed to read admin url: %s", err)
} }
if resp != exampleURL { if resp != profilemanager.DefaultAdminURL {
t.Errorf("unexpected admin url: %s", resp) 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().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().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().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().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)") 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.Username = username
req.ManagementUrl = managementURL req.ManagementUrl = managementURL
req.AdminURL = adminURL
req.NatExternalIPs = natExternalIPs req.NatExternalIPs = natExternalIPs
req.CustomDNSAddress = customDNSAddressConverted req.CustomDNSAddress = customDNSAddressConverted
req.ExtraIFaceBlacklist = extraIFaceBlackList req.ExtraIFaceBlacklist = extraIFaceBlackList
+4 -5
View File
@@ -12,11 +12,10 @@ import (
"sync" "sync"
"github.com/sirupsen/logrus" "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"
"github.com/netbirdio/netbird/client/iface/netstack" "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"
"github.com/netbirdio/netbird/client/internal/auth" "github.com/netbirdio/netbird/client/internal/auth"
"github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peer"
@@ -256,10 +255,10 @@ func New(opts Options) (*Client, error) {
} }
if opts.Performance.PreallocatedBuffersPerPool != nil { if opts.Performance.PreallocatedBuffersPerPool != nil {
wgdevice.SetPreallocatedBuffersPerPool(*opts.Performance.PreallocatedBuffersPerPool) wgbackend.SetPreallocatedBuffersPerPool(*opts.Performance.PreallocatedBuffersPerPool)
} }
if opts.Performance.MaxBatchSize != nil { if opts.Performance.MaxBatchSize != nil {
wgdevice.SetMaxBatchSizeOverride(*opts.Performance.MaxBatchSize) wgbackend.SetMaxBatchSizeOverride(*opts.Performance.MaxBatchSize)
} }
return &Client{ return &Client{
@@ -651,7 +650,7 @@ func (c *Client) getEngine() (*internal.Engine, error) {
return engine, nil 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() engine, err := c.getEngine()
if err != nil { if err != nil {
return nil, netip.Addr{}, err return nil, netip.Addr{}, err
+2 -3
View File
@@ -1,10 +1,9 @@
package firewall package firewall
import ( import (
wgdevice "golang.zx2c4.com/wireguard/device"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr" "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 // EnvForceUserspaceFirewall forces the use of the userspace packet filter even when
@@ -20,5 +19,5 @@ type IFaceMapper interface {
IsUserspaceBind() bool IsUserspaceBind() bool
SetFilter(device.PacketFilter) error SetFilter(device.PacketFilter) error
GetDevice() *device.FilteredDevice GetDevice() *device.FilteredDevice
GetWGDevice() *wgdevice.Device GetWGDevice() *wgbackend.Device
} }
+2 -2
View File
@@ -19,7 +19,6 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
wgdevice "golang.zx2c4.com/wireguard/device"
nberrors "github.com/netbirdio/netbird/client/errors" nberrors "github.com/netbirdio/netbird/client/errors"
"github.com/netbirdio/netbird/client/firewall/firewalld" "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/device"
"github.com/netbirdio/netbird/client/iface/netstack" "github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbid "github.com/netbirdio/netbird/client/internal/acl/id" nbid "github.com/netbirdio/netbird/client/internal/acl/id"
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types" nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
"github.com/netbirdio/netbird/client/internal/statemanager" "github.com/netbirdio/netbird/client/internal/statemanager"
@@ -121,7 +121,7 @@ type Iface interface {
Name() string Name() string
Address() wgaddr.Address Address() wgaddr.Address
SetFilter(device.PacketFilter) error SetFilter(device.PacketFilter) error
GetWGDevice() *wgdevice.Device GetWGDevice() *wgbackend.Device
} }
// InterfaceAllower opens the NetBird interface in the host firewall so it // InterfaceAllower opens the NetBird interface in the host firewall so it
@@ -5,17 +5,17 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"go.uber.org/mock/gomock"
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
wgdevice "golang.zx2c4.com/wireguard/device" "go.uber.org/mock/gomock"
fw "github.com/netbirdio/netbird/client/firewall/manager" fw "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/mocks" "github.com/netbirdio/netbird/client/iface/mocks"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/shared/management/domain" "github.com/netbirdio/netbird/shared/management/domain"
) )
@@ -791,10 +791,10 @@ func setupRoutedManager(tb testing.TB, network string) *Manager {
} }
}, },
GetDeviceFunc: func() *device.FilteredDevice { GetDeviceFunc: func() *device.FilteredDevice {
return &device.FilteredDevice{Device: dev} return &device.FilteredDevice{TUN: dev}
}, },
GetWGDeviceFunc: func() *wgdevice.Device { GetWGDeviceFunc: func() *wgbackend.Device {
return &wgdevice.Device{} return &wgbackend.Device{}
}, },
} }
@@ -4,17 +4,17 @@ import (
"net/netip" "net/netip"
"testing" "testing"
"go.uber.org/mock/gomock"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
wgdevice "golang.zx2c4.com/wireguard/device" "go.uber.org/mock/gomock"
fw "github.com/netbirdio/netbird/client/firewall/manager" fw "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/mocks" "github.com/netbirdio/netbird/client/iface/mocks"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
// TestAddRouteFilteringReturnsExistingRule verifies that adding the same route // TestAddRouteFilteringReturnsExistingRule verifies that adding the same route
@@ -180,10 +180,10 @@ func TestBlockInvalidRoutedDualStack(t *testing.T) {
} }
}, },
GetDeviceFunc: func() *device.FilteredDevice { GetDeviceFunc: func() *device.FilteredDevice {
return &device.FilteredDevice{Device: dev} return &device.FilteredDevice{TUN: dev}
}, },
GetWGDeviceFunc: func() *wgdevice.Device { GetWGDeviceFunc: func() *wgbackend.Device {
return &wgdevice.Device{} return &wgbackend.Device{}
}, },
} }
@@ -228,10 +228,10 @@ func TestBlockInvalidRoutedIdempotent(t *testing.T) {
} }
}, },
GetDeviceFunc: func() *device.FilteredDevice { GetDeviceFunc: func() *device.FilteredDevice {
return &device.FilteredDevice{Device: dev} return &device.FilteredDevice{TUN: dev}
}, },
GetWGDeviceFunc: func() *wgdevice.Device { GetWGDeviceFunc: func() *wgbackend.Device {
return &wgdevice.Device{} return &wgbackend.Device{}
}, },
} }
@@ -291,10 +291,10 @@ func TestBlockRuleNotAccumulatedOnRepeatedEnableRouting(t *testing.T) {
} }
}, },
GetDeviceFunc: func() *device.FilteredDevice { GetDeviceFunc: func() *device.FilteredDevice {
return &device.FilteredDevice{Device: dev} return &device.FilteredDevice{TUN: dev}
}, },
GetWGDeviceFunc: func() *wgdevice.Device { GetWGDeviceFunc: func() *wgbackend.Device {
return &wgdevice.Device{} return &wgbackend.Device{}
}, },
} }
@@ -410,10 +410,10 @@ func setupTestManager(t *testing.T) *Manager {
} }
}, },
GetDeviceFunc: func() *device.FilteredDevice { GetDeviceFunc: func() *device.FilteredDevice {
return &device.FilteredDevice{Device: dev} return &device.FilteredDevice{TUN: dev}
}, },
GetWGDeviceFunc: func() *wgdevice.Device { GetWGDeviceFunc: func() *wgbackend.Device {
return &wgdevice.Device{} return &wgbackend.Device{}
}, },
} }
+3 -3
View File
@@ -14,7 +14,6 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
wgdevice "golang.zx2c4.com/wireguard/device"
fw "github.com/netbirdio/netbird/client/firewall/manager" fw "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack" "github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
@@ -22,6 +21,7 @@ import (
nbiface "github.com/netbirdio/netbird/client/iface" nbiface "github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/netflow" "github.com/netbirdio/netbird/client/internal/netflow"
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types" nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
"github.com/netbirdio/netbird/shared/management/domain" "github.com/netbirdio/netbird/shared/management/domain"
@@ -34,7 +34,7 @@ type IFaceMock struct {
NameFunc func() string NameFunc func() string
SetFilterFunc func(device.PacketFilter) error SetFilterFunc func(device.PacketFilter) error
AddressFunc func() wgaddr.Address AddressFunc func() wgaddr.Address
GetWGDeviceFunc func() *wgdevice.Device GetWGDeviceFunc func() *wgbackend.Device
GetDeviceFunc func() *device.FilteredDevice GetDeviceFunc func() *device.FilteredDevice
} }
@@ -45,7 +45,7 @@ func (i *IFaceMock) Name() string {
return i.NameFunc() return i.NameFunc()
} }
func (i *IFaceMock) GetWGDevice() *wgdevice.Device { func (i *IFaceMock) GetWGDevice() *wgbackend.Device {
if i.GetWGDeviceFunc == nil { if i.GetWGDeviceFunc == nil {
return nil return nil
} }
@@ -5,12 +5,12 @@ import (
"strconv" "strconv"
"sync/atomic" "sync/atomic"
wgdevice "golang.zx2c4.com/wireguard/device"
"gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/stack"
nblog "github.com/netbirdio/netbird/client/firewall/uspfilter/log" 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 // PacketCapture captures raw packets for debugging. Implementations must be
@@ -23,7 +23,7 @@ type PacketCapture interface {
type endpoint struct { type endpoint struct {
logger *nblog.Logger logger *nblog.Logger
dispatcher stack.NetworkDispatcher dispatcher stack.NetworkDispatcher
device *wgdevice.Device device *wgbackend.Device
mtu atomic.Uint32 mtu atomic.Uint32
capture atomic.Pointer[PacketCapture] capture atomic.Pointer[PacketCapture]
} }
@@ -12,7 +12,6 @@ import (
"time" "time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
wgdevice "golang.zx2c4.com/wireguard/device"
"gvisor.dev/gvisor/pkg/buffer" "gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/header" "gvisor.dev/gvisor/pkg/tcpip/header"
@@ -26,6 +25,7 @@ import (
"github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack" "github.com/netbirdio/netbird/client/firewall/uspfilter/conntrack"
nblog "github.com/netbirdio/netbird/client/firewall/uspfilter/log" nblog "github.com/netbirdio/netbird/client/firewall/uspfilter/log"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types" 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. // IFace provides the WireGuard device and overlay addresses the forwarder needs.
type IFace interface { type IFace interface {
GetWGDevice() *wgdevice.Device GetWGDevice() *wgbackend.Device
Address() wgaddr.Address Address() wgaddr.Address
} }
+2 -3
View File
@@ -1,8 +1,7 @@
package bind package bind
import ( import (
wireguard "golang.zx2c4.com/wireguard/conn" "github.com/netbirdio/netbird/client/iface/wgbackend"
nbnet "github.com/netbirdio/netbird/client/net" nbnet "github.com/netbirdio/netbird/client/net"
) )
@@ -10,6 +9,6 @@ import (
func init() { func init() {
listener := nbnet.NewListener() listener := nbnet.NewListener()
if listener.ListenConfig.Control != nil { 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 ( import (
"net" "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 { func EndpointToUDPAddr(e Endpoint) *net.UDPAddr {
return &net.UDPAddr{ return &net.UDPAddr{
+15 -15
View File
@@ -15,10 +15,10 @@ import (
"github.com/pion/transport/v3" "github.com/pion/transport/v3"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
wgConn "golang.zx2c4.com/wireguard/conn"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbnet "github.com/netbirdio/netbird/client/net" nbnet "github.com/netbirdio/netbird/client/net"
) )
@@ -36,7 +36,7 @@ type receiverCreator struct {
iceBind *ICEBind 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) 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 // 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. // use the port because in the Send function the wgConn.Endpoint the port info is not exported.
type ICEBind struct { type ICEBind struct {
*wgConn.StdNetBind *wgbackend.StdNetBind
transportNet transport.Net transportNet transport.Net
address wgaddr.Address address wgaddr.Address
@@ -74,7 +74,7 @@ type ICEBind struct {
} }
func NewICEBind(transportNet transport.Net, address wgaddr.Address, mtu uint16) *ICEBind { func NewICEBind(transportNet transport.Net, address wgaddr.Address, mtu uint16) *ICEBind {
b, _ := wgConn.NewStdNetBind().(*wgConn.StdNetBind) b, _ := wgbackend.NewStdNetBind().(*wgbackend.StdNetBind)
ib := &ICEBind{ ib := &ICEBind{
StdNetBind: b, StdNetBind: b,
transportNet: transportNet, transportNet: transportNet,
@@ -90,11 +90,11 @@ func NewICEBind(transportNet transport.Net, address wgaddr.Address, mtu uint16)
rc := receiverCreator{ rc := receiverCreator{
ib, ib,
} }
ib.StdNetBind = wgConn.NewStdNetBindWithReceiverCreator(rc) ib.StdNetBind = wgbackend.NewStdNetBindWithReceiverCreator(rc)
return ib 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() s.closedChanMu.Lock()
defer s.closedChanMu.Unlock() 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() b.endpointsMu.Lock()
conn, ok := b.endpoints[ep.DstIP()] conn, ok := b.endpoints[ep.DstIP()]
b.endpointsMu.Unlock() b.endpointsMu.Unlock()
@@ -206,7 +206,7 @@ func (b *ICEBind) Send(bufs [][]byte, ep wgConn.Endpoint) error {
return nil 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() s.muUDPMux.Lock()
defer s.muUDPMux.Unlock() defer s.muUDPMux.Unlock()
@@ -218,7 +218,7 @@ func (s *ICEBind) createReceiverFn(pc wgConn.BatchReader, conn *net.UDPConn, rxO
} }
s.createOrUpdateMux() 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) msgs := getMessages(msgsPool)
for i := range bufs { for i := range bufs {
(*msgs)[i].Buffers[0] = bufs[i] (*msgs)[i].Buffers[0] = bufs[i]
@@ -229,13 +229,13 @@ func (s *ICEBind) createReceiverFn(pc wgConn.BatchReader, conn *net.UDPConn, rxO
var numMsgs int var numMsgs int
if runtime.GOOS == "linux" || runtime.GOOS == "android" { if runtime.GOOS == "linux" || runtime.GOOS == "android" {
if rxOffload { if rxOffload {
readAt := len(*msgs) - (wgConn.IdealBatchSize / wgConn.UdpSegmentMaxDatagrams) readAt := len(*msgs) - (wgbackend.IdealBatchSize / wgbackend.UdpSegmentMaxDatagrams)
//nolint:staticcheck //nolint:staticcheck
_, err = pc.ReadBatch((*msgs)[readAt:], 0) _, err = pc.ReadBatch((*msgs)[readAt:], 0)
if err != nil { if err != nil {
return 0, err return 0, err
} }
numMsgs, err = wgConn.SplitCoalescedMessages(*msgs, readAt, wgConn.GetGSOSize) numMsgs, err = wgbackend.SplitCoalescedMessages(*msgs, readAt, wgbackend.GetGSOSize)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -278,8 +278,8 @@ func (s *ICEBind) createReceiverFn(pc wgConn.BatchReader, conn *net.UDPConn, rxO
s.activityRecorder.record(addrPort) s.activityRecorder.record(addrPort)
} }
ep := &wgConn.StdNetEndpoint{AddrPort: addrPort} // TODO: remove allocation ep := &wgbackend.StdNetEndpoint{AddrPort: addrPort} // TODO: remove allocation
wgConn.GetSrcFromControl(msg.OOB[:msg.NN], ep) wgbackend.GetSrcFromControl(msg.OOB[:msg.NN], ep)
eps[i] = ep eps[i] = ep
} }
return numMsgs, nil 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 // 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. // 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() closedChan := c.currentClosedChan()
select { select {
@@ -375,7 +375,7 @@ func (c *ICEBind) receiveRelayed(buffs [][]byte, sizes []int, eps []wgConn.Endpo
} }
copy(buffs[0], msg.Buffer) copy(buffs[0], msg.Buffer)
sizes[0] = len(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 isTransportPkg(buffs, sizes[0]) {
if ep, ok := eps[0].(*Endpoint); ok { if ep, ok := eps[0].(*Endpoint); ok {
+6 -5
View File
@@ -6,7 +6,8 @@ import (
"time" "time"
"github.com/stretchr/testify/require" "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 // 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 // calling Bind.Close, closeBindLocked waits on exactly that WaitGroup while
// holding device.net, so a receive function that never returns wedges the // holding device.net, so a receive function that never returns wedges the
// device and every goroutine that needs the same lock. // 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) wg, _ := startReceiversEntered(fns)
return wg 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 // 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 // entered is either inside its blocking receive or about to be, which is a
// stronger signal to synchronise on than a bare sleep. // 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 wg sync.WaitGroup
var entered sync.WaitGroup var entered sync.WaitGroup
wg.Add(len(fns)) wg.Add(len(fns))
entered.Add(len(fns)) entered.Add(len(fns))
for i := range fns { for i := range fns {
go func(fn wgConn.ReceiveFunc) { go func(fn wgbackend.ReceiveFunc) {
defer wg.Done() defer wg.Done()
buffs := [][]byte{make([]byte, 1500)} buffs := [][]byte{make([]byte, 1500)}
sizes := make([]int, 1) sizes := make([]int, 1)
eps := make([]wgConn.Endpoint, 1) eps := make([]wgbackend.Endpoint, 1)
first := true first := true
for { for {
if first { if first {
+4 -5
View File
@@ -16,10 +16,9 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
"golang.org/x/net/ipv6" "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/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
func TestICEBind_CreatesReceiverForBothIPv4AndIPv6(t *testing.T) { func TestICEBind_CreatesReceiverForBothIPv4AndIPv6(t *testing.T) {
@@ -370,16 +369,16 @@ func TestICEBind_OpenDoesNotDiscoverInterfaces(t *testing.T) {
} }
func TestICEBind_DeviceUpDoesNotWaitOnInterfaceDiscovery(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) require.NoError(t, err)
var dev *device.Device var dev *wgbackend.Device
n := newCountingNet() n := newCountingNet()
n.before = func() { n.before = func() {
_ = dev.IpcGetOperation(io.Discard) _ = dev.IpcGetOperation(io.Discard)
} }
iceBind := NewICEBind(n, testAddress(), 1280) 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) upDone := make(chan error, 1)
go func() { go func() {
+7 -7
View File
@@ -7,15 +7,15 @@ import (
"sync" "sync"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/conn"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
// RelayBindJS is a conn.Bind implementation for WebAssembly environments. // 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 // Do not limit to build only js, because we want to be able to run tests
type RelayBindJS struct { type RelayBindJS struct {
*conn.StdNetBind *wgbackend.StdNetBind
recvChan chan recvMessage recvChan chan recvMessage
endpoints map[netip.Addr]net.Conn endpoints map[netip.Addr]net.Conn
@@ -34,12 +34,12 @@ func NewRelayBindJS() *RelayBindJS {
} }
// Open creates a receive function for handling relay packets in WASM. // 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) log.Debugf("Open: creating receive function for port %d", uport)
s.ctx, s.cancel = context.WithCancel(context.Background()) 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 { select {
case <-s.ctx.Done(): case <-s.ctx.Done():
return 0, net.ErrClosed return 0, net.ErrClosed
@@ -49,13 +49,13 @@ func (s *RelayBindJS) Open(uport uint16) ([]conn.ReceiveFunc, uint16, error) {
} }
copy(bufs[0], msg.Buffer) copy(bufs[0], msg.Buffer)
sizes[0] = len(msg.Buffer) sizes[0] = len(msg.Buffer)
eps[0] = conn.Endpoint(msg.Endpoint) eps[0] = wgbackend.Endpoint(msg.Endpoint)
return 1, nil return 1, nil
} }
} }
log.Debugf("Open: receive function created, returning port %d", uport) 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 { 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. // 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 { if ep == nil {
return nil return nil
} }
+3 -2
View File
@@ -12,7 +12,8 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/net/ipv4" "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 // 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 // A leftover size from an earlier read, which is what makes the missing reset
// observable. // observable.
sizes := []int{148} sizes := []int{148}
eps := make([]wgConn.Endpoint, 1) eps := make([]wgbackend.Endpoint, 1)
n, err := recvFn(bufs, sizes, eps) n, err := recvFn(bufs, sizes, eps)
require.NoError(t, err) require.NoError(t, err)
+3 -3
View File
@@ -5,16 +5,16 @@ package configurer
import ( import (
"net" "net"
"golang.zx2c4.com/wireguard/ipc" "github.com/netbirdio/netbird/client/iface/wgbackend"
) )
func openUAPI(deviceName string) (net.Listener, error) { func openUAPI(deviceName string) (net.Listener, error) {
uapiSock, err := ipc.UAPIOpen(deviceName) uapiSock, err := wgbackend.UAPIOpen(deviceName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
listener, err := ipc.UAPIListen(deviceName, uapiSock) listener, err := wgbackend.UAPIListen(deviceName, uapiSock)
if err != nil { if err != nil {
_ = uapiSock.Close() _ = uapiSock.Close()
return nil, err return nil, err
+2 -2
View File
@@ -3,9 +3,9 @@ package configurer
import ( import (
"net" "net"
"golang.zx2c4.com/wireguard/ipc" "github.com/netbirdio/netbird/client/iface/wgbackend"
) )
func openUAPI(deviceName string) (net.Listener, error) { 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" "time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
wgconn "golang.zx2c4.com/wireguard/conn"
"golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbnet "github.com/netbirdio/netbird/client/net" nbnet "github.com/netbirdio/netbird/client/net"
"github.com/netbirdio/netbird/monotime" "github.com/netbirdio/netbird/monotime"
) )
@@ -22,7 +21,7 @@ import (
var ErrAllowedIPNotFound = fmt.Errorf("allowed IP not found") var ErrAllowedIPNotFound = fmt.Errorf("allowed IP not found")
type WGUSPConfigurer struct { type WGUSPConfigurer struct {
device *device.Device device *wgbackend.Device
deviceName string deviceName string
activityRecorder *bind.ActivityRecorder activityRecorder *bind.ActivityRecorder
statsCache *statsCache statsCache *statsCache
@@ -31,14 +30,14 @@ type WGUSPConfigurer struct {
} }
// NewUSPConfigurer creates a userspace configurer and starts its UAPI listener. // 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 := NewUSPConfigurerNoUAPI(device, deviceName, activityRecorder)
wgCfg.startUAPI() wgCfg.startUAPI()
return wgCfg return wgCfg
} }
// NewUSPConfigurerNoUAPI creates a userspace configurer without a UAPI listener. // 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{ wgCfg := &WGUSPConfigurer{
device: device, device: device,
deviceName: deviceName, deviceName: deviceName,
@@ -59,7 +58,7 @@ func (c *WGUSPConfigurer) ConfigureInterface(privateKey string, port int) error
return fmt.Errorf("invalid listen port %d", port) 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) return fmt.Errorf("set private key: %w", err)
} }
if err := c.device.SetListenPort(uint16(port)); err != nil { 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 return err
} }
presharedKey := device.NoisePresharedKey(psk) presharedKey := wgbackend.NoisePresharedKey(psk)
return c.device.ConfigurePeer(device.NoisePublicKey(parsedPeerKey), device.PeerConfig{ return c.device.ConfigurePeer(wgbackend.NoisePublicKey(parsedPeerKey), wgbackend.PeerConfig{
PresharedKey: &presharedKey, PresharedKey: &presharedKey,
UpdateOnly: updateOnly, UpdateOnly: updateOnly,
}) })
@@ -99,7 +98,7 @@ func (c *WGUSPConfigurer) UpdatePeer(peerKey string, allowedIps []netip.Prefix,
return err return err
} }
cfg := device.PeerConfig{ cfg := wgbackend.PeerConfig{
PersistentKeepalive: &keepAlive, PersistentKeepalive: &keepAlive,
AllowedIPs: prefixes, AllowedIPs: prefixes,
} }
@@ -116,11 +115,11 @@ func (c *WGUSPConfigurer) UpdatePeer(peerKey string, allowedIps []netip.Prefix,
} }
if preSharedKey != nil { if preSharedKey != nil {
psk := device.NoisePresharedKey(*preSharedKey) psk := wgbackend.NoisePresharedKey(*preSharedKey)
cfg.PresharedKey = &psk 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) 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) 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 { if peer == nil {
return ErrPeerNotFound return ErrPeerNotFound
} }
@@ -147,7 +146,7 @@ func (c *WGUSPConfigurer) RemoveEndpointAddress(peerKey string) error {
peer.ZeroAndFlushAll() peer.ZeroAndFlushAll()
var noKeepAlive time.Duration 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, PersistentKeepalive: &noKeepAlive,
UpdateOnly: true, UpdateOnly: true,
}); err != nil { }); err != nil {
@@ -163,7 +162,7 @@ func (c *WGUSPConfigurer) RemovePeer(peerKey string) error {
return err return err
} }
c.device.RemovePeer(device.NoisePublicKey(peerKeyParsed)) c.device.RemovePeer(wgbackend.NoisePublicKey(peerKeyParsed))
c.activityRecorder.Remove(peerKey) c.activityRecorder.Remove(peerKey)
return nil return nil
} }
@@ -178,7 +177,7 @@ func (c *WGUSPConfigurer) AddAllowedIP(peerKey string, allowedIP netip.Prefix) e
return fmt.Errorf("invalid allowed IP %v", allowedIP) 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 { if peer == nil {
return nil return nil
} }
@@ -198,7 +197,7 @@ func (c *WGUSPConfigurer) RemoveAllowedIP(peerKey string, allowedIP netip.Prefix
return ErrAllowedIPNotFound return ErrAllowedIPNotFound
} }
peer := c.device.LookupPeer(device.NoisePublicKey(peerKeyParsed)) peer := c.device.LookupPeer(wgbackend.NoisePublicKey(peerKeyParsed))
if peer == nil { if peer == nil {
return ErrPeerNotFound return ErrPeerNotFound
} }
@@ -284,7 +283,7 @@ func (c *WGUSPConfigurer) fetchStats() (map[string]WGStats, error) {
return stats, nil return stats, nil
} }
func peerStats(peer *device.Peer) Peer { func peerStats(peer *wgbackend.Peer) Peer {
stats := Peer{ stats := Peer{
PublicKey: wgtypes.Key(peer.PublicKey()).String(), PublicKey: wgtypes.Key(peer.PublicKey()).String(),
AllowedIPs: prefixesToIPNets(peer.AllowedIPs()), AllowedIPs: prefixesToIPNets(peer.AllowedIPs()),
@@ -299,7 +298,7 @@ func peerStats(peer *device.Peer) Peer {
return stats return stats
} }
func endpointAddrPort(endpoint wgconn.Endpoint) (netip.AddrPort, bool) { func endpointAddrPort(endpoint wgbackend.Endpoint) (netip.AddrPort, bool) {
if endpoint == nil { if endpoint == nil {
return netip.AddrPort{}, false return netip.AddrPort{}, false
} }
@@ -8,11 +8,10 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
wgconn "golang.zx2c4.com/wireguard/conn"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/iface/bind" "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 // newTestUSPConfigurer builds a configurer over a real wireguard-go device backed by an
@@ -20,7 +19,7 @@ import (
func newTestUSPConfigurer(t *testing.T) *WGUSPConfigurer { func newTestUSPConfigurer(t *testing.T) *WGUSPConfigurer {
t.Helper() 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) t.Cleanup(dev.Close)
c := NewUSPConfigurerNoUAPI(dev, "wgtest0", bind.NewActivityRecorder()) c := NewUSPConfigurerNoUAPI(dev, "wgtest0", bind.NewActivityRecorder())
+7 -7
View File
@@ -11,20 +11,20 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "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 // 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. // opened, no peer goroutine starts, and the configuration state is all the tests observe.
type idleTUN struct { type idleTUN struct {
events chan tun.Event events chan wgbackend.TUNEvent
closed chan struct{} closed chan struct{}
} }
func newIdleTUN() *idleTUN { 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 } 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) 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 { func (t *idleTUN) Close() error {
close(t.closed) close(t.closed)
@@ -142,12 +142,12 @@ func TestRemoveEndpointAddressKeepsThePeerAndStopsTheKeepalive(t *testing.T) {
endpoint := &net.UDPAddr{IP: net.IPv4(192, 0, 2, 10), Port: 51820} endpoint := &net.UDPAddr{IP: net.IPv4(192, 0, 2, 10), Port: 51820}
prefixes := []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")} prefixes := []netip.Prefix{netip.MustParsePrefix("100.64.0.1/32")}
require.NoError(t, c.UpdatePeer(peerKey, prefixes, 25*time.Second, endpoint, nil), "add peer") 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.NotNil(t, before, "the peer must be on the device")
require.NoError(t, c.RemoveEndpointAddress(peerKey), "remove endpoint address") 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") "the peer object must survive the endpoint removal")
stats, err := c.FullStats() stats, err := c.FullStats()
require.NoError(t, err, "read device stats") require.NoError(t, err, "read device stats")
+3 -6
View File
@@ -3,13 +3,10 @@
package iface package iface
import ( 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/device"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
type WGTunDevice interface { type WGTunDevice interface {
@@ -21,7 +18,7 @@ type WGTunDevice interface {
DeviceName() string DeviceName() string
Close() error Close() error
FilteredDevice() *device.FilteredDevice FilteredDevice() *device.FilteredDevice
Device() *wgdevice.Device Device() *wgbackend.Device
GetNet() *netstack.Net GetNet() *wgbackend.NetstackNet
GetICEBind() device.EndpointManager GetICEBind() device.EndpointManager
} }
+7 -9
View File
@@ -8,14 +8,12 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix" "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/bind"
"github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "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 // 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 disableDNS bool
name string name string
device *device.Device device *wgbackend.Device
filteredDevice *FilteredDevice filteredDevice *FilteredDevice
udpMux *udpmux.UniversalUDPMuxDefault udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer configurer WGConfigurer
@@ -74,7 +72,7 @@ func (t *WGTunDevice) Create(routes []string, dns string, searchDomains []string
return nil, err return nil, err
} }
unmonitoredTUN, name, err := tun.CreateUnmonitoredTUNFromFD(fd) unmonitoredTUN, name, err := wgbackend.CreateUnmonitoredTUNFromFD(fd)
if err != nil { if err != nil {
_ = unix.Close(fd) _ = unix.Close(fd)
log.Errorf("failed to create Android interface: %s", err) 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) t.filteredDevice = newDeviceFilter(t.renewableTun)
log.Debugf("attaching to interface %v", name) 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. // 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 // this helps with support for the older NetBird clients that had a hardcoded direct mode
// t.device.DisableSomeRoamingForBrokenMobileSemantics() // t.device.DisableSomeRoamingForBrokenMobileSemantics()
@@ -121,7 +119,7 @@ func (t *WGTunDevice) RenewTun(fd int) error {
return fmt.Errorf("device not initialized") return fmt.Errorf("device not initialized")
} }
unmonitoredTUN, _, err := tun.CreateUnmonitoredTUNFromFD(fd) unmonitoredTUN, _, err := wgbackend.CreateUnmonitoredTUNFromFD(fd)
if err != nil { if err != nil {
_ = unix.Close(fd) _ = unix.Close(fd)
log.Errorf("failed to renew Android interface: %s", err) log.Errorf("failed to renew Android interface: %s", err)
@@ -155,7 +153,7 @@ func (t *WGTunDevice) Close() error {
return nil return nil
} }
func (t *WGTunDevice) Device() *device.Device { func (t *WGTunDevice) Device() *wgbackend.Device {
return t.device return t.device
} }
@@ -175,7 +173,7 @@ func (t *WGTunDevice) FilteredDevice() *FilteredDevice {
return t.filteredDevice return t.filteredDevice
} }
func (t *WGTunDevice) GetNet() *netstack.Net { func (t *WGTunDevice) GetNet() *wgbackend.NetstackNet {
return nil return nil
} }
+7 -9
View File
@@ -7,14 +7,12 @@ import (
"os/exec" "os/exec"
log "github.com/sirupsen/logrus" 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/bind"
"github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
type TunDevice struct { type TunDevice struct {
@@ -25,7 +23,7 @@ type TunDevice struct {
mtu uint16 mtu uint16
iceBind *bind.ICEBind iceBind *bind.ICEBind
device *device.Device device *wgbackend.Device
filteredDevice *FilteredDevice filteredDevice *FilteredDevice
udpMux *udpmux.UniversalUDPMuxDefault udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer configurer WGConfigurer
@@ -43,17 +41,17 @@ func NewTunDevice(name string, address wgaddr.Address, port int, key string, mtu
} }
func (t *TunDevice) Create() (WGConfigurer, error) { 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 { if err != nil {
return nil, fmt.Errorf("error creating tun device: %s", err) return nil, fmt.Errorf("error creating tun device: %s", err)
} }
t.filteredDevice = newDeviceFilter(tunDevice) t.filteredDevice = newDeviceFilter(tunDevice)
// We need to create a wireguard-go device and listen to configuration requests // We need to create a wireguard-go device and listen to configuration requests
t.device = device.NewDevice( t.device = wgbackend.NewDevice(
t.filteredDevice, t.filteredDevice,
t.iceBind, t.iceBind,
device.NewLogger(wgLogLevel(), "[netbird] "), wgbackend.NewLogger(wgLogLevel(), "[netbird] "),
) )
err = t.assignAddr() err = t.assignAddr()
@@ -125,7 +123,7 @@ func (t *TunDevice) FilteredDevice() *FilteredDevice {
} }
// Device returns the wireguard device // Device returns the wireguard device
func (t *TunDevice) Device() *device.Device { func (t *TunDevice) Device() *wgbackend.Device {
return t.device return t.device
} }
@@ -160,7 +158,7 @@ func (t *TunDevice) assignAddr() error {
return nil return nil
} }
func (t *TunDevice) GetNet() *netstack.Net { func (t *TunDevice) GetNet() *wgbackend.NetstackNet {
return nil return nil
} }
+8 -7
View File
@@ -8,7 +8,8 @@ import (
"sync/atomic" "sync/atomic"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
// PacketFilter interface for firewall abilities // PacketFilter interface for firewall abilities
@@ -42,7 +43,7 @@ type PacketCapture interface {
// FilteredDevice to override Read or Write of packets // FilteredDevice to override Read or Write of packets
type FilteredDevice struct { type FilteredDevice struct {
tun.Device wgbackend.TUN
filter PacketFilter filter PacketFilter
capture atomic.Pointer[PacketCapture] capture atomic.Pointer[PacketCapture]
@@ -54,9 +55,9 @@ type FilteredDevice struct {
} }
// newDeviceFilter constructor function // newDeviceFilter constructor function
func newDeviceFilter(device tun.Device) *FilteredDevice { func newDeviceFilter(device wgbackend.TUN) *FilteredDevice {
return &FilteredDevice{ return &FilteredDevice{
Device: device, TUN: device,
} }
} }
@@ -66,7 +67,7 @@ func newDeviceFilter(device tun.Device) *FilteredDevice {
func (d *FilteredDevice) Close() error { func (d *FilteredDevice) Close() error {
var err error var err error
d.closeOnce.Do(func() { d.closeOnce.Do(func() {
err = d.Device.Close() err = d.TUN.Close()
}) })
if err != nil { if err != nil {
return err return err
@@ -142,14 +143,14 @@ func (d *FilteredDevice) Write(bufs [][]byte, offset int) (int, error) {
// wintun read path and converting them into errors. // wintun read path and converting them into errors.
func (d *FilteredDevice) deviceRead(bufs [][]byte, sizes []int, offset int) (n int, err error) { func (d *FilteredDevice) deviceRead(bufs [][]byte, sizes []int, offset int) (n int, err error) {
defer d.recoverFromPanic("read", &n, &err) 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 // deviceWrite calls the underlying device Write, recovering from panics in the
// wintun write path and converting them into errors. // wintun write path and converting them into errors.
func (d *FilteredDevice) deviceWrite(bufs [][]byte, offset int) (n int, err error) { func (d *FilteredDevice) deviceWrite(bufs [][]byte, offset int) (n int, err error) {
defer d.recoverFromPanic("write", &n, &err) 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 // 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" log "github.com/sirupsen/logrus"
"golang.org/x/sys/unix" "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/bind"
"github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
type TunDevice struct { type TunDevice struct {
@@ -25,7 +23,7 @@ type TunDevice struct {
iceBind *bind.ICEBind iceBind *bind.ICEBind
tunFd int tunFd int
device *device.Device device *wgbackend.Device
filteredDevice *FilteredDevice filteredDevice *FilteredDevice
udpMux *udpmux.UniversalUDPMuxDefault udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer configurer WGConfigurer
@@ -50,7 +48,7 @@ var ErrInvalidTunnelFD = fmt.Errorf("invalid tunnel file descriptor: fd is 0 (Sw
func (t *TunDevice) Create() (WGConfigurer, error) { func (t *TunDevice) Create() (WGConfigurer, error) {
log.Infof("create tun interface") log.Infof("create tun interface")
var tunDevice tun.Device var tunDevice wgbackend.TUN
var err error var err error
// Validate the tunnel file descriptor. // Validate the tunnel file descriptor.
@@ -79,7 +77,7 @@ func (t *TunDevice) Create() (WGConfigurer, error) {
_ = unix.Close(dupTunFd) _ = unix.Close(dupTunFd)
return nil, err 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 { if err != nil {
log.Errorf("Unable to create new tun device from fd: %v", err) log.Errorf("Unable to create new tun device from fd: %v", err)
_ = unix.Close(dupTunFd) _ = unix.Close(dupTunFd)
@@ -88,7 +86,7 @@ func (t *TunDevice) Create() (WGConfigurer, error) {
t.filteredDevice = newDeviceFilter(tunDevice) t.filteredDevice = newDeviceFilter(tunDevice)
log.Debug("Attaching to interface") 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. // 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 // this helps with support for the older NetBird clients that had a hardcoded direct mode
// t.device.DisableSomeRoamingForBrokenMobileSemantics() // t.device.DisableSomeRoamingForBrokenMobileSemantics()
@@ -118,7 +116,7 @@ func (t *TunDevice) Up() (*udpmux.UniversalUDPMuxDefault, error) {
return udpMux, nil return udpMux, nil
} }
func (t *TunDevice) Device() *device.Device { func (t *TunDevice) Device() *wgbackend.Device {
return t.device return t.device
} }
@@ -163,7 +161,7 @@ func (t *TunDevice) FilteredDevice() *FilteredDevice {
return t.filteredDevice return t.filteredDevice
} }
func (t *TunDevice) GetNet() *netstack.Net { func (t *TunDevice) GetNet() *wgbackend.NetstackNet {
return nil return nil
} }
+3 -4
View File
@@ -9,12 +9,11 @@ import (
"github.com/pion/transport/v3" "github.com/pion/transport/v3"
log "github.com/sirupsen/logrus" 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/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbnet "github.com/netbirdio/netbird/client/net" nbnet "github.com/netbirdio/netbird/client/net"
"github.com/netbirdio/netbird/sharedsock" "github.com/netbirdio/netbird/sharedsock"
) )
@@ -170,7 +169,7 @@ func (t *TunKernelDevice) DeviceName() string {
} }
// Device returns the wireguard device, not applicable for kernel devices // Device returns the wireguard device, not applicable for kernel devices
func (t *TunKernelDevice) Device() *device.Device { func (t *TunKernelDevice) Device() *wgbackend.Device {
return nil return nil
} }
@@ -183,7 +182,7 @@ func (t *TunKernelDevice) assignAddr() error {
return t.link.assignAddr(&t.address) return t.link.assignAddr(&t.address)
} }
func (t *TunKernelDevice) GetNet() *netstack.Net { func (t *TunKernelDevice) GetNet() *wgbackend.NetstackNet {
return nil return nil
} }
+8 -10
View File
@@ -6,20 +6,18 @@ import (
"net/netip" "net/netip"
log "github.com/sirupsen/logrus" 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/bind"
"github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/configurer"
nbnetstack "github.com/netbirdio/netbird/client/iface/netstack" nbnetstack "github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nbnet "github.com/netbirdio/netbird/client/net" nbnet "github.com/netbirdio/netbird/client/net"
) )
type Bind interface { type Bind interface {
conn.Bind wgbackend.Bind
GetICEMux() (*udpmux.UniversalUDPMuxDefault, error) GetICEMux() (*udpmux.UniversalUDPMuxDefault, error)
ActivityRecorder() *bind.ActivityRecorder ActivityRecorder() *bind.ActivityRecorder
EndpointManager EndpointManager
@@ -34,13 +32,13 @@ type TunNetstackDevice struct {
listenAddress string listenAddress string
bind Bind bind Bind
device *device.Device device *wgbackend.Device
filteredDevice *FilteredDevice filteredDevice *FilteredDevice
nsTun *nbnetstack.NetStackTun nsTun *nbnetstack.NetStackTun
udpMux *udpmux.UniversalUDPMuxDefault udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer 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 { 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.filteredDevice = newDeviceFilter(tunIface)
t.net = net t.net = net
t.device = device.NewDevice( t.device = wgbackend.NewDevice(
t.filteredDevice, t.filteredDevice,
t.bind, t.bind,
device.NewLogger(wgLogLevel(), "[netbird] "), wgbackend.NewLogger(wgLogLevel(), "[netbird] "),
) )
t.configurer = configurer.NewUSPConfigurerNoUAPI(t.device, t.name, t.bind.ActivityRecorder()) 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 // Device returns the wireguard device
func (t *TunNetstackDevice) Device() *device.Device { func (t *TunNetstackDevice) Device() *wgbackend.Device {
return t.device return t.device
} }
func (t *TunNetstackDevice) GetNet() *netstack.Net { func (t *TunNetstackDevice) GetNet() *wgbackend.NetstackNet {
return t.net return t.net
} }
+7 -9
View File
@@ -6,14 +6,12 @@ import (
"fmt" "fmt"
log "github.com/sirupsen/logrus" 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/bind"
"github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
type TunDevice struct { type TunDevice struct {
@@ -24,7 +22,7 @@ type TunDevice struct {
mtu uint16 mtu uint16
iceBind *bind.ICEBind iceBind *bind.ICEBind
device *device.Device device *wgbackend.Device
filteredDevice *FilteredDevice filteredDevice *FilteredDevice
udpMux *udpmux.UniversalUDPMuxDefault udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer configurer WGConfigurer
@@ -45,7 +43,7 @@ func NewTunDevice(name string, address wgaddr.Address, port int, key string, mtu
func (t *TunDevice) Create() (WGConfigurer, error) { func (t *TunDevice) Create() (WGConfigurer, error) {
log.Info("create tun interface") 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 { if err != nil {
log.Debugf("failed to create tun interface (%s, %d): %s", t.name, int(t.mtu), err) 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) return nil, fmt.Errorf("error creating tun device: %s", err)
@@ -53,10 +51,10 @@ func (t *TunDevice) Create() (WGConfigurer, error) {
t.filteredDevice = newDeviceFilter(tunIface) t.filteredDevice = newDeviceFilter(tunIface)
// We need to create a wireguard-go device and listen to configuration requests // We need to create a wireguard-go device and listen to configuration requests
t.device = device.NewDevice( t.device = wgbackend.NewDevice(
t.filteredDevice, t.filteredDevice,
t.iceBind, t.iceBind,
device.NewLogger(wgLogLevel(), "[netbird] "), wgbackend.NewLogger(wgLogLevel(), "[netbird] "),
) )
err = t.assignAddr() err = t.assignAddr()
@@ -132,7 +130,7 @@ func (t *TunDevice) FilteredDevice() *FilteredDevice {
} }
// Device returns the wireguard device // Device returns the wireguard device
func (t *TunDevice) Device() *device.Device { func (t *TunDevice) Device() *wgbackend.Device {
return t.device return t.device
} }
@@ -143,7 +141,7 @@ func (t *TunDevice) assignAddr() error {
return link.assignAddr(&t.address) return link.assignAddr(&t.address)
} }
func (t *TunDevice) GetNet() *netstack.Net { func (t *TunDevice) GetNet() *wgbackend.NetstackNet {
return nil return nil
} }
+9 -11
View File
@@ -6,15 +6,13 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.org/x/sys/windows" "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" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"github.com/netbirdio/netbird/client/iface/bind" "github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
const defaultWindowsGUIDSTring = "{f2f29e61-d91f-4d76-8151-119b20c4bdeb}" const defaultWindowsGUIDSTring = "{f2f29e61-d91f-4d76-8151-119b20c4bdeb}"
@@ -27,8 +25,8 @@ type TunDevice struct {
mtu uint16 mtu uint16
iceBind *bind.ICEBind iceBind *bind.ICEBind
device *device.Device device *wgbackend.Device
nativeTunDevice *tun.NativeTun nativeTunDevice *wgbackend.NativeTUN
filteredDevice *FilteredDevice filteredDevice *FilteredDevice
udpMux *udpmux.UniversalUDPMuxDefault udpMux *udpmux.UniversalUDPMuxDefault
configurer WGConfigurer configurer WGConfigurer
@@ -60,18 +58,18 @@ func (t *TunDevice) Create() (WGConfigurer, error) {
return nil, err return nil, err
} }
log.Info("create tun interface") 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 { if err != nil {
return nil, fmt.Errorf("error creating tun device: %s", err) return nil, fmt.Errorf("error creating tun device: %s", err)
} }
t.nativeTunDevice = tunDevice.(*tun.NativeTun) t.nativeTunDevice = tunDevice.(*wgbackend.NativeTUN)
t.filteredDevice = newDeviceFilter(tunDevice) t.filteredDevice = newDeviceFilter(tunDevice)
// We need to create a wireguard-go device and listen to configuration requests // We need to create a wireguard-go device and listen to configuration requests
t.device = device.NewDevice( t.device = wgbackend.NewDevice(
t.filteredDevice, t.filteredDevice,
t.iceBind, t.iceBind,
device.NewLogger(wgLogLevel(), "[netbird] "), wgbackend.NewLogger(wgLogLevel(), "[netbird] "),
) )
luid := winipcfg.LUID(t.nativeTunDevice.LUID()) luid := winipcfg.LUID(t.nativeTunDevice.LUID())
@@ -172,7 +170,7 @@ func (t *TunDevice) FilteredDevice() *FilteredDevice {
} }
// Device returns the wireguard device // Device returns the wireguard device
func (t *TunDevice) Device() *device.Device { func (t *TunDevice) Device() *wgbackend.Device {
return t.device return t.device
} }
@@ -209,7 +207,7 @@ func (t *TunDevice) assignAddr() error {
return luid.SetIPAddresses([]netip.Prefix{v4Prefix}) return luid.SetIPAddresses([]netip.Prefix{v4Prefix})
} }
func (t *TunDevice) GetNet() *netstack.Net { func (t *TunDevice) GetNet() *wgbackend.NetstackNet {
return nil return nil
} }
+13 -12
View File
@@ -10,7 +10,8 @@ import (
"time" "time"
log "github.com/sirupsen/logrus" 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 // closeAwareDevice wraps a tun.Device along with a flag
@@ -23,15 +24,15 @@ import (
// goroutine is awaited and closed only once. // goroutine is awaited and closed only once.
type closeAwareDevice struct { type closeAwareDevice struct {
isClosed atomic.Bool isClosed atomic.Bool
tun.Device wgbackend.TUN
closeEventCh chan struct{} closeEventCh chan struct{}
wg sync.WaitGroup wg sync.WaitGroup
closeOnce sync.Once closeOnce sync.Once
} }
func newClosableDevice(tunDevice tun.Device) *closeAwareDevice { func newClosableDevice(tunDevice wgbackend.TUN) *closeAwareDevice {
return &closeAwareDevice{ return &closeAwareDevice{
Device: tunDevice, TUN: tunDevice,
isClosed: atomic.Bool{}, isClosed: atomic.Bool{},
closeEventCh: make(chan struct{}), closeEventCh: make(chan struct{}),
} }
@@ -39,18 +40,18 @@ func newClosableDevice(tunDevice tun.Device) *closeAwareDevice {
// redirectEvents redirects the Events() method of the underlying tun.Device // redirectEvents redirects the Events() method of the underlying tun.Device
// to the given channel (RenewableTUN's events channel). // 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) c.wg.Add(1)
go func() { go func() {
defer c.wg.Done() defer c.wg.Done()
for { for {
select { select {
case ev, ok := <-c.Device.Events(): case ev, ok := <-c.TUN.Events():
if !ok { if !ok {
return return
} }
if ev == tun.EventDown { if ev == wgbackend.TUNEventDown {
continue continue
} }
@@ -72,7 +73,7 @@ func (c *closeAwareDevice) Close() (err error) {
c.closeOnce.Do(func() { c.closeOnce.Do(func() {
c.isClosed.Store(true) c.isClosed.Store(true)
close(c.closeEventCh) close(c.closeEventCh)
err = c.Device.Close() err = c.TUN.Close()
c.wg.Wait() c.wg.Wait()
}) })
@@ -87,7 +88,7 @@ type RenewableTUN struct {
devices []*closeAwareDevice devices []*closeAwareDevice
mu sync.Mutex mu sync.Mutex
cond *sync.Cond cond *sync.Cond
events chan tun.Event events chan wgbackend.TUNEvent
closed atomic.Bool closed atomic.Bool
} }
@@ -95,7 +96,7 @@ func NewRenewableTUN() *RenewableTUN {
r := &RenewableTUN{ r := &RenewableTUN{
devices: make([]*closeAwareDevice, 0), devices: make([]*closeAwareDevice, 0),
mu: sync.Mutex{}, mu: sync.Mutex{},
events: make(chan tun.Event, 16), events: make(chan wgbackend.TUNEvent, 16),
} }
r.cond = sync.NewCond(&r.mu) r.cond = sync.NewCond(&r.mu)
return r 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 // Events returns a channel that is fed events from the underlying tun.Device's events channel
// once it is added. // once it is added.
func (r *RenewableTUN) Events() <-chan tun.Event { func (r *RenewableTUN) Events() <-chan wgbackend.TUNEvent {
return r.events return r.events
} }
@@ -259,7 +260,7 @@ func (r *RenewableTUN) BatchSize() int {
return 1 return 1
} }
func (r *RenewableTUN) AddDevice(device tun.Device) { func (r *RenewableTUN) AddDevice(device wgbackend.TUN) {
r.mu.Lock() r.mu.Lock()
if r.closed.Load() { if r.closed.Load() {
r.mu.Unlock() r.mu.Unlock()
+3 -3
View File
@@ -3,13 +3,13 @@ package device
import ( import (
"os" "os"
"golang.zx2c4.com/wireguard/device" "github.com/netbirdio/netbird/client/iface/wgbackend"
) )
func wgLogLevel() int { func wgLogLevel() int {
if os.Getenv("NB_WG_DEBUG") == "true" { if os.Getenv("NB_WG_DEBUG") == "true" {
return device.LogLevelVerbose return wgbackend.LogLevelVerbose
} else { } else {
return device.LogLevelSilent return wgbackend.LogLevelSilent
} }
} }
+3 -6
View File
@@ -1,13 +1,10 @@
package iface package iface
import ( 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/device"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
type WGTunDevice interface { type WGTunDevice interface {
@@ -19,8 +16,8 @@ type WGTunDevice interface {
DeviceName() string DeviceName() string
Close() error Close() error
FilteredDevice() *device.FilteredDevice FilteredDevice() *device.FilteredDevice
Device() *wgdevice.Device Device() *wgbackend.Device
GetNet() *netstack.Net GetNet() *wgbackend.NetstackNet
RenewTun(fd int) error RenewTun(fd int) error
GetICEBind() device.EndpointManager GetICEBind() device.EndpointManager
} }
+3 -5
View File
@@ -10,17 +10,15 @@ import (
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/pion/transport/v3" "github.com/pion/transport/v3"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
wgdevice "golang.zx2c4.com/wireguard/device"
"github.com/netbirdio/netbird/client/errors" "github.com/netbirdio/netbird/client/errors"
"github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
nbnetstack "github.com/netbirdio/netbird/client/iface/netstack" nbnetstack "github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "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/client/iface/wgproxy"
"github.com/netbirdio/netbird/monotime" "github.com/netbirdio/netbird/monotime"
) )
@@ -271,7 +269,7 @@ func (w *WGIface) GetDevice() *device.FilteredDevice {
} }
// GetWGDevice returns the WireGuard device // GetWGDevice returns the WireGuard device
func (w *WGIface) GetWGDevice() *wgdevice.Device { func (w *WGIface) GetWGDevice() *wgbackend.Device {
return w.tun.Device() return w.tun.Device()
} }
@@ -344,7 +342,7 @@ func (w *WGIface) waitUntilRemoved() error {
} }
// GetNet returns the netstack.Net for the netstack device // GetNet returns the netstack.Net for the netstack device
func (w *WGIface) GetNet() *netstack.Net { func (w *WGIface) GetNet() *wgbackend.NetstackNet {
w.mu.Lock() w.mu.Lock()
defer w.mu.Unlock() defer w.mu.Unlock()
+17 -11
View File
@@ -8,12 +8,11 @@ import (
"testing" "testing"
"time" "time"
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/device"
nbnetstack "github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "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/client/iface/wgproxy"
) )
@@ -34,14 +33,14 @@ func (f *fakeTunDevice) Create() (device.WGConfigurer, error) {
func (f *fakeTunDevice) Up() (*udpmux.UniversalUDPMuxDefault, error) { func (f *fakeTunDevice) Up() (*udpmux.UniversalUDPMuxDefault, error) {
return nil, errors.New("not implemented") return nil, errors.New("not implemented")
} }
func (f *fakeTunDevice) UpdateAddr(wgaddr.Address) error { return nil } func (f *fakeTunDevice) UpdateAddr(wgaddr.Address) error { return nil }
func (f *fakeTunDevice) WgAddress() wgaddr.Address { return wgaddr.Address{} } func (f *fakeTunDevice) WgAddress() wgaddr.Address { return wgaddr.Address{} }
func (f *fakeTunDevice) MTU() uint16 { return DefaultMTU } func (f *fakeTunDevice) MTU() uint16 { return DefaultMTU }
func (f *fakeTunDevice) DeviceName() string { return "nb-close-test" } func (f *fakeTunDevice) DeviceName() string { return "nb-close-test" }
func (f *fakeTunDevice) FilteredDevice() *device.FilteredDevice { return nil } func (f *fakeTunDevice) FilteredDevice() *device.FilteredDevice { return nil }
func (f *fakeTunDevice) Device() *wgdevice.Device { return nil } func (f *fakeTunDevice) Device() *wgbackend.Device { return nil }
func (f *fakeTunDevice) GetNet() *netstack.Net { return nil } func (f *fakeTunDevice) GetNet() *wgbackend.NetstackNet { return nil }
func (f *fakeTunDevice) GetICEBind() device.EndpointManager { return nil } func (f *fakeTunDevice) GetICEBind() device.EndpointManager { return nil }
func (f *fakeTunDevice) Close() error { func (f *fakeTunDevice) Close() error {
close(f.closeStarted) 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 // WGIface.GetDevice() which needs the same mutex. The fix is to drop
// the lock before tun.Close() returns control. // the lock before tun.Close() returns control.
func TestWGIface_CloseReleasesMutexBeforeTunClose(t *testing.T) { 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{ tun := &fakeTunDevice{
closeStarted: make(chan struct{}), closeStarted: make(chan struct{}),
unblockClose: make(chan struct{}), unblockClose: make(chan struct{}),
@@ -105,7 +108,10 @@ func TestWGIface_CloseReleasesMutexBeforeTunClose(t *testing.T) {
close(tun.unblockClose) close(tun.unblockClose)
select { select {
case <-closeDone: case err := <-closeDone:
if err != nil {
t.Errorf("WGIface.Close() returned an error: %v", err)
}
case <-time.After(2 * time.Second): case <-time.After(2 * time.Second):
t.Fatal("WGIface.Close() never returned after the tun was unblocked") 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. // 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 is a generated GoMock package.
package mocks package mocks
@@ -8,8 +8,8 @@ import (
os "os" os "os"
reflect "reflect" reflect "reflect"
tun "github.com/netbirdio/wireguard-go/tun"
gomock "go.uber.org/mock/gomock" gomock "go.uber.org/mock/gomock"
tun "golang.zx2c4.com/wireguard/tun"
) )
// MockDevice is a mock of Device interface. // MockDevice is a mock of Device interface.
+4 -3
View File
@@ -5,7 +5,8 @@ import (
"net" "net"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
type Dialer interface { type Dialer interface {
@@ -13,10 +14,10 @@ type Dialer interface {
} }
type NSDialer struct { type NSDialer struct {
net *netstack.Net net *wgbackend.NetstackNet
} }
func NewNSDialer(net *netstack.Net) *NSDialer { func NewNSDialer(net *wgbackend.NetstackNet) *NSDialer {
return &NSDialer{ return &NSDialer{
net: net, net: net,
} }
+5 -5
View File
@@ -6,8 +6,8 @@ import (
"strconv" "strconv"
log "github.com/sirupsen/logrus" 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" const EnvSkipProxy = "NB_NETSTACK_SKIP_PROXY"
@@ -19,7 +19,7 @@ type NetStackTun struct { //nolint:revive
listenAddress string listenAddress string
proxy *Proxy proxy *Proxy
tundev tun.Device tundev wgbackend.TUN
} }
func NewNetStackTun(listenAddress string, addresses []netip.Addr, dnsAddress netip.Addr, mtu int) *NetStackTun { 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) { func (t *NetStackTun) Create() (wgbackend.TUN, *wgbackend.NetstackNet, error) {
nsTunDev, tunNet, err := netstack.CreateNetTUN( nsTunDev, tunNet, err := wgbackend.CreateNetTUN(
t.addresses, t.addresses,
[]netip.Addr{t.dnsAddress}, []netip.Addr{t.dnsAddress},
t.mtu) t.mtu)
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/pion/ice/v4" "github.com/netbirdio/ice/v4"
"github.com/pion/logging" "github.com/pion/logging"
"github.com/pion/stun/v3" "github.com/pion/stun/v3"
"github.com/pion/transport/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 Section -MainProgram
${INSTALL_TYPE} ${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 # SetOverwrite ifnewer
SetOutPath "$INSTDIR" SetOutPath "$INSTDIR"
!ifndef ARCH !ifndef ARCH
+3 -3
View File
@@ -8,10 +8,10 @@ import (
reflect "reflect" reflect "reflect"
gomock "go.uber.org/mock/gomock" 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/device"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
// MockIFaceMapper is a mock of IFaceMapper interface. // MockIFaceMapper is a mock of IFaceMapper interface.
@@ -108,10 +108,10 @@ func (mr *MockIFaceMapperMockRecorder) GetDevice() *gomock.Call {
} }
// GetWGDevice mocks base method. // GetWGDevice mocks base method.
func (m *MockIFaceMapper) GetWGDevice() *wgdevice.Device { func (m *MockIFaceMapper) GetWGDevice() *wgbackend.Device {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetWGDevice") ret := m.ctrl.Call(m, "GetWGDevice")
ret0, _ := ret[0].(*wgdevice.Device) ret0, _ := ret[0].(*wgbackend.Device)
return ret0 return ret0
} }
-7
View File
@@ -175,7 +175,6 @@ The IP addresses in the interfaces file are anonymized using the same process as
Configuration 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: 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 - ManagementURL
- AdminURL
- NATExternalIPs - NATExternalIPs
- CustomDNSAddress - CustomDNSAddress
@@ -603,9 +602,6 @@ func (g *BundleGenerator) addConfig() error {
if g.internalConfig.ManagementURL != nil { if g.internalConfig.ManagementURL != nil {
configContent.WriteString(fmt.Sprintf("ManagementURL: %s\n", g.anonymizer.AnonymizeURI(g.internalConfig.ManagementURL.String()))) 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))) configContent.WriteString(fmt.Sprintf("NATExternalIPs: %v\n", anonymizeNATExternalIPs(g.internalConfig.NATExternalIPs, g.anonymizer)))
if g.internalConfig.CustomDNSAddress != "" { if g.internalConfig.CustomDNSAddress != "" {
configContent.WriteString(fmt.Sprintf("CustomDNSAddress: %s\n", g.anonymizer.AnonymizeString(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 { if g.internalConfig.ManagementURL != nil {
configContent.WriteString(fmt.Sprintf("ManagementURL: %s\n", g.internalConfig.ManagementURL.String())) 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)) configContent.WriteString(fmt.Sprintf("NATExternalIPs: %v\n", g.internalConfig.NATExternalIPs))
if g.internalConfig.CustomDNSAddress != "" { if g.internalConfig.CustomDNSAddress != "" {
configContent.WriteString(fmt.Sprintf("CustomDNSAddress: %s\n", 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") mURL, _ := url.Parse("https://api.example.com:443")
aURL, _ := url.Parse("https://admin.example.com:443")
bTrue := true bTrue := true
iVal := 42 iVal := 42
cfg := &profilemanager.Config{ cfg := &profilemanager.Config{
PrivateKey: "priv", PrivateKey: "priv",
PreSharedKey: "psk", PreSharedKey: "psk",
ManagementURL: mURL, ManagementURL: mURL,
AdminURL: aURL,
WgIface: "wt0", WgIface: "wt0",
WgPort: 51820, WgPort: 51820,
NetworkMonitor: &bTrue, NetworkMonitor: &bTrue,
@@ -947,7 +945,7 @@ func TestAddConfig_AllFieldsCovered(t *testing.T) {
} }
// renderAddConfigSpecific renders the fields handled by the anonymize/non-anonymize // 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 // addCommonConfigFields covers the rest. Keeping this in the test mirrors the
// production shape without needing to write an actual zip. // production shape without needing to write an actual zip.
func renderAddConfigSpecific(g *BundleGenerator) string { func renderAddConfigSpecific(g *BundleGenerator) string {
@@ -956,9 +954,6 @@ func renderAddConfigSpecific(g *BundleGenerator) string {
if g.internalConfig.ManagementURL != nil { if g.internalConfig.ManagementURL != nil {
sb.WriteString("ManagementURL: " + g.anonymizer.AnonymizeURI(g.internalConfig.ManagementURL.String()) + "\n") 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") sb.WriteString("NATExternalIPs: x\n")
if g.internalConfig.CustomDNSAddress != "" { if g.internalConfig.CustomDNSAddress != "" {
sb.WriteString("CustomDNSAddress: " + g.anonymizer.AnonymizeString(g.internalConfig.CustomDNSAddress) + "\n") sb.WriteString("CustomDNSAddress: " + g.anonymizer.AnonymizeString(g.internalConfig.CustomDNSAddress) + "\n")
@@ -967,9 +962,6 @@ func renderAddConfigSpecific(g *BundleGenerator) string {
if g.internalConfig.ManagementURL != nil { if g.internalConfig.ManagementURL != nil {
sb.WriteString("ManagementURL: " + g.internalConfig.ManagementURL.String() + "\n") 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") sb.WriteString("NATExternalIPs: x\n")
if g.internalConfig.CustomDNSAddress != "" { if g.internalConfig.CustomDNSAddress != "" {
sb.WriteString("CustomDNSAddress: " + g.internalConfig.CustomDNSAddress + "\n") 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) { func parseResolvConfFile(resolvConfFile string) (*resolvConf, error) {
rconf := &resolvConf{ rconf := newResolvConf()
searchDomains: make([]string, 0),
nameServers: make([]netip.Addr, 0),
others: make([]string, 0),
}
file, err := os.Open(resolvConfFile) file, err := os.Open(resolvConfFile)
if err != nil { 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 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 { if len(cur) == 0 {
return rconf, fmt.Errorf("file is empty") return rconf, fmt.Errorf("file is empty")
} }
for _, line := range strings.Split(string(cur), "\n") { for _, line := range strings.Split(string(cur), "\n") {
line = strings.TrimSpace(line) parseResolvConfLine(rconf, 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)
}
} }
return rconf, nil 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 package dns
import ( import (
"bytes"
"net/netip" "net/netip"
"os"
"path" "path"
"path/filepath" "path/filepath"
"sync" "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 { type repair struct {
operationFile string 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 { if f.inotify != nil {
return 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) log.Errorf("failed to start inotify watcher for resolv.conf: %s", err)
return 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.inotify = inotify
f.inotifyWg.Add(1) f.inotifyWg.Add(1)
go func() { go func() {
defer f.inotifyWg.Done() 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) // A change that landed before the watch was armed produced no event.
lastWritten := f.checkAndRepair(nbSearchDomains, nbNameserverIP, written, stateManager)
rConf, err := parseResolvConfFile(f.operationFile) for {
if err != nil { select {
log.Warnf("failed to parse resolv conf: %s", err) case event, ok := <-inotify.Events:
continue if !ok {
} return
}
log.Debugf("check resolv.conf parameters: %s", rConf) if !f.isEventRelevant(event) {
if !isNbParamsMissing(nbSearchDomains, nbNameserverIP, rConf) { continue
log.Tracef("resolv.conf still correct, skip the update") }
continue case err, ok := <-inotify.Errors:
} if !ok {
log.Info("broken params in resolv.conf, repairing it...") return
}
err = f.inotify.Remove(f.watchDir) // fsnotify stops delivering events until an error is received, and an
if err != nil { // error such as a queue overflow means events were dropped.
log.Errorf("failed to rm inotify watch for resolv.conf: %s", err) log.Warnf("resolv.conf inotify error, checking the file: %v", 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
} }
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 { if err != nil {
log.Errorf("failed to add inotify watch for resolv.conf: %s", err) log.Warnf("failed to read resolv.conf: %s", err)
return 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() { func (f *repair) stopWatchFileChanges() {
+159 -6
View File
@@ -4,12 +4,19 @@ package dns
import ( import (
"context" "context"
"fmt"
"net/netip" "net/netip"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"sync/atomic"
"testing" "testing"
"time" "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/client/internal/statemanager"
"github.com/netbirdio/netbird/util" "github.com/netbirdio/netbird/util"
) )
@@ -106,14 +113,14 @@ nameserver 8.8.8.8`,
var changed bool var changed bool
ctx, cancel := context.WithTimeout(context.Background(), time.Second) 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 changed = true
cancel() cancel()
return nil return nil, nil
} }
r := newRepair(operationFile, updateFn) 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) err = os.WriteFile(operationFile, []byte(tt.touchedConfContent), 0755)
if err != nil { if err != nil {
@@ -153,14 +160,14 @@ searchdomain netbird.cloud something`
var changed bool var changed bool
ctx, cancel := context.WithTimeout(context.Background(), time.Second) 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 changed = true
cancel() cancel()
return nil return nil, nil
} }
r := newRepair(tmpLink, updateFn) 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) err = os.WriteFile(tmpLink, []byte(modifyContent), 0755)
if err != nil { if err != nil {
@@ -175,3 +182,149 @@ searchdomain netbird.cloud something`
t.Errorf("unexpected result: want: %v, got: %v", true, false) 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() f.repair.stopWatchFileChanges()
err = f.updateConfig(nbSearchDomains, f.nbNameserverIP, resolvConf, stateManager) written, err := f.updateConfig(nbSearchDomains, f.nbNameserverIP, resolvConf, stateManager)
if err != nil { if err != nil {
return err return err
} }
f.repair.watchFileChanges(nbSearchDomains, f.nbNameserverIP, stateManager) f.repair.watchFileChanges(nbSearchDomains, f.nbNameserverIP, written, stateManager)
return nil return nil
} }
@@ -74,7 +74,7 @@ func (f *fileConfigurator) getOriginalNameservers() []netip.Addr {
return f.originalNameservers 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) searchDomainList := mergeSearchDomains(nbSearchDomains, cfg.searchDomains)
buf := prepareResolvConfContent( buf := prepareResolvConfContent(
@@ -90,7 +90,7 @@ func (f *fileConfigurator) updateConfig(nbSearchDomains []string, nbNameserverIP
if restoreErr != nil { if restoreErr != nil {
log.Errorf("attempt to restore default file failed with error: %s", err) 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) 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) log.Errorf("failed to create unclean shutdown resolv.conf backup: %s", err)
} }
return nil return buf.Bytes(), nil
} }
func (f *fileConfigurator) restoreHostDNS() error { func (f *fileConfigurator) restoreHostDNS() error {
+3 -2
View File
@@ -7,14 +7,15 @@ import (
"github.com/google/gopacket" "github.com/google/gopacket"
"github.com/google/gopacket/layers" "github.com/google/gopacket/layers"
"github.com/miekg/dns" "github.com/miekg/dns"
"golang.zx2c4.com/wireguard/tun"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
type responseWriter struct { type responseWriter struct {
local net.Addr local net.Addr
remote net.Addr remote net.Addr
packet gopacket.Packet packet gopacket.Packet
device tun.Device device wgbackend.TUN
} }
// LocalAddr returns the net.Addr of the server // 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/assert"
"github.com/stretchr/testify/mock" "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/firewall/uspfilter" "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/configurer"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr" "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/local"
"github.com/netbirdio/netbird/client/internal/dns/test" "github.com/netbirdio/netbird/client/internal/dns/test"
"github.com/netbirdio/netbird/client/internal/dns/types" "github.com/netbirdio/netbird/client/internal/dns/types"
@@ -83,7 +83,7 @@ func (w *mocWGIface) GetStats(_ string) (configurer.WGStats, error) {
return configurer.WGStats{}, nil return configurer.WGStats{}, nil
} }
func (w *mocWGIface) GetNet() *netstack.Net { func (w *mocWGIface) GetNet() *wgbackend.NetstackNet {
return nil return nil
} }
+2 -2
View File
@@ -108,7 +108,7 @@ func (s *ServiceViaMemory) filterDNSTraffic() error {
if s.tcpDNS == nil { if s.tcpDNS == nil {
if dev := s.wgInterface.GetDevice(); dev != nil { if dev := s.wgInterface.GetDevice(); dev != nil {
// MTU only affects TCP segment sizing; DNS messages are small so this has no practical impact. // 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{ writer := &responseWriter{
remote: remoteAddrFromPacket(packet), remote: remoteAddrFromPacket(packet),
packet: packet, packet: packet,
device: dev.Device, device: dev.TUN,
} }
go s.dnsMux.ServeDNS(writer, msg) go s.dnsMux.ServeDNS(writer, msg)
return true return true
+5 -4
View File
@@ -12,7 +12,6 @@ import (
"github.com/miekg/dns" "github.com/miekg/dns"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun"
"gvisor.dev/gvisor/pkg/buffer" "gvisor.dev/gvisor/pkg/buffer"
"gvisor.dev/gvisor/pkg/tcpip" "gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
@@ -21,6 +20,8 @@ import (
"gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp" "gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
"gvisor.dev/gvisor/pkg/waiter" "gvisor.dev/gvisor/pkg/waiter"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
const ( const (
@@ -38,7 +39,7 @@ type tcpDNSServer struct {
s *stack.Stack s *stack.Stack
ep *dnsEndpoint ep *dnsEndpoint
mux *dns.ServeMux mux *dns.ServeMux
tunDev tun.Device tunDev wgbackend.TUN
ip netip.Addr ip netip.Addr
port uint16 port uint16
mtu uint16 mtu uint16
@@ -49,7 +50,7 @@ type tcpDNSServer struct {
timer *time.Timer 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{ return &tcpDNSServer{
mux: mux, mux: mux,
tunDev: tunDev, 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. // dnsEndpoint implements stack.LinkEndpoint for writing packets back via the tun device.
type dnsEndpoint struct { type dnsEndpoint struct {
dispatcher stack.NetworkDispatcher dispatcher stack.NetworkDispatcher
tunDev tun.Device tunDev wgbackend.TUN
mtu atomic.Uint32 mtu atomic.Uint32
} }
+3 -3
View File
@@ -44,10 +44,10 @@ import (
"github.com/miekg/dns" "github.com/miekg/dns"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/wgaddr" "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/resutil"
"github.com/netbirdio/netbird/client/internal/dns/types" "github.com/netbirdio/netbird/client/internal/dns/types"
"github.com/netbirdio/netbird/client/internal/peer" "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. // ExchangeWithNetstack performs a DNS exchange using netstack for dialing.
// This is needed when netstack is enabled to reach peer IPs through the tunnel. // 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 request came in over TCP, go straight to TCP upstream
if dnsProtocolFromContext(ctx) == protoTCP { if dnsProtocolFromContext(ctx) == protoTCP {
rm, err := netstackExchange(ctx, nsNet, r, upstream, 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 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) conn, err := nsNet.DialContext(ctx, network, upstream)
if err != nil { if err != nil {
return nil, fmt.Errorf("with %s: %w", network, err) return nil, fmt.Errorf("with %s: %w", network, err)
+2 -2
View File
@@ -9,15 +9,15 @@ import (
"time" "time"
"github.com/miekg/dns" "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/client/internal/peer"
"github.com/netbirdio/netbird/shared/management/domain" "github.com/netbirdio/netbird/shared/management/domain"
) )
type upstreamResolver struct { type upstreamResolver struct {
*upstreamResolverBase *upstreamResolverBase
nsNet *netstack.Net nsNet *wgbackend.NetstackNet
} }
func newUpstreamResolver( func newUpstreamResolver(
+2 -2
View File
@@ -13,10 +13,10 @@ import (
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/dns/test" "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) IsUserspaceBind() bool { return false }
func (m *mockNetstackProvider) GetFilter() device.PacketFilter { return nil } func (m *mockNetstackProvider) GetFilter() device.PacketFilter { return nil }
func (m *mockNetstackProvider) GetDevice() *device.FilteredDevice { 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) { func (m *mockNetstackProvider) GetInterfaceGUIDString() (string, error) {
return "", nil return "", nil
} }
+2 -3
View File
@@ -5,10 +5,9 @@ package dns
import ( import (
"net" "net"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
// WGIface defines subset methods of interface required for manager // WGIface defines subset methods of interface required for manager
@@ -19,5 +18,5 @@ type WGIface interface {
IsUserspaceBind() bool IsUserspaceBind() bool
GetFilter() device.PacketFilter GetFilter() device.PacketFilter
GetDevice() *device.FilteredDevice GetDevice() *device.FilteredDevice
GetNet() *netstack.Net GetNet() *wgbackend.NetstackNet
} }
+2 -3
View File
@@ -1,10 +1,9 @@
package dns package dns
import ( import (
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
// WGIface defines subset methods of interface required for manager // WGIface defines subset methods of interface required for manager
@@ -14,6 +13,6 @@ type WGIface interface {
IsUserspaceBind() bool IsUserspaceBind() bool
GetFilter() device.PacketFilter GetFilter() device.PacketFilter
GetDevice() *device.FilteredDevice GetDevice() *device.FilteredDevice
GetNet() *netstack.Net GetNet() *wgbackend.NetstackNet
GetInterfaceGUIDString() (string, error) GetInterfaceGUIDString() (string, error)
} }
+4 -4
View File
@@ -14,10 +14,10 @@ import (
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/miekg/dns" "github.com/miekg/dns"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun/netstack"
nberrors "github.com/netbirdio/netbird/client/errors" nberrors "github.com/netbirdio/netbird/client/errors"
firewall "github.com/netbirdio/netbird/client/firewall/manager" 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/dns/resutil"
"github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/route" "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 { func (f *DNSForwarder) Listen(entries []*ForwarderEntry) error {
var netstackNet *netstack.Net var netstackNet *wgbackend.NetstackNet
if f.wgIface != nil { if f.wgIface != nil {
netstackNet = f.wgIface.GetNet() netstackNet = f.wgIface.GetNet()
} }
@@ -153,7 +153,7 @@ func (f *DNSForwarder) Listen(entries []*ForwarderEntry) error {
return <-errCh 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 { if netstackNet != nil {
return netstackNet.ListenUDPAddrPort(f.listenAddress) 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)) 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 { if netstackNet != nil {
return netstackNet.ListenTCPAddrPort(f.listenAddress) return netstackNet.ListenTCPAddrPort(f.listenAddress)
} }
+2 -2
View File
@@ -9,11 +9,11 @@ import (
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/tun/netstack"
nberrors "github.com/netbirdio/netbird/client/errors" nberrors "github.com/netbirdio/netbird/client/errors"
firewall "github.com/netbirdio/netbird/client/firewall/manager" firewall "github.com/netbirdio/netbird/client/firewall/manager"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
nftypes "github.com/netbirdio/netbird/client/internal/netflow/types" nftypes "github.com/netbirdio/netbird/client/internal/netflow/types"
"github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peer"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
@@ -28,7 +28,7 @@ const (
// wgIface defines the interface for WireGuard interface operations needed by the DNS forwarder. // wgIface defines the interface for WireGuard interface operations needed by the DNS forwarder.
type wgIface interface { type wgIface interface {
GetNet() *netstack.Net GetNet() *wgbackend.NetstackNet
Address() wgaddr.Address Address() wgaddr.Address
} }
+4 -5
View File
@@ -19,11 +19,9 @@ import (
"time" "time"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/pion/ice/v4" "github.com/netbirdio/ice/v4"
"github.com/pion/stun/v3" "github.com/pion/stun/v3"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/anonymize" "github.com/netbirdio/netbird/client/anonymize"
@@ -37,6 +35,7 @@ import (
nbnetstack "github.com/netbirdio/netbird/client/iface/netstack" nbnetstack "github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "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/acl"
"github.com/netbirdio/netbird/client/internal/certproof" "github.com/netbirdio/netbird/client/internal/certproof"
"github.com/netbirdio/netbird/client/internal/debug" "github.com/netbirdio/netbird/client/internal/debug"
@@ -254,7 +253,7 @@ type Engine struct {
// wgInterface. Reaching the device through wgInterface requires // wgInterface. Reaching the device through wgInterface requires
// syncMsgMux, which handleSync holds while it adds and removes peers; // syncMsgMux, which handleSync holds while it adds and removes peers;
// SetPerformance must stay reachable exactly when that work is stuck. // SetPerformance must stay reachable exactly when that work is stuck.
wgDevice atomic.Pointer[wgdevice.Device] wgDevice atomic.Pointer[wgbackend.Device]
udpMux *udpmux.UniversalUDPMuxDefault udpMux *udpmux.UniversalUDPMuxDefault
@@ -2788,7 +2787,7 @@ func (e *Engine) stopDNSForwarder() {
e.dnsForwardMgr = nil e.dnsForwardMgr = nil
} }
func (e *Engine) GetNet() (*netstack.Net, error) { func (e *Engine) GetNet() (*wgbackend.NetstackNet, error) {
e.syncMsgMux.Lock() e.syncMsgMux.Lock()
intf := e.wgInterface intf := e.wgInterface
e.syncMsgMux.Unlock() e.syncMsgMux.Unlock()
+5 -6
View File
@@ -14,10 +14,9 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "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/internal/stdnet"
"github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface"
@@ -61,11 +60,11 @@ type MockWGIface struct {
SetFilterFunc func(filter device.PacketFilter) error SetFilterFunc func(filter device.PacketFilter) error
GetFilterFunc func() device.PacketFilter GetFilterFunc func() device.PacketFilter
GetDeviceFunc func() *device.FilteredDevice GetDeviceFunc func() *device.FilteredDevice
GetWGDeviceFunc func() *wgdevice.Device GetWGDeviceFunc func() *wgbackend.Device
GetStatsFunc func() (map[string]configurer.WGStats, error) GetStatsFunc func() (map[string]configurer.WGStats, error)
GetInterfaceGUIDStringFunc func() (string, error) GetInterfaceGUIDStringFunc func() (string, error)
GetProxyFunc func() wgproxy.Proxy GetProxyFunc func() wgproxy.Proxy
GetNetFunc func() *netstack.Net GetNetFunc func() *wgbackend.NetstackNet
LastActivitiesFunc func() map[string]monotime.Time LastActivitiesFunc func() map[string]monotime.Time
} }
@@ -149,7 +148,7 @@ func (m *MockWGIface) GetDevice() *device.FilteredDevice {
return m.GetDeviceFunc() return m.GetDeviceFunc()
} }
func (m *MockWGIface) GetWGDevice() *wgdevice.Device { func (m *MockWGIface) GetWGDevice() *wgbackend.Device {
return m.GetWGDeviceFunc() return m.GetWGDeviceFunc()
} }
@@ -161,7 +160,7 @@ func (m *MockWGIface) GetProxy() wgproxy.Proxy {
return m.GetProxyFunc() return m.GetProxyFunc()
} }
func (m *MockWGIface) GetNet() *netstack.Net { func (m *MockWGIface) GetNet() *wgbackend.NetstackNet {
return m.GetNetFunc() return m.GetNetFunc()
} }
+3 -4
View File
@@ -5,14 +5,13 @@ import (
"net/netip" "net/netip"
"time" "time"
wgdevice "golang.zx2c4.com/wireguard/device"
"golang.zx2c4.com/wireguard/tun/netstack"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/client/iface/configurer" "github.com/netbirdio/netbird/client/iface/configurer"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/udpmux" "github.com/netbirdio/netbird/client/iface/udpmux"
"github.com/netbirdio/netbird/client/iface/wgaddr" "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/client/iface/wgproxy"
"github.com/netbirdio/netbird/monotime" "github.com/netbirdio/netbird/monotime"
) )
@@ -37,9 +36,9 @@ type wgIfaceBase interface {
SetFilter(filter device.PacketFilter) error SetFilter(filter device.PacketFilter) error
GetFilter() device.PacketFilter GetFilter() device.PacketFilter
GetDevice() *device.FilteredDevice GetDevice() *device.FilteredDevice
GetWGDevice() *wgdevice.Device GetWGDevice() *wgbackend.Device
GetStats() (map[string]configurer.WGStats, error) GetStats() (map[string]configurer.WGStats, error)
GetNet() *netstack.Net GetNet() *wgbackend.NetstackNet
FullStats() (*configurer.Stats, error) FullStats() (*configurer.Stats, error)
LastActivities() map[string]monotime.Time LastActivities() map[string]monotime.Time
SetPresharedKey(peerKey string, psk wgtypes.Key, updateOnly bool) error SetPresharedKey(peerKey string, psk wgtypes.Key, updateOnly bool) error
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/pion/ice/v4" "github.com/netbirdio/ice/v4"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+1 -1
View File
@@ -8,7 +8,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/pion/ice/v4" "github.com/netbirdio/ice/v4"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
icemaker "github.com/netbirdio/netbird/client/internal/peer/ice" icemaker "github.com/netbirdio/netbird/client/internal/peer/ice"
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/pion/ice/v4" "github.com/netbirdio/ice/v4"
"github.com/pion/logging" "github.com/pion/logging"
"github.com/pion/randutil" "github.com/pion/randutil"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
+1 -1
View File
@@ -1,7 +1,7 @@
package ice package ice
import ( import (
"github.com/pion/ice/v4" "github.com/netbirdio/ice/v4"
"github.com/netbirdio/netbird/client/internal/stdnet" "github.com/netbirdio/netbird/client/internal/stdnet"
) )
+1 -1
View File
@@ -1,7 +1,7 @@
package peer package peer
import ( import (
"github.com/pion/ice/v4" "github.com/netbirdio/ice/v4"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes" "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/pion/ice/v4" "github.com/netbirdio/ice/v4"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface"
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/pion/ice/v4" "github.com/netbirdio/ice/v4"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
+3 -47
View File
@@ -36,7 +36,8 @@ const (
DefaultManagementURL = "https://api.netbird.io:443" DefaultManagementURL = "https://api.netbird.io:443"
// oldDefaultManagementURL points to the NetBird's old cloud management endpoint // oldDefaultManagementURL points to the NetBird's old cloud management endpoint
oldDefaultManagementURL = "https://api.wiretrustee.com:443" 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" DefaultAdminURL = "https://app.netbird.io:443"
) )
@@ -60,7 +61,6 @@ var DefaultInterfaceBlacklist = []string{
// ConfigInput carries configuration changes to the client // ConfigInput carries configuration changes to the client
type ConfigInput struct { type ConfigInput struct {
ManagementURL string ManagementURL string
AdminURL string
ConfigPath string ConfigPath string
StateFilePath string StateFilePath string
PreSharedKey *string PreSharedKey *string
@@ -114,7 +114,6 @@ type Config struct {
PrivateKey string PrivateKey string
PreSharedKey string PreSharedKey string
ManagementURL *url.URL ManagementURL *url.URL
AdminURL *url.URL
WgIface string WgIface string
WgPort int WgPort int
NetworkMonitor *bool 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 { if input.WireguardPort != nil && *input.WireguardPort != config.WgPort {
log.Infof("updating Wireguard port %d (old value %d)", log.Infof("updating Wireguard port %d (old value %d)",
*input.WireguardPort, config.WgPort) *input.WireguardPort, config.WgPort)
@@ -1027,8 +1003,7 @@ func ParseServiceURL(serviceName, serviceURL string) (*url.URL, error) {
// matches only another nil one. // matches only another nil one.
// //
// The path plays no part: a management URL is dialed, and only its host and // 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 // port are. util.SameServiceURL is this comparison plus the path.
// what SameServiceURLIncludingPath needs and delegates to.
func SameServiceURL(a, b *url.URL) bool { func SameServiceURL(a, b *url.URL) bool {
if a == nil || b == nil { if a == nil || b == nil {
return a == b return a == b
@@ -1039,25 +1014,6 @@ func SameServiceURL(a, b *url.URL) bool {
util.ServiceURLPort(a) == util.ServiceURLPort(b) 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) { func parseURL(serviceName, serviceURL string) (*url.URL, error) {
parsedMgmtURL, err := url.ParseRequestURI(serviceURL) parsedMgmtURL, err := url.ParseRequestURI(serviceURL)
if err != nil { if err != nil {
@@ -36,17 +36,14 @@ func TestGetConfig(t *testing.T) {
} }
assert.Equal(t, config.ManagementURL.String(), DefaultManagementURL) assert.Equal(t, config.ManagementURL.String(), DefaultManagementURL)
assert.Equal(t, config.AdminURL.String(), DefaultAdminURL)
managementURL := "https://test.management.url:33071" managementURL := "https://test.management.url:33071"
adminURL := "https://app.admin.url:443"
path := filepath.Join(t.TempDir(), "config.json") path := filepath.Join(t.TempDir(), "config.json")
preSharedKey := "preSharedKey" preSharedKey := "preSharedKey"
// case 2: new config has to be generated // case 2: new config has to be generated
config, err = UpdateOrCreateConfig(ConfigInput{ config, err = UpdateOrCreateConfig(ConfigInput{
ManagementURL: managementURL, ManagementURL: managementURL,
AdminURL: adminURL,
ConfigPath: path, ConfigPath: path,
PreSharedKey: &preSharedKey, PreSharedKey: &preSharedKey,
}) })
@@ -64,7 +61,6 @@ func TestGetConfig(t *testing.T) {
// case 3: existing config -> fetch it // case 3: existing config -> fetch it
config, err = UpdateOrCreateConfig(ConfigInput{ config, err = UpdateOrCreateConfig(ConfigInput{
ManagementURL: managementURL, ManagementURL: managementURL,
AdminURL: adminURL,
ConfigPath: path, ConfigPath: path,
PreSharedKey: &preSharedKey, PreSharedKey: &preSharedKey,
}) })
@@ -79,7 +75,6 @@ func TestGetConfig(t *testing.T) {
newManagementURL := "https://test.newManagement.url:33071" newManagementURL := "https://test.newManagement.url:33071"
config, err = UpdateOrCreateConfig(ConfigInput{ config, err = UpdateOrCreateConfig(ConfigInput{
ManagementURL: newManagementURL, ManagementURL: newManagementURL,
AdminURL: adminURL,
ConfigPath: path, ConfigPath: path,
PreSharedKey: &preSharedKey, PreSharedKey: &preSharedKey,
}) })
@@ -164,7 +159,6 @@ func TestNewProfileDefaults(t *testing.T) {
require.NoError(t, err, "should create new config") require.NoError(t, err, "should create new config")
assert.Equal(t, DefaultManagementURL, config.ManagementURL.String(), "ManagementURL should have default") 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.PrivateKey, "PrivateKey should be generated")
assert.NotEmpty(t, config.SSHKey, "SSHKey should be generated") assert.NotEmpty(t, config.SSHKey, "SSHKey should be generated")
assert.Equal(t, iface.WgInterfaceDefault, config.WgIface, "WgIface should have default") 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) require.NotEmpty(t, cfg.SSHKey)
} }
// The admin panel is opened, not dialed, so its path identifies it. Comparing // Profiles written before the admin URL was dropped still carry an AdminURL key.
// it as a bare endpoint left a custom panel URL unable to change. // It must not break loading, and the next write must drop it.
func TestAdminURLPathIsPartOfTheIdentity(t *testing.T) { func TestLegacyAdminURLIsIgnored(t *testing.T) {
path := filepath.Join(t.TempDir(), "panel.json") path := filepath.Join(t.TempDir(), "legacy.json")
_, err := UpdateOrCreateConfig(ConfigInput{ _, err := UpdateOrCreateConfig(ConfigInput{ConfigPath: path})
ConfigPath: path,
AdminURL: "https://app.example.com/netbird",
})
require.NoError(t, err) require.NoError(t, err)
cfg, err := GetExistingConfig(path) raw, err := os.ReadFile(path)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "https://app.example.com:443/netbird", cfg.AdminURL.String()) var stored map[string]json.RawMessage
require.NoError(t, json.Unmarshal(raw, &stored))
// Equivalent spellings of the same panel are still not a change. stored["AdminURL"] = json.RawMessage(`{"Scheme":"https","Host":"panel.example.com:443","Path":"/netbird"}`)
for _, same := range []string{ rewritten, err := json.Marshal(stored)
"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"})
require.NoError(t, err) 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.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, // unsetOnDisk rewrites the stored config so the named fields carry a JSON null,
@@ -4,10 +4,9 @@ import (
"net" "net"
"net/netip" "net/netip"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
) )
type wgIfaceBase interface { type wgIfaceBase interface {
@@ -20,5 +19,5 @@ type wgIfaceBase interface {
IsUserspaceBind() bool IsUserspaceBind() bool
GetFilter() device.PacketFilter GetFilter() device.PacketFilter
GetDevice() *device.FilteredDevice GetDevice() *device.FilteredDevice
GetNet() *netstack.Net GetNet() *wgbackend.NetstackNet
} }
@@ -10,10 +10,10 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/iface/device"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
"github.com/netbirdio/netbird/client/internal/routemanager/refcounter" "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) IsUserspaceBind() bool { return false }
func (m *reconcileWGMock) GetFilter() device.PacketFilter { return nil } func (m *reconcileWGMock) GetFilter() device.PacketFilter { return nil }
func (m *reconcileWGMock) GetDevice() *device.FilteredDevice { 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 // 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. // 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 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) { func (p *Preferences) GetAdminURL() (string, error) {
if p.configInput.AdminURL != "" { return profilemanager.DefaultAdminURL, nil
return p.configInput.AdminURL, nil
}
cfg, err := profilemanager.ReadConfigOrDefault(p.configInput.ConfigPath)
if err != nil {
return "", err
}
return cfg.AdminURL.String(), err
} }
// SetAdminURL store the given url and wait for commit // SetAdminURL is a no-op kept for API compatibility.
func (p *Preferences) SetAdminURL(url string) { //
p.configInput.AdminURL = url // 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 // HasPreSharedKey reports whether a pre-shared key is staged, persisted, or
// enforced by MDM; the key itself is never handed to the native layer. // 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") stateFile := filepath.Join(t.TempDir(), "state.json")
p := NewPreferences(cfgFile, stateFile) p := NewPreferences(cfgFile, stateFile)
// The admin URL is deprecated: setting it is a no-op.
p.SetAdminURL(exampleString) p.SetAdminURL(exampleString)
resp, err := p.GetAdminURL() resp, err := p.GetAdminURL()
if err != nil { if err != nil {
t.Fatalf("failed to read admin url: %s", err) t.Fatalf("failed to read admin url: %s", err)
} }
if resp != exampleString { if resp != profilemanager.DefaultAdminURL {
t.Errorf("unexpected admin url: %s", resp) 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) t.Fatalf("failed to read admin url: %s", err)
} }
if resp != exampleURL { if resp != profilemanager.DefaultAdminURL {
t.Errorf("unexpected admin url: %s", resp) 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 != "" { if msg.ManagementUrl != "" {
config.ManagementURL = msg.ManagementUrl config.ManagementURL = msg.ManagementUrl
} }
if msg.AdminURL != "" {
config.AdminURL = msg.AdminURL
}
if msg.InterfaceName != nil { if msg.InterfaceName != nil {
config.InterfaceName = msg.InterfaceName config.InterfaceName = msg.InterfaceName
} }
@@ -2413,7 +2410,6 @@ func (s *Server) GetConfig(ctx context.Context, req *proto.GetConfigRequest) (*p
// overlay (Loader lives outside profilemanager). // overlay (Loader lives outside profilemanager).
cfg.ApplyMDMPolicy(s.mdmLoader.Load()) cfg.ApplyMDMPolicy(s.mdmLoader.Load())
managementURL := cfg.ManagementURL managementURL := cfg.ManagementURL
adminURL := cfg.AdminURL
var preSharedKey = cfg.PreSharedKey var preSharedKey = cfg.PreSharedKey
if preSharedKey != "" { if preSharedKey != "" {
@@ -2469,7 +2465,6 @@ func (s *Server) GetConfig(ctx context.Context, req *proto.GetConfigRequest) (*p
return &proto.GetConfigResponse{ return &proto.GetConfigResponse{
ManagementUrl: managementURL.String(), ManagementUrl: managementURL.String(),
PreSharedKey: preSharedKey, PreSharedKey: preSharedKey,
AdminURL: adminURL.String(),
InterfaceName: cfg.WgIface, InterfaceName: cfg.WgIface,
WireguardPort: int64(cfg.WgPort), WireguardPort: int64(cfg.WgPort),
Mtu: int64(cfg.MTU), Mtu: int64(cfg.MTU),
+7 -3
View File
@@ -1,6 +1,7 @@
package server package server
import ( import (
"os"
"os/user" "os/user"
"path/filepath" "path/filepath"
"reflect" "reflect"
@@ -129,7 +130,10 @@ func TestSetConfig_AllFieldsSaved(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "https://new-api.netbird.io:443", cfg.ManagementURL.String()) 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, rosenpassEnabled, cfg.RosenpassEnabled)
require.Equal(t, rosenpassPermissive, cfg.RosenpassPermissive) require.Equal(t, rosenpassPermissive, cfg.RosenpassPermissive)
require.NotNil(t, cfg.ServerSSHAllowed) require.NotNil(t, cfg.ServerSSHAllowed)
@@ -182,11 +186,11 @@ func verifyAllFieldsCovered(t *testing.T, req *proto.SetConfigRequest) {
"CleanNATExternalIPs": true, // control flag for clearing "CleanNATExternalIPs": true, // control flag for clearing
"CleanDNSLabels": true, // control flag for clearing "CleanDNSLabels": true, // control flag for clearing
"LazyConnectionEnabled": true, // deprecated: proto field retained for compat, no longer applied "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{ expectedFields := map[string]bool{
"ManagementUrl": true, "ManagementUrl": true,
"AdminURL": true,
"RosenpassEnabled": true, "RosenpassEnabled": true,
"RosenpassPermissive": true, "RosenpassPermissive": true,
"ServerSSHAllowed": 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. // This map must be updated when adding new config-related CLI flags.
flagToField := map[string]string{ flagToField := map[string]string{
"management-url": "ManagementUrl", "management-url": "ManagementUrl",
"admin-url": "AdminURL",
"enable-rosenpass": "RosenpassEnabled", "enable-rosenpass": "RosenpassEnabled",
"rosenpass-permissive": "RosenpassPermissive", "rosenpass-permissive": "RosenpassPermissive",
"allow-server-ssh": "ServerSSHAllowed", "allow-server-ssh": "ServerSSHAllowed",
@@ -286,6 +289,7 @@ func TestCLIFlags_MappedToSetConfig(t *testing.T) {
fieldsWithoutCLIFlags := map[string]bool{ fieldsWithoutCLIFlags := map[string]bool{
"DisableNotifications": true, // Only settable via UI "DisableNotifications": true, // Only settable via UI
"LazyConnectionEnabled": true, // deprecated: no longer settable (managed by server + NB_LAZY_CONN) "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. // 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(): case <-ctx.Done():
_ = session.Signal(ssh.SIGTERM) _ = session.Signal(ssh.SIGTERM)
select { select {
case <-done: case err := <-done:
log.Debugf("session ended after SIGTERM: %v", err)
return ctx.Err() return ctx.Err()
case <-time.After(100 * time.Millisecond): case <-time.After(100 * time.Millisecond):
return ctx.Err() return ctx.Err()
@@ -205,7 +206,8 @@ func (c *Client) ExecuteCommandWithPTY(ctx context.Context, command string) erro
case <-ctx.Done(): case <-ctx.Done():
_ = session.Signal(ssh.SIGTERM) _ = session.Signal(ssh.SIGTERM)
select { select {
case <-done: case err := <-done:
log.Debugf("session ended after SIGTERM: %v", err)
return ctx.Err() return ctx.Err()
case <-time.After(100 * time.Millisecond): case <-time.After(100 * time.Millisecond):
return ctx.Err() return ctx.Err()
+3 -3
View File
@@ -20,9 +20,9 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
cryptossh "golang.org/x/crypto/ssh" cryptossh "golang.org/x/crypto/ssh"
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
"golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface/wgaddr" "github.com/netbirdio/netbird/client/iface/wgaddr"
"github.com/netbirdio/netbird/client/iface/wgbackend"
sshauth "github.com/netbirdio/netbird/client/ssh/auth" sshauth "github.com/netbirdio/netbird/client/ssh/auth"
"github.com/netbirdio/netbird/client/ssh/detection" "github.com/netbirdio/netbird/client/ssh/detection"
"github.com/netbirdio/netbird/shared/auth" "github.com/netbirdio/netbird/shared/auth"
@@ -167,7 +167,7 @@ type Server struct {
allowSFTP bool allowSFTP bool
jwtEnabled bool jwtEnabled bool
netstackNet *netstack.Net netstackNet *wgbackend.NetstackNet
wgAddress wgaddr.Address wgAddress wgaddr.Address
@@ -445,7 +445,7 @@ func (s *Server) buildSessionInfo(state *sessionState) SessionInfo {
} }
// SetNetstackNet sets the netstack network for userspace networking // 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() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
s.netstackNet = net s.netstackNet = net
+1
View File
@@ -8,5 +8,6 @@
"arrowParens": "always", "arrowParens": "always",
"endOfLine": "lf", "endOfLine": "lf",
"plugins": ["prettier-plugin-tailwindcss"], "plugins": ["prettier-plugin-tailwindcss"],
"tailwindStylesheet": "./src/globals.css",
"tailwindFunctions": ["cn", "clsx", "cva", "tw"] "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 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`. **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): `Config` (read-only mirror, all required):
```ts ```ts
{ managementUrl, adminUrl, configFile, logFile, preSharedKey, interfaceName: string; { managementUrl, configFile, logFile, preSharedKey, interfaceName: string;
wireguardPort, mtu, sshJwtCacheTtl: number; wireguardPort, mtu, sshJwtCacheTtl: number;
disableAutoConnect, serverSshAllowed, disableAutoConnect, serverSshAllowed,
rosenpassEnabled, rosenpassPermissive, rosenpassEnabled, rosenpassPermissive,
@@ -254,7 +254,7 @@ The tray also reads a tray-only synthetic `"Error"` for icon purposes; the front
disableSshAuth: boolean; } 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 }`. `Features`: `{ disableProfiles, disableUpdateSettings, disableNetworks: boolean }`.

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