mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-19 16:56:39 +00:00
Compare commits
30 Commits
v0.25.0
...
sqlite-asy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e76a14a8f | ||
|
|
9fa0fbda0d | ||
|
|
5a7aa461de | ||
|
|
e9c967b27c | ||
|
|
ace588758c | ||
|
|
8bb16e016c | ||
|
|
6a2a97f088 | ||
|
|
3591795a58 | ||
|
|
5311ce4e4a | ||
|
|
c61cb00f40 | ||
|
|
72a1e97304 | ||
|
|
5242851ecc | ||
|
|
cb69348a30 | ||
|
|
69dbcbd362 | ||
|
|
5de4acf2fe | ||
|
|
aa3b79d311 | ||
|
|
8b4ec96516 | ||
|
|
1f3a12d941 | ||
|
|
1de3bb5420 | ||
|
|
163933d429 | ||
|
|
875a2e2b63 | ||
|
|
fd8bba6aa3 | ||
|
|
86908eee58 | ||
|
|
c1caec3fcb | ||
|
|
b28b8fce50 | ||
|
|
f780f17f85 | ||
|
|
5903715a61 | ||
|
|
5469de53c5 | ||
|
|
bc3d647d6b | ||
|
|
7060b63838 |
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.20-bullseye
|
FROM golang:1.21-bullseye
|
||||||
|
|
||||||
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||||
&& apt-get -y install --no-install-recommends\
|
&& apt-get -y install --no-install-recommends\
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
|
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
|
||||||
"ghcr.io/devcontainers/features/go:1": {
|
"ghcr.io/devcontainers/features/go:1": {
|
||||||
"version": "1.20"
|
"version": "1.21"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
|||||||
13
.github/workflows/android-build-validation.yml
vendored
13
.github/workflows/android-build-validation.yml
vendored
@@ -19,9 +19,16 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20.x"
|
go-version: "1.21.x"
|
||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
uses: android-actions/setup-android@v2
|
uses: android-actions/setup-android@v3
|
||||||
|
with:
|
||||||
|
cmdline-tools-version: 8512546
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
java-version: "11"
|
||||||
|
distribution: "adopt"
|
||||||
- name: NDK Cache
|
- name: NDK Cache
|
||||||
id: ndk-cache
|
id: ndk-cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
@@ -29,7 +36,7 @@ jobs:
|
|||||||
path: /usr/local/lib/android/sdk/ndk
|
path: /usr/local/lib/android/sdk/ndk
|
||||||
key: ndk-cache-23.1.7779620
|
key: ndk-cache-23.1.7779620
|
||||||
- name: Setup NDK
|
- name: Setup NDK
|
||||||
run: /usr/local/lib/android/sdk/tools/bin/sdkmanager --install "ndk;23.1.7779620"
|
run: /usr/local/lib/android/sdk/cmdline-tools/7.0/bin/sdkmanager --install "ndk;23.1.7779620"
|
||||||
- name: install gomobile
|
- name: install gomobile
|
||||||
run: go install golang.org/x/mobile/cmd/gomobile@v0.0.0-20230531173138-3c911d8e3eda
|
run: go install golang.org/x/mobile/cmd/gomobile@v0.0.0-20230531173138-3c911d8e3eda
|
||||||
- name: gomobile init
|
- name: gomobile init
|
||||||
|
|||||||
2
.github/workflows/golang-test-darwin.yml
vendored
2
.github/workflows/golang-test-darwin.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20.x"
|
go-version: "1.21.x"
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/golang-test-linux.yml
vendored
4
.github/workflows/golang-test-linux.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20.x"
|
go-version: "1.21.x"
|
||||||
|
|
||||||
|
|
||||||
- name: Cache Go modules
|
- name: Cache Go modules
|
||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20.x"
|
go-version: "1.21.x"
|
||||||
|
|
||||||
- name: Cache Go modules
|
- name: Cache Go modules
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
|
|||||||
4
.github/workflows/golang-test-windows.yml
vendored
4
.github/workflows/golang-test-windows.yml
vendored
@@ -23,13 +23,13 @@ jobs:
|
|||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
id: go
|
id: go
|
||||||
with:
|
with:
|
||||||
go-version: "1.20.x"
|
go-version: "1.21.x"
|
||||||
|
|
||||||
- name: Download wintun
|
- name: Download wintun
|
||||||
uses: carlosperate/download-file-action@v2
|
uses: carlosperate/download-file-action@v2
|
||||||
id: download-wintun
|
id: download-wintun
|
||||||
with:
|
with:
|
||||||
file-url: https://www.wintun.net/builds/wintun-0.14.1.zip
|
file-url: https://pkgs.netbird.io/wintun/wintun-0.14.1.zip
|
||||||
file-name: wintun.zip
|
file-name: wintun.zip
|
||||||
location: ${{ env.downloadPath }}
|
location: ${{ env.downloadPath }}
|
||||||
sha256: '07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51'
|
sha256: '07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51'
|
||||||
|
|||||||
2
.github/workflows/golangci-lint.yml
vendored
2
.github/workflows/golangci-lint.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20.x"
|
go-version: "1.21.x"
|
||||||
cache: false
|
cache: false
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
|||||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -20,7 +20,7 @@ on:
|
|||||||
- 'client/ui/**'
|
- 'client/ui/**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
SIGN_PIPE_VER: "v0.0.10"
|
SIGN_PIPE_VER: "v0.0.11"
|
||||||
GORELEASER_VER: "v1.14.1"
|
GORELEASER_VER: "v1.14.1"
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
name: Set up Go
|
name: Set up Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20"
|
go-version: "1.21"
|
||||||
cache: false
|
cache: false
|
||||||
-
|
-
|
||||||
name: Cache Go modules
|
name: Cache Go modules
|
||||||
@@ -120,7 +120,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20"
|
go-version: "1.21"
|
||||||
cache: false
|
cache: false
|
||||||
- name: Cache Go modules
|
- name: Cache Go modules
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
@@ -175,7 +175,7 @@ jobs:
|
|||||||
name: Set up Go
|
name: Set up Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20"
|
go-version: "1.21"
|
||||||
cache: false
|
cache: false
|
||||||
-
|
-
|
||||||
name: Cache Go modules
|
name: Cache Go modules
|
||||||
|
|||||||
10
.github/workflows/test-infrastructure-files.yml
vendored
10
.github/workflows/test-infrastructure-files.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20.x"
|
go-version: "1.21.x"
|
||||||
|
|
||||||
- name: Cache Go modules
|
- name: Cache Go modules
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
@@ -87,8 +87,10 @@ jobs:
|
|||||||
CI_NETBIRD_SIGNAL_PORT: 12345
|
CI_NETBIRD_SIGNAL_PORT: 12345
|
||||||
CI_NETBIRD_STORE_CONFIG_ENGINE: "sqlite"
|
CI_NETBIRD_STORE_CONFIG_ENGINE: "sqlite"
|
||||||
CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false
|
CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false
|
||||||
|
CI_NETBIRD_TURN_EXTERNAL_IP: "1.2.3.4"
|
||||||
|
|
||||||
run: |
|
run: |
|
||||||
|
set -x
|
||||||
grep AUTH_CLIENT_ID docker-compose.yml | grep $CI_NETBIRD_AUTH_CLIENT_ID
|
grep AUTH_CLIENT_ID docker-compose.yml | grep $CI_NETBIRD_AUTH_CLIENT_ID
|
||||||
grep AUTH_CLIENT_SECRET docker-compose.yml | grep $CI_NETBIRD_AUTH_CLIENT_SECRET
|
grep AUTH_CLIENT_SECRET docker-compose.yml | grep $CI_NETBIRD_AUTH_CLIENT_SECRET
|
||||||
grep AUTH_AUTHORITY docker-compose.yml | grep $CI_NETBIRD_AUTH_AUTHORITY
|
grep AUTH_AUTHORITY docker-compose.yml | grep $CI_NETBIRD_AUTH_AUTHORITY
|
||||||
@@ -120,6 +122,7 @@ jobs:
|
|||||||
grep -A 10 PKCEAuthorizationFlow management.json | grep -A 10 ProviderConfig | grep TokenEndpoint | grep $CI_NETBIRD_AUTH_TOKEN_ENDPOINT
|
grep -A 10 PKCEAuthorizationFlow management.json | grep -A 10 ProviderConfig | grep TokenEndpoint | grep $CI_NETBIRD_AUTH_TOKEN_ENDPOINT
|
||||||
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
|
||||||
|
|
||||||
- name: Install modules
|
- name: Install modules
|
||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
@@ -175,7 +178,10 @@ jobs:
|
|||||||
- name: test management.json file gen
|
- name: test management.json file gen
|
||||||
run: test -f management.json
|
run: test -f management.json
|
||||||
- name: test turnserver.conf file gen
|
- name: test turnserver.conf file gen
|
||||||
run: test -f turnserver.conf
|
run: |
|
||||||
|
set -x
|
||||||
|
test -f turnserver.conf
|
||||||
|
grep external-ip turnserver.conf
|
||||||
- name: test zitadel.env file gen
|
- name: test zitadel.env file gen
|
||||||
run: test -f zitadel.env
|
run: test -f zitadel.env
|
||||||
- name: test dashboard.env file gen
|
- name: test dashboard.env file gen
|
||||||
|
|||||||
@@ -189,6 +189,8 @@ CGO_ENABLED=0 go build .
|
|||||||
|
|
||||||
> Windows clients have a Wireguard driver requirement. You can download the wintun driver from https://www.wintun.net/builds/wintun-0.14.1.zip, after decompressing, you can copy the file `windtun\bin\ARCH\wintun.dll` to the same path as your binary file or to `C:\Windows\System32\wintun.dll`.
|
> Windows clients have a Wireguard driver requirement. You can download the wintun driver from https://www.wintun.net/builds/wintun-0.14.1.zip, after decompressing, you can copy the file `windtun\bin\ARCH\wintun.dll` to the same path as your binary file or to `C:\Windows\System32\wintun.dll`.
|
||||||
|
|
||||||
|
> To test the client GUI application on Windows machines with RDP or vituralized environments (e.g. virtualbox or cloud), you need to download and extract the opengl32.dll from https://fdossena.com/?p=mesa/index.frag next to the built application.
|
||||||
|
|
||||||
To start NetBird the client in the foreground:
|
To start NetBird the client in the foreground:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -49,14 +49,14 @@ https://user-images.githubusercontent.com/700848/197345890-2e2cded5-7b7a-436f-a4
|
|||||||
### Key features
|
### Key features
|
||||||
|
|
||||||
| Connectivity | Management | Automation | Platforms |
|
| Connectivity | Management | Automation | Platforms |
|
||||||
|-------------------------------------------------------------------|--------------------------------------------------------------------------|----------------------------------------------------------------------------|---------------------------------------|
|
|---------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|----------------------------------------------------------------------------|---------------------------------------|
|
||||||
| <ul><li> - \[x] Kernel WireGuard </ul></li> | <ul><li> - \[x] [Admin Web UI](https://github.com/netbirdio/dashboard) </ul></li> | <ul><li> - \[x] [Public API](https://docs.netbird.io/api) </ul></li> | <ul><li> - \[x] Linux </ul></li> |
|
| <ul><li> - \[x] Kernel WireGuard </ul></li> | <ul><li> - \[x] [Admin Web UI](https://github.com/netbirdio/dashboard) </ul></li> | <ul><li> - \[x] [Public API](https://docs.netbird.io/api) </ul></li> | <ul><li> - \[x] Linux </ul></li> |
|
||||||
| <ul><li> - \[x] Peer-to-peer connections </ul></li> | <ul><li> - \[x] Auto peer discovery and configuration </ul></li> | <ul><li> - \[x] [Setup keys for bulk network provisioning](https://docs.netbird.io/how-to/register-machines-using-setup-keys) </ul></li> | <ul><li> - \[x] Mac </ul></li> |
|
| <ul><li> - \[x] Peer-to-peer connections </ul></li> | <ul><li> - \[x] Auto peer discovery and configuration </ul></li> | <ul><li> - \[x] [Setup keys for bulk network provisioning](https://docs.netbird.io/how-to/register-machines-using-setup-keys) </ul></li> | <ul><li> - \[x] Mac </ul></li> |
|
||||||
| <ul><li> - \[x] Peer-to-peer encryption </ul></li> | <ul><li> - \[x] [IdP integrations](https://docs.netbird.io/selfhosted/identity-providers) </ul></li> | <ul><li> - \[x] [Self-hosting quickstart script](https://docs.netbird.io/selfhosted/selfhosted-quickstart) </ul></li> | <ul><li> - \[x] Windows </ul></li> |
|
| <ul><li> - \[x] Peer-to-peer encryption </ul></li> | <ul><li> - \[x] [IdP integrations](https://docs.netbird.io/selfhosted/identity-providers) </ul></li> | <ul><li> - \[x] [Self-hosting quickstart script](https://docs.netbird.io/selfhosted/selfhosted-quickstart) </ul></li> | <ul><li> - \[x] Windows </ul></li> |
|
||||||
| <ul><li> - \[x] Connection relay fallback </ul></li> | <ul><li> - \[x] [SSO & MFA support](https://docs.netbird.io/how-to/installation#running-net-bird-with-sso-login) </ul></li> | <ul><li> - \[x] IdP groups sync with JWT </ul></li> | <ul><li> - \[x] Android </ul></li> |
|
| <ul><li> - \[x] Connection relay fallback </ul></li> | <ul><li> - \[x] [SSO & MFA support](https://docs.netbird.io/how-to/installation#running-net-bird-with-sso-login) </ul></li> | <ul><li> - \[x] IdP groups sync with JWT </ul></li> | <ul><li> - \[x] Android </ul></li> |
|
||||||
| <ul><li> - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks) </ul></li> | <ul><li> - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access) </ul></li> | | <ul><li> - \[ ] iOS </ul></li> |
|
| <ul><li> - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks) </ul></li> | <ul><li> - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access) </ul></li> | | <ul><li> - \[x] iOS </ul></li> |
|
||||||
| <ul><li> - \[x] NAT traversal with BPF </ul></li> | <ul><li> - \[x] [Private DNS](https://docs.netbird.io/how-to/manage-dns-in-your-network) </ul></li> | | <ul><li> - \[x] Docker </ul></li> |
|
| <ul><li> - \[x] NAT traversal with BPF </ul></li> | <ul><li> - \[x] [Private DNS](https://docs.netbird.io/how-to/manage-dns-in-your-network) </ul></li> | | <ul><li> - \[x] Docker </ul></li> |
|
||||||
| | <ul><li> - \[x] [Multiuser support](https://docs.netbird.io/how-to/add-users-to-your-network) </ul></li> | | <ul><li> - \[x] OpenWRT </ul></li> |
|
| <ul><li> - \[x] Post-quantum-secure connection through [Rosenpass](https://rosenpass.eu) </ul></li> | <ul><li> - \[x] [Multiuser support](https://docs.netbird.io/how-to/add-users-to-your-network) </ul></li> | | <ul><li> - \[x] OpenWRT </ul></li> |
|
||||||
| | <ul><li> - \[x] [Activity logging](https://docs.netbird.io/how-to/monitor-system-and-network-activity) </ul></li> | | |
|
| | <ul><li> - \[x] [Activity logging](https://docs.netbird.io/how-to/monitor-system-and-network-activity) </ul></li> | | |
|
||||||
| | <ul><li> - \[x] SSH access management </ul></li> | | |
|
| | <ul><li> - \[x] SSH access management </ul></li> | | |
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3
|
FROM alpine:3.18.5
|
||||||
RUN apk add --no-cache ca-certificates iptables ip6tables
|
RUN apk add --no-cache ca-certificates iptables ip6tables
|
||||||
ENV NB_FOREGROUND_MODE=true
|
ENV NB_FOREGROUND_MODE=true
|
||||||
ENTRYPOINT [ "/go/bin/netbird","up"]
|
ENTRYPOINT [ "/go/bin/netbird","up"]
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ var loginCmd = &cobra.Command{
|
|||||||
return fmt.Errorf("get config file: %v", err)
|
return fmt.Errorf("get config file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config, _ = internal.UpdateOldManagementPort(ctx, config, configPath)
|
config, _ = internal.UpdateOldManagementURL(ctx, config, configPath)
|
||||||
|
|
||||||
err = foregroundLogin(ctx, cmd, config, setupKey)
|
err = foregroundLogin(ctx, cmd, config, setupKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -26,8 +26,11 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
externalIPMapFlag = "external-ip-map"
|
externalIPMapFlag = "external-ip-map"
|
||||||
preSharedKeyFlag = "preshared-key"
|
|
||||||
dnsResolverAddress = "dns-resolver-address"
|
dnsResolverAddress = "dns-resolver-address"
|
||||||
|
enableRosenpassFlag = "enable-rosenpass"
|
||||||
|
preSharedKeyFlag = "preshared-key"
|
||||||
|
interfaceNameFlag = "interface-name"
|
||||||
|
wireguardPortFlag = "wireguard-port"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -50,6 +53,9 @@ var (
|
|||||||
preSharedKey string
|
preSharedKey string
|
||||||
natExternalIPs []string
|
natExternalIPs []string
|
||||||
customDNSAddress string
|
customDNSAddress string
|
||||||
|
rosenpassEnabled bool
|
||||||
|
interfaceName string
|
||||||
|
wireguardPort uint16
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "netbird",
|
Use: "netbird",
|
||||||
Short: "",
|
Short: "",
|
||||||
@@ -119,6 +125,7 @@ func init() {
|
|||||||
`An empty string "" clears the previous configuration. `+
|
`An empty string "" clears the previous configuration. `+
|
||||||
`E.g. --dns-resolver-address 127.0.0.1:5053 or --dns-resolver-address ""`,
|
`E.g. --dns-resolver-address 127.0.0.1:5053 or --dns-resolver-address ""`,
|
||||||
)
|
)
|
||||||
|
upCmd.PersistentFlags().BoolVar(&rosenpassEnabled, enableRosenpassFlag, false, "[Experimental] Enable Rosenpass feature. If enabled, the connection will be post-quantum secured via Rosenpass.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupCloseHandler handles SIGTERM signal and exits with success
|
// SetupCloseHandler handles SIGTERM signal and exits with success
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -16,6 +17,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
"github.com/netbirdio/netbird/client/system"
|
"github.com/netbirdio/netbird/client/system"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,6 +38,8 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
upCmd.PersistentFlags().BoolVarP(&foregroundMode, "foreground-mode", "F", false, "start service in foreground")
|
upCmd.PersistentFlags().BoolVarP(&foregroundMode, "foreground-mode", "F", false, "start service in foreground")
|
||||||
|
upCmd.PersistentFlags().StringVar(&interfaceName, interfaceNameFlag, iface.WgInterfaceDefault, "Wireguard interface name")
|
||||||
|
upCmd.PersistentFlags().Uint16Var(&wireguardPort, wireguardPortFlag, iface.DefaultWgPort, "Wireguard interface listening port")
|
||||||
}
|
}
|
||||||
|
|
||||||
func upFunc(cmd *cobra.Command, args []string) error {
|
func upFunc(cmd *cobra.Command, args []string) error {
|
||||||
@@ -86,6 +90,22 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
CustomDNSAddress: customDNSAddressConverted,
|
CustomDNSAddress: customDNSAddressConverted,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(enableRosenpassFlag).Changed {
|
||||||
|
ic.RosenpassEnabled = &rosenpassEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(interfaceNameFlag).Changed {
|
||||||
|
if err := parseInterfaceName(interfaceName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ic.InterfaceName = &interfaceName
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(wireguardPortFlag).Changed {
|
||||||
|
p := int(wireguardPort)
|
||||||
|
ic.WireguardPort = &p
|
||||||
|
}
|
||||||
|
|
||||||
if rootCmd.PersistentFlags().Changed(preSharedKeyFlag) {
|
if rootCmd.PersistentFlags().Changed(preSharedKeyFlag) {
|
||||||
ic.PreSharedKey = &preSharedKey
|
ic.PreSharedKey = &preSharedKey
|
||||||
}
|
}
|
||||||
@@ -95,7 +115,7 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
return fmt.Errorf("get config file: %v", err)
|
return fmt.Errorf("get config file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
config, _ = internal.UpdateOldManagementPort(ctx, config, configPath)
|
config, _ = internal.UpdateOldManagementURL(ctx, config, configPath)
|
||||||
|
|
||||||
err = foregroundLogin(ctx, cmd, config, setupKey)
|
err = foregroundLogin(ctx, cmd, config, setupKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -153,6 +173,22 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
Hostname: hostName,
|
Hostname: hostName,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(enableRosenpassFlag).Changed {
|
||||||
|
loginRequest.RosenpassEnabled = &rosenpassEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(interfaceNameFlag).Changed {
|
||||||
|
if err := parseInterfaceName(interfaceName); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
loginRequest.InterfaceName = &interfaceName
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(wireguardPortFlag).Changed {
|
||||||
|
wp := int64(wireguardPort)
|
||||||
|
loginRequest.WireguardPort = &wp
|
||||||
|
}
|
||||||
|
|
||||||
var loginErr error
|
var loginErr error
|
||||||
|
|
||||||
var loginResp *proto.LoginResponse
|
var loginResp *proto.LoginResponse
|
||||||
@@ -224,6 +260,18 @@ func validateNATExternalIPs(list []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseInterfaceName(name string) error {
|
||||||
|
if runtime.GOOS != "darwin" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(name, "utun") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("invalid interface name %s. Please use the prefix utun followed by a number on MacOS. e.g., utun1 or utun199", name)
|
||||||
|
}
|
||||||
|
|
||||||
func validateElement(element string) (int, error) {
|
func validateElement(element string) (int, error) {
|
||||||
if isValidIP(element) {
|
if isValidIP(element) {
|
||||||
return ipInputType, nil
|
return ipInputType, nil
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ type AclManager struct {
|
|||||||
type iFaceMapper interface {
|
type iFaceMapper interface {
|
||||||
Name() string
|
Name() string
|
||||||
Address() iface.WGAddress
|
Address() iface.WGAddress
|
||||||
|
IsUserspaceBind() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAclManager(table *nftables.Table, wgIface iFaceMapper, routeingFwChainName string) (*AclManager, error) {
|
func newAclManager(table *nftables.Table, wgIface iFaceMapper, routeingFwChainName string) (*AclManager, error) {
|
||||||
@@ -198,6 +199,81 @@ func (m *AclManager) DeleteRule(rule firewall.Rule) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createDefaultAllowRules In case if the USP firewall manager can use the native firewall manager we must to create allow rules for
|
||||||
|
// input and output chains
|
||||||
|
func (m *AclManager) createDefaultAllowRules() error {
|
||||||
|
expIn := []expr.Any{
|
||||||
|
&expr.Payload{
|
||||||
|
DestRegister: 1,
|
||||||
|
Base: expr.PayloadBaseNetworkHeader,
|
||||||
|
Offset: 12,
|
||||||
|
Len: 4,
|
||||||
|
},
|
||||||
|
// mask
|
||||||
|
&expr.Bitwise{
|
||||||
|
SourceRegister: 1,
|
||||||
|
DestRegister: 1,
|
||||||
|
Len: 4,
|
||||||
|
Mask: []byte{0x00, 0x00, 0x00, 0x00},
|
||||||
|
Xor: zeroXor,
|
||||||
|
},
|
||||||
|
// net address
|
||||||
|
&expr.Cmp{
|
||||||
|
Register: 1,
|
||||||
|
Data: []byte{0x00, 0x00, 0x00, 0x00},
|
||||||
|
},
|
||||||
|
&expr.Verdict{
|
||||||
|
Kind: expr.VerdictAccept,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = m.rConn.InsertRule(&nftables.Rule{
|
||||||
|
Table: m.workTable,
|
||||||
|
Chain: m.chainInputRules,
|
||||||
|
Position: 0,
|
||||||
|
Exprs: expIn,
|
||||||
|
})
|
||||||
|
|
||||||
|
expOut := []expr.Any{
|
||||||
|
&expr.Payload{
|
||||||
|
DestRegister: 1,
|
||||||
|
Base: expr.PayloadBaseNetworkHeader,
|
||||||
|
Offset: 16,
|
||||||
|
Len: 4,
|
||||||
|
},
|
||||||
|
// mask
|
||||||
|
&expr.Bitwise{
|
||||||
|
SourceRegister: 1,
|
||||||
|
DestRegister: 1,
|
||||||
|
Len: 4,
|
||||||
|
Mask: []byte{0x00, 0x00, 0x00, 0x00},
|
||||||
|
Xor: zeroXor,
|
||||||
|
},
|
||||||
|
// net address
|
||||||
|
&expr.Cmp{
|
||||||
|
Register: 1,
|
||||||
|
Data: []byte{0x00, 0x00, 0x00, 0x00},
|
||||||
|
},
|
||||||
|
&expr.Verdict{
|
||||||
|
Kind: expr.VerdictAccept,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = m.rConn.InsertRule(&nftables.Rule{
|
||||||
|
Table: m.workTable,
|
||||||
|
Chain: m.chainOutputRules,
|
||||||
|
Position: 0,
|
||||||
|
Exprs: expOut,
|
||||||
|
})
|
||||||
|
|
||||||
|
err := m.rConn.Flush()
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("failed to create default allow rules: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Flush rule/chain/set operations from the buffer
|
// Flush rule/chain/set operations from the buffer
|
||||||
//
|
//
|
||||||
// Method also get all rules after flush and refreshes handle values in the rulesets
|
// Method also get all rules after flush and refreshes handle values in the rulesets
|
||||||
@@ -735,7 +811,6 @@ func (m *AclManager) createPreroutingMangle() *nftables.Chain {
|
|||||||
Chain: chain,
|
Chain: chain,
|
||||||
Exprs: expressions,
|
Exprs: expressions,
|
||||||
})
|
})
|
||||||
chain = m.rConn.AddChain(chain)
|
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,11 +106,19 @@ func (m *Manager) RemoveRoutingRules(pair firewall.RouterPair) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AllowNetbird allows netbird interface traffic
|
// AllowNetbird allows netbird interface traffic
|
||||||
// todo review this method usage
|
|
||||||
func (m *Manager) AllowNetbird() error {
|
func (m *Manager) AllowNetbird() error {
|
||||||
|
if !m.wgIface.IsUserspaceBind() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
m.mutex.Lock()
|
m.mutex.Lock()
|
||||||
defer m.mutex.Unlock()
|
defer m.mutex.Unlock()
|
||||||
|
|
||||||
|
err := m.aclManager.createDefaultAllowRules()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create default allow rules: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
chains, err := m.rConn.ListChainsOfTableFamily(nftables.TableFamilyIPv4)
|
chains, err := m.rConn.ListChainsOfTableFamily(nftables.TableFamilyIPv4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("list of chains: %w", err)
|
return fmt.Errorf("list of chains: %w", err)
|
||||||
@@ -145,6 +153,7 @@ func (m *Manager) AllowNetbird() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to flush allow input netbird rules: %v", err)
|
return fmt.Errorf("failed to flush allow input netbird rules: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ func (i *iFaceMock) Address() iface.WGAddress {
|
|||||||
panic("AddressFunc is not set")
|
panic("AddressFunc is not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *iFaceMock) IsUserspaceBind() bool { return false }
|
||||||
|
|
||||||
func TestNftablesManager(t *testing.T) {
|
func TestNftablesManager(t *testing.T) {
|
||||||
mock := &iFaceMock{
|
mock := &iFaceMock{
|
||||||
NameFunc: func() string {
|
NameFunc: func() string {
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ Sleep 3000
|
|||||||
Delete "$INSTDIR\${UI_APP_EXE}"
|
Delete "$INSTDIR\${UI_APP_EXE}"
|
||||||
Delete "$INSTDIR\${MAIN_APP_EXE}"
|
Delete "$INSTDIR\${MAIN_APP_EXE}"
|
||||||
Delete "$INSTDIR\wintun.dll"
|
Delete "$INSTDIR\wintun.dll"
|
||||||
|
Delete "$INSTDIR\opengl32.dll"
|
||||||
RmDir /r "$INSTDIR"
|
RmDir /r "$INSTDIR"
|
||||||
|
|
||||||
SetShellVarContext all
|
SetShellVarContext all
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func TestDefaultManager(t *testing.T) {
|
|||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
ifaceMock := mocks.NewMockIFaceMapper(ctrl)
|
ifaceMock := mocks.NewMockIFaceMapper(ctrl)
|
||||||
ifaceMock.EXPECT().IsUserspaceBind().Return(true)
|
ifaceMock.EXPECT().IsUserspaceBind().Return(true).AnyTimes()
|
||||||
ifaceMock.EXPECT().SetFilter(gomock.Any())
|
ifaceMock.EXPECT().SetFilter(gomock.Any())
|
||||||
ip, network, err := net.ParseCIDR("172.0.0.1/32")
|
ip, network, err := net.ParseCIDR("172.0.0.1/32")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -331,7 +331,7 @@ func TestDefaultManagerEnableSSHRules(t *testing.T) {
|
|||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
|
|
||||||
ifaceMock := mocks.NewMockIFaceMapper(ctrl)
|
ifaceMock := mocks.NewMockIFaceMapper(ctrl)
|
||||||
ifaceMock.EXPECT().IsUserspaceBind().Return(true)
|
ifaceMock.EXPECT().IsUserspaceBind().Return(true).AnyTimes()
|
||||||
ifaceMock.EXPECT().SetFilter(gomock.Any())
|
ifaceMock.EXPECT().SetFilter(gomock.Any())
|
||||||
ip, network, err := net.ParseCIDR("172.0.0.1/32")
|
ip, network, err := net.ParseCIDR("172.0.0.1/32")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -12,16 +13,19 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/client/ssh"
|
"github.com/netbirdio/netbird/client/ssh"
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
mgm "github.com/netbirdio/netbird/management/client"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ManagementLegacyPort is the port that was used before by the Management gRPC server.
|
// managementLegacyPortString is the port that was used before by the Management gRPC server.
|
||||||
// It is used for backward compatibility now.
|
// It is used for backward compatibility now.
|
||||||
// NB: hardcoded from github.com/netbirdio/netbird/management/cmd to avoid import
|
// NB: hardcoded from github.com/netbirdio/netbird/management/cmd to avoid import
|
||||||
ManagementLegacyPort = 33073
|
managementLegacyPortString = "33073"
|
||||||
// DefaultManagementURL points to the NetBird's cloud management endpoint
|
// DefaultManagementURL points to the NetBird's cloud management endpoint
|
||||||
DefaultManagementURL = "https://api.wiretrustee.com:443"
|
DefaultManagementURL = "https://api.netbird.io:443"
|
||||||
|
// oldDefaultManagementURL points to the NetBird's old cloud management endpoint
|
||||||
|
oldDefaultManagementURL = "https://api.wiretrustee.com:443"
|
||||||
// DefaultAdminURL points to NetBird's cloud management console
|
// DefaultAdminURL points to NetBird's cloud management console
|
||||||
DefaultAdminURL = "https://app.netbird.io:443"
|
DefaultAdminURL = "https://app.netbird.io:443"
|
||||||
)
|
)
|
||||||
@@ -37,6 +41,9 @@ type ConfigInput struct {
|
|||||||
PreSharedKey *string
|
PreSharedKey *string
|
||||||
NATExternalIPs []string
|
NATExternalIPs []string
|
||||||
CustomDNSAddress []byte
|
CustomDNSAddress []byte
|
||||||
|
RosenpassEnabled *bool
|
||||||
|
InterfaceName *string
|
||||||
|
WireguardPort *int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config Configuration type
|
// Config Configuration type
|
||||||
@@ -50,10 +57,11 @@ type Config struct {
|
|||||||
WgPort int
|
WgPort int
|
||||||
IFaceBlackList []string
|
IFaceBlackList []string
|
||||||
DisableIPv6Discovery bool
|
DisableIPv6Discovery bool
|
||||||
|
RosenpassEnabled bool
|
||||||
// SSHKey is a private SSH key in a PEM format
|
// SSHKey is a private SSH key in a PEM format
|
||||||
SSHKey string
|
SSHKey string
|
||||||
|
|
||||||
// ExternalIP mappings, if different than the host interface IP
|
// ExternalIP mappings, if different from the host interface IP
|
||||||
//
|
//
|
||||||
// External IP must not be behind a CGNAT and port-forwarding for incoming UDP packets from WgPort on ExternalIP
|
// External IP must not be behind a CGNAT and port-forwarding for incoming UDP packets from WgPort on ExternalIP
|
||||||
// to WgPort on host interface IP must be present. This can take form of single port-forwarding rule, 1:1 DNAT
|
// to WgPort on host interface IP must be present. This can take form of single port-forwarding rule, 1:1 DNAT
|
||||||
@@ -136,11 +144,10 @@ func createNewConfig(input ConfigInput) (*Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
SSHKey: string(pem),
|
SSHKey: string(pem),
|
||||||
PrivateKey: wgKey,
|
PrivateKey: wgKey,
|
||||||
WgIface: iface.WgInterfaceDefault,
|
|
||||||
WgPort: iface.DefaultWgPort,
|
|
||||||
IFaceBlackList: []string{},
|
IFaceBlackList: []string{},
|
||||||
DisableIPv6Discovery: false,
|
DisableIPv6Discovery: false,
|
||||||
NATExternalIPs: input.NATExternalIPs,
|
NATExternalIPs: input.NATExternalIPs,
|
||||||
@@ -161,10 +168,24 @@ func createNewConfig(input ConfigInput) (*Config, error) {
|
|||||||
config.ManagementURL = URL
|
config.ManagementURL = URL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.WgPort = iface.DefaultWgPort
|
||||||
|
if input.WireguardPort != nil {
|
||||||
|
config.WgPort = *input.WireguardPort
|
||||||
|
}
|
||||||
|
|
||||||
|
config.WgIface = iface.WgInterfaceDefault
|
||||||
|
if input.InterfaceName != nil {
|
||||||
|
config.WgIface = *input.InterfaceName
|
||||||
|
}
|
||||||
|
|
||||||
if input.PreSharedKey != nil {
|
if input.PreSharedKey != nil {
|
||||||
config.PreSharedKey = *input.PreSharedKey
|
config.PreSharedKey = *input.PreSharedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.RosenpassEnabled != nil {
|
||||||
|
config.RosenpassEnabled = *input.RosenpassEnabled
|
||||||
|
}
|
||||||
|
|
||||||
defaultAdminURL, err := parseURL("Admin URL", DefaultAdminURL)
|
defaultAdminURL, err := parseURL("Admin URL", DefaultAdminURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -233,6 +254,17 @@ func update(input ConfigInput) (*Config, error) {
|
|||||||
config.WgPort = iface.DefaultWgPort
|
config.WgPort = iface.DefaultWgPort
|
||||||
refresh = true
|
refresh = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.WireguardPort != nil {
|
||||||
|
config.WgPort = *input.WireguardPort
|
||||||
|
refresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.InterfaceName != nil {
|
||||||
|
config.WgIface = *input.InterfaceName
|
||||||
|
refresh = true
|
||||||
|
}
|
||||||
|
|
||||||
if input.NATExternalIPs != nil && len(config.NATExternalIPs) != len(input.NATExternalIPs) {
|
if input.NATExternalIPs != nil && len(config.NATExternalIPs) != len(input.NATExternalIPs) {
|
||||||
config.NATExternalIPs = input.NATExternalIPs
|
config.NATExternalIPs = input.NATExternalIPs
|
||||||
refresh = true
|
refresh = true
|
||||||
@@ -243,6 +275,11 @@ func update(input ConfigInput) (*Config, error) {
|
|||||||
refresh = true
|
refresh = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.RosenpassEnabled != nil {
|
||||||
|
config.RosenpassEnabled = *input.RosenpassEnabled
|
||||||
|
refresh = true
|
||||||
|
}
|
||||||
|
|
||||||
if refresh {
|
if refresh {
|
||||||
// since we have new management URL, we need to update config file
|
// since we have new management URL, we need to update config file
|
||||||
if err := util.WriteJson(input.ConfigPath, config); err != nil {
|
if err := util.WriteJson(input.ConfigPath, config); err != nil {
|
||||||
@@ -302,3 +339,86 @@ func configFileIsExists(path string) bool {
|
|||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
return !os.IsNotExist(err)
|
return !os.IsNotExist(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateOldManagementURL checks whether client can switch to the new Management URL with port 443 and the management domain.
|
||||||
|
// If it can switch, then it updates the config and returns a new one. Otherwise, it returns the provided config.
|
||||||
|
// The check is performed only for the NetBird's managed version.
|
||||||
|
func UpdateOldManagementURL(ctx context.Context, config *Config, configPath string) (*Config, error) {
|
||||||
|
|
||||||
|
defaultManagementURL, err := parseURL("Management URL", DefaultManagementURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedOldDefaultManagementURL, err := parseURL("Management URL", oldDefaultManagementURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ManagementURL.Hostname() != defaultManagementURL.Hostname() &&
|
||||||
|
config.ManagementURL.Hostname() != parsedOldDefaultManagementURL.Hostname() {
|
||||||
|
// only do the check for the NetBird's managed version
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var mgmTlsEnabled bool
|
||||||
|
if config.ManagementURL.Scheme == "https" {
|
||||||
|
mgmTlsEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mgmTlsEnabled {
|
||||||
|
// only do the check for HTTPs scheme (the hosted version of the Management service is always HTTPs)
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ManagementURL.Port() != managementLegacyPortString &&
|
||||||
|
config.ManagementURL.Hostname() == defaultManagementURL.Hostname() {
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
newURL, err := parseURL("Management URL", fmt.Sprintf("%s://%s:%d",
|
||||||
|
config.ManagementURL.Scheme, defaultManagementURL.Hostname(), 443))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// here we check whether we could switch from the legacy 33073 port to the new 443
|
||||||
|
log.Infof("attempting to switch from the legacy Management URL %s to the new one %s",
|
||||||
|
config.ManagementURL.String(), newURL.String())
|
||||||
|
key, err := wgtypes.ParseKey(config.PrivateKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := mgm.NewClient(ctx, newURL.Host, key, mgmTlsEnabled)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err = client.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to close the Management service client %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// gRPC check
|
||||||
|
_, err = client.GetServerPublicKey()
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// everything is alright => update the config
|
||||||
|
newConfig, err := UpdateConfig(ConfigInput{
|
||||||
|
ManagementURL: newURL.String(),
|
||||||
|
ConfigPath: configPath,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
||||||
|
return config, fmt.Errorf("failed updating config file: %v", err)
|
||||||
|
}
|
||||||
|
log.Infof("successfully switched to the new Management URL: %s", newURL.String())
|
||||||
|
|
||||||
|
return newConfig, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
@@ -120,3 +122,60 @@ func TestHiddenPreSharedKey(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpdateOldManagementURL(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
previousManagementURL string
|
||||||
|
expectedManagementURL string
|
||||||
|
fileShouldNotChange bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Update old management URL with legacy port",
|
||||||
|
previousManagementURL: "https://api.wiretrustee.com:33073",
|
||||||
|
expectedManagementURL: DefaultManagementURL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Update old management URL",
|
||||||
|
previousManagementURL: oldDefaultManagementURL,
|
||||||
|
expectedManagementURL: DefaultManagementURL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "No update needed when management URL is up to date",
|
||||||
|
previousManagementURL: DefaultManagementURL,
|
||||||
|
expectedManagementURL: DefaultManagementURL,
|
||||||
|
fileShouldNotChange: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "No update needed when not using cloud management",
|
||||||
|
previousManagementURL: "https://netbird.example.com:33073",
|
||||||
|
expectedManagementURL: "https://netbird.example.com:33073",
|
||||||
|
fileShouldNotChange: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
configPath := filepath.Join(tempDir, "config.json")
|
||||||
|
config, err := UpdateOrCreateConfig(ConfigInput{
|
||||||
|
ManagementURL: tt.previousManagementURL,
|
||||||
|
ConfigPath: configPath,
|
||||||
|
})
|
||||||
|
require.NoError(t, err, "failed to create testing config")
|
||||||
|
previousStats, err := os.Stat(configPath)
|
||||||
|
require.NoError(t, err, "failed to create testing config stats")
|
||||||
|
resultConfig, err := UpdateOldManagementURL(context.TODO(), config, configPath)
|
||||||
|
require.NoError(t, err, "got error when updating old management url")
|
||||||
|
require.Equal(t, tt.expectedManagementURL, resultConfig.ManagementURL.String())
|
||||||
|
newStats, err := os.Stat(configPath)
|
||||||
|
require.NoError(t, err, "failed to create testing config stats")
|
||||||
|
switch tt.fileShouldNotChange {
|
||||||
|
case true:
|
||||||
|
require.Equal(t, previousStats.ModTime(), newStats.ModTime(), "file should not change")
|
||||||
|
case false:
|
||||||
|
require.NotEqual(t, previousStats.ModTime(), newStats.ModTime(), "file should have changed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -235,6 +235,7 @@ func createEngineConfig(key wgtypes.Key, config *Config, peerConfig *mgmProto.Pe
|
|||||||
SSHKey: []byte(config.SSHKey),
|
SSHKey: []byte(config.SSHKey),
|
||||||
NATExternalIPs: config.NATExternalIPs,
|
NATExternalIPs: config.NATExternalIPs,
|
||||||
CustomDNSAddress: config.CustomDNSAddress,
|
CustomDNSAddress: config.CustomDNSAddress,
|
||||||
|
RosenpassEnabled: config.RosenpassEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.PreSharedKey != "" {
|
if config.PreSharedKey != "" {
|
||||||
@@ -283,83 +284,6 @@ func loginToManagement(ctx context.Context, client mgm.Client, pubSSHKey []byte)
|
|||||||
return loginResp, nil
|
return loginResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOldManagementPort checks whether client can switch to the new Management port 443.
|
|
||||||
// If it can switch, then it updates the config and returns a new one. Otherwise, it returns the provided config.
|
|
||||||
// The check is performed only for the NetBird's managed version.
|
|
||||||
func UpdateOldManagementPort(ctx context.Context, config *Config, configPath string) (*Config, error) {
|
|
||||||
|
|
||||||
defaultManagementURL, err := parseURL("Management URL", DefaultManagementURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.ManagementURL.Hostname() != defaultManagementURL.Hostname() {
|
|
||||||
// only do the check for the NetBird's managed version
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var mgmTlsEnabled bool
|
|
||||||
if config.ManagementURL.Scheme == "https" {
|
|
||||||
mgmTlsEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !mgmTlsEnabled {
|
|
||||||
// only do the check for HTTPs scheme (the hosted version of the Management service is always HTTPs)
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if mgmTlsEnabled && config.ManagementURL.Port() == fmt.Sprintf("%d", ManagementLegacyPort) {
|
|
||||||
|
|
||||||
newURL, err := parseURL("Management URL", fmt.Sprintf("%s://%s:%d",
|
|
||||||
config.ManagementURL.Scheme, config.ManagementURL.Hostname(), 443))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// here we check whether we could switch from the legacy 33073 port to the new 443
|
|
||||||
log.Infof("attempting to switch from the legacy Management URL %s to the new one %s",
|
|
||||||
config.ManagementURL.String(), newURL.String())
|
|
||||||
key, err := wgtypes.ParseKey(config.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
|
||||||
return config, err
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := mgm.NewClient(ctx, newURL.Host, key, mgmTlsEnabled)
|
|
||||||
if err != nil {
|
|
||||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
|
||||||
return config, err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
err = client.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed to close the Management service client %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// gRPC check
|
|
||||||
_, err = client.GetServerPublicKey()
|
|
||||||
if err != nil {
|
|
||||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// everything is alright => update the config
|
|
||||||
newConfig, err := UpdateConfig(ConfigInput{
|
|
||||||
ManagementURL: newURL.String(),
|
|
||||||
ConfigPath: configPath,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Infof("couldn't switch to the new Management %s", newURL.String())
|
|
||||||
return config, fmt.Errorf("failed updating config file: %v", err)
|
|
||||||
}
|
|
||||||
log.Infof("successfully switched to the new Management URL: %s", newURL.String())
|
|
||||||
|
|
||||||
return newConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func statusRecorderToMgmConnStateNotifier(statusRecorder *peer.Status) mgm.ConnStateNotifier {
|
func statusRecorderToMgmConnStateNotifier(statusRecorder *peer.Status) mgm.ConnStateNotifier {
|
||||||
var sri interface{} = statusRecorder
|
var sri interface{} = statusRecorder
|
||||||
mgmNotifier, _ := sri.(mgm.ConnStateNotifier)
|
mgmNotifier, _ := sri.(mgm.ConnStateNotifier)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/firewall/uspfilter"
|
"github.com/netbirdio/netbird/client/firewall/uspfilter"
|
||||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
@@ -250,11 +251,12 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
|
|
||||||
for n, testCase := range testCases {
|
for n, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
privKey, _ := wgtypes.GenerateKey()
|
||||||
newNet, err := stdnet.NewNet(nil)
|
newNet, err := stdnet.NewNet(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
wgIface, err := iface.NewWGIFace(fmt.Sprintf("utun230%d", n), fmt.Sprintf("100.66.100.%d/32", n+1), iface.DefaultMTU, nil, newNet)
|
wgIface, err := iface.NewWGIFace(fmt.Sprintf("utun230%d", n), fmt.Sprintf("100.66.100.%d/32", n+1), 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -331,7 +333,8 @@ func TestDNSFakeResolverHandleUpdates(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.1/32", iface.DefaultMTU, nil, newNet)
|
privKey, _ := wgtypes.GeneratePrivateKey()
|
||||||
|
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.1/32", 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("build interface wireguard: %v", err)
|
t.Errorf("build interface wireguard: %v", err)
|
||||||
return
|
return
|
||||||
@@ -782,7 +785,8 @@ func createWgInterfaceWithBind(t *testing.T) (*iface.WGIface, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.2/24", iface.DefaultMTU, nil, newNet)
|
privKey, _ := wgtypes.GeneratePrivateKey()
|
||||||
|
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.2/24", 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("build interface wireguard: %v", err)
|
t.Fatalf("build interface wireguard: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Code generated by bpf2go; DO NOT EDIT.
|
// Code generated by bpf2go; DO NOT EDIT.
|
||||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||||
// +build arm64be armbe mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64
|
|
||||||
|
|
||||||
package ebpf
|
package ebpf
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1,6 +1,5 @@
|
|||||||
// Code generated by bpf2go; DO NOT EDIT.
|
// Code generated by bpf2go; DO NOT EDIT.
|
||||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
//go:build 386 || amd64 || amd64p32 || arm || arm64 || loong64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||||
// +build 386 amd64 amd64p32 arm arm64 mips64le mips64p32le mipsle ppc64le riscv64
|
|
||||||
|
|
||||||
package ebpf
|
package ebpf
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -46,8 +46,8 @@ int xdp_dns_fwd(struct iphdr *ip, struct udphdr *udp) {
|
|||||||
if(!read_settings()){
|
if(!read_settings()){
|
||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
}
|
}
|
||||||
bpf_printk("dns port: %d", ntohs(dns_port));
|
// bpf_printk("dns port: %d", ntohs(dns_port));
|
||||||
bpf_printk("dns ip: %d", ntohl(dns_ip));
|
// bpf_printk("dns ip: %d", ntohl(dns_ip));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (udp->dest == GENERAL_DNS_PORT && ip->daddr == dns_ip) {
|
if (udp->dest == GENERAL_DNS_PORT && ip->daddr == dns_ip) {
|
||||||
|
|||||||
@@ -8,12 +8,6 @@
|
|||||||
#include "dns_fwd.c"
|
#include "dns_fwd.c"
|
||||||
#include "wg_proxy.c"
|
#include "wg_proxy.c"
|
||||||
|
|
||||||
#define bpf_printk(fmt, ...) \
|
|
||||||
({ \
|
|
||||||
char ____fmt[] = fmt; \
|
|
||||||
bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
|
|
||||||
})
|
|
||||||
|
|
||||||
const __u16 flag_feature_wg_proxy = 0b01;
|
const __u16 flag_feature_wg_proxy = 0b01;
|
||||||
const __u16 flag_feature_dns_fwd = 0b10;
|
const __u16 flag_feature_dns_fwd = 0b10;
|
||||||
|
|
||||||
|
|||||||
12
client/internal/ebpf/ebpf/src/readme.md
Normal file
12
client/internal/ebpf/ebpf/src/readme.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Debug
|
||||||
|
|
||||||
|
|
||||||
|
The CONFIG_BPF_EVENTS kernel module is required for bpf_printk.
|
||||||
|
Apply this code to use bpf_printk
|
||||||
|
```
|
||||||
|
#define bpf_printk(fmt, ...) \
|
||||||
|
({ \
|
||||||
|
char ____fmt[] = fmt; \
|
||||||
|
bpf_trace_printk(____fmt, sizeof(____fmt), ##__VA_ARGS__); \
|
||||||
|
})
|
||||||
|
```
|
||||||
@@ -34,7 +34,7 @@ int xdp_wg_proxy(struct iphdr *ip, struct udphdr *udp) {
|
|||||||
if (!read_port_settings()){
|
if (!read_port_settings()){
|
||||||
return XDP_PASS;
|
return XDP_PASS;
|
||||||
}
|
}
|
||||||
bpf_printk("proxy port: %d, wg port: %d", proxy_port, wg_port);
|
// bpf_printk("proxy port: %d, wg port: %d", proxy_port, wg_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2130706433 = 127.0.0.1
|
// 2130706433 = 127.0.0.1
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package internal
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@@ -13,7 +12,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/ice/v2"
|
"github.com/pion/ice/v3"
|
||||||
|
"github.com/pion/stun/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/internal/acl"
|
"github.com/netbirdio/netbird/client/internal/acl"
|
||||||
"github.com/netbirdio/netbird/client/internal/dns"
|
"github.com/netbirdio/netbird/client/internal/dns"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/rosenpass"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||||
"github.com/netbirdio/netbird/client/internal/wgproxy"
|
"github.com/netbirdio/netbird/client/internal/wgproxy"
|
||||||
nbssh "github.com/netbirdio/netbird/client/ssh"
|
nbssh "github.com/netbirdio/netbird/client/ssh"
|
||||||
@@ -31,7 +32,6 @@ import (
|
|||||||
mgm "github.com/netbirdio/netbird/management/client"
|
mgm "github.com/netbirdio/netbird/management/client"
|
||||||
mgmProto "github.com/netbirdio/netbird/management/proto"
|
mgmProto "github.com/netbirdio/netbird/management/proto"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
"github.com/netbirdio/netbird/sharedsock"
|
|
||||||
signal "github.com/netbirdio/netbird/signal/client"
|
signal "github.com/netbirdio/netbird/signal/client"
|
||||||
sProto "github.com/netbirdio/netbird/signal/proto"
|
sProto "github.com/netbirdio/netbird/signal/proto"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
@@ -77,6 +77,8 @@ type EngineConfig struct {
|
|||||||
NATExternalIPs []string
|
NATExternalIPs []string
|
||||||
|
|
||||||
CustomDNSAddress string
|
CustomDNSAddress string
|
||||||
|
|
||||||
|
RosenpassEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Engine is a mechanism responsible for reacting on Signal and Management stream events and managing connections to the remote peers.
|
// Engine is a mechanism responsible for reacting on Signal and Management stream events and managing connections to the remote peers.
|
||||||
@@ -87,6 +89,8 @@ type Engine struct {
|
|||||||
mgmClient mgm.Client
|
mgmClient mgm.Client
|
||||||
// peerConns is a map that holds all the peers that are known to this peer
|
// peerConns is a map that holds all the peers that are known to this peer
|
||||||
peerConns map[string]*peer.Conn
|
peerConns map[string]*peer.Conn
|
||||||
|
// rpManager is a Rosenpass manager
|
||||||
|
rpManager *rosenpass.Manager
|
||||||
|
|
||||||
// syncMsgMux is used to guarantee sequential Management Service message processing
|
// syncMsgMux is used to guarantee sequential Management Service message processing
|
||||||
syncMsgMux *sync.Mutex
|
syncMsgMux *sync.Mutex
|
||||||
@@ -95,9 +99,9 @@ type Engine struct {
|
|||||||
mobileDep MobileDependency
|
mobileDep MobileDependency
|
||||||
|
|
||||||
// STUNs is a list of STUN servers used by ICE
|
// STUNs is a list of STUN servers used by ICE
|
||||||
STUNs []*ice.URL
|
STUNs []*stun.URI
|
||||||
// TURNs is a list of STUN servers used by ICE
|
// TURNs is a list of STUN servers used by ICE
|
||||||
TURNs []*ice.URL
|
TURNs []*stun.URI
|
||||||
|
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
|
||||||
@@ -107,7 +111,6 @@ type Engine struct {
|
|||||||
wgProxyFactory *wgproxy.Factory
|
wgProxyFactory *wgproxy.Factory
|
||||||
|
|
||||||
udpMux *bind.UniversalUDPMuxDefault
|
udpMux *bind.UniversalUDPMuxDefault
|
||||||
udpMuxConn io.Closer
|
|
||||||
|
|
||||||
// networkSerial is the latest CurrentSerial (state ID) of the network sent by the Management service
|
// networkSerial is the latest CurrentSerial (state ID) of the network sent by the Management service
|
||||||
networkSerial uint64
|
networkSerial uint64
|
||||||
@@ -146,8 +149,8 @@ func NewEngine(
|
|||||||
syncMsgMux: &sync.Mutex{},
|
syncMsgMux: &sync.Mutex{},
|
||||||
config: config,
|
config: config,
|
||||||
mobileDep: mobileDep,
|
mobileDep: mobileDep,
|
||||||
STUNs: []*ice.URL{},
|
STUNs: []*stun.URI{},
|
||||||
TURNs: []*ice.URL{},
|
TURNs: []*stun.URI{},
|
||||||
networkSerial: 0,
|
networkSerial: 0,
|
||||||
sshServerFunc: nbssh.DefaultSSHServer,
|
sshServerFunc: nbssh.DefaultSSHServer,
|
||||||
statusRecorder: statusRecorder,
|
statusRecorder: statusRecorder,
|
||||||
@@ -180,66 +183,38 @@ func (e *Engine) Start() error {
|
|||||||
e.syncMsgMux.Lock()
|
e.syncMsgMux.Lock()
|
||||||
defer e.syncMsgMux.Unlock()
|
defer e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
wgIFaceName := e.config.WgIfaceName
|
wgIface, err := e.newWgIface()
|
||||||
wgAddr := e.config.WgAddr
|
|
||||||
myPrivateKey := e.config.WgPrivateKey
|
|
||||||
var err error
|
|
||||||
transportNet, err := e.newStdNet()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to create pion's stdnet: %s", err)
|
log.Errorf("failed creating wireguard interface instance %s: [%s]", e.config.WgIfaceName, err.Error())
|
||||||
}
|
|
||||||
|
|
||||||
e.wgInterface, err = iface.NewWGIFace(wgIFaceName, wgAddr, iface.DefaultMTU, e.mobileDep.TunAdapter, transportNet)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed creating wireguard interface instance %s: [%s]", wgIFaceName, err.Error())
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
e.wgInterface = wgIface
|
||||||
|
|
||||||
var routes []*route.Route
|
if e.config.RosenpassEnabled {
|
||||||
|
log.Infof("rosenpass is enabled")
|
||||||
switch runtime.GOOS {
|
e.rpManager, err = rosenpass.NewManager(e.config.PreSharedKey, e.config.WgIfaceName)
|
||||||
case "android":
|
|
||||||
var dnsConfig *nbdns.Config
|
|
||||||
routes, dnsConfig, err = e.readInitialSettings()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if e.dnsServer == nil {
|
err := e.rpManager.Run()
|
||||||
e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses, *dnsConfig, e.mobileDep.NetworkChangeListener)
|
if err != nil {
|
||||||
go e.mobileDep.DnsReadyListener.OnReady()
|
return err
|
||||||
}
|
}
|
||||||
case "ios":
|
|
||||||
if e.dnsServer == nil {
|
|
||||||
e.dnsServer = dns.NewDefaultServerIos(e.ctx, e.wgInterface, e.mobileDep.DnsManager)
|
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
if e.dnsServer == nil {
|
initialRoutes, dnsServer, err := e.newDnsServer()
|
||||||
e.dnsServer, err = dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e.close()
|
e.close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
e.dnsServer = dnsServer
|
||||||
}
|
|
||||||
|
|
||||||
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, routes)
|
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, initialRoutes)
|
||||||
e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
|
e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
|
||||||
|
|
||||||
switch runtime.GOOS {
|
err = e.wgInterfaceCreate()
|
||||||
case "android":
|
|
||||||
err = e.wgInterface.CreateOnAndroid(iface.MobileIFaceArguments{
|
|
||||||
Routes: e.routeManager.InitialRouteRange(),
|
|
||||||
Dns: e.dnsServer.DnsIP(),
|
|
||||||
SearchDomains: e.dnsServer.SearchDomains(),
|
|
||||||
})
|
|
||||||
case "ios":
|
|
||||||
e.mobileDep.NetworkChangeListener.SetInterfaceIP(wgAddr)
|
|
||||||
err = e.wgInterface.CreateOniOS(e.mobileDep.FileDescriptor)
|
|
||||||
default:
|
|
||||||
err = e.wgInterface.Create()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed creating tunnel interface %s: [%s]", wgIFaceName, err.Error())
|
log.Errorf("failed creating tunnel interface %s: [%s]", e.config.WgIfaceName, err.Error())
|
||||||
e.close()
|
e.close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -257,33 +232,13 @@ func (e *Engine) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = e.wgInterface.Configure(myPrivateKey.String(), e.config.WgPort)
|
e.udpMux, err = e.wgInterface.Up()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed configuring Wireguard interface [%s]: %s", wgIFaceName, err.Error())
|
log.Errorf("failed to pull up wgInterface [%s]: %s", e.wgInterface.Name(), err.Error())
|
||||||
e.close()
|
e.close()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.wgInterface.IsUserspaceBind() {
|
|
||||||
iceBind := e.wgInterface.GetBind()
|
|
||||||
udpMux, err := iceBind.GetICEMux()
|
|
||||||
if err != nil {
|
|
||||||
e.close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.udpMux = udpMux
|
|
||||||
log.Infof("using userspace bind mode %s", udpMux.LocalAddr().String())
|
|
||||||
} else {
|
|
||||||
rawSock, err := sharedsock.Listen(e.config.WgPort, sharedsock.NewIncomingSTUNFilter())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
mux := bind.NewUniversalUDPMuxDefault(bind.UniversalUDPMuxParams{UDPConn: rawSock, Net: transportNet})
|
|
||||||
go mux.ReadFromConn(e.ctx)
|
|
||||||
e.udpMuxConn = rawSock
|
|
||||||
e.udpMux = mux
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.firewall != nil {
|
if e.firewall != nil {
|
||||||
e.acl = acl.NewDefaultManager(e.firewall)
|
e.acl = acl.NewDefaultManager(e.firewall)
|
||||||
}
|
}
|
||||||
@@ -425,7 +380,8 @@ func sendSignal(message *sProto.Message, s signal.Client) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SignalOfferAnswer signals either an offer or an answer to remote peer
|
// SignalOfferAnswer signals either an offer or an answer to remote peer
|
||||||
func SignalOfferAnswer(offerAnswer peer.OfferAnswer, myKey wgtypes.Key, remoteKey wgtypes.Key, s signal.Client, isAnswer bool) error {
|
func SignalOfferAnswer(offerAnswer peer.OfferAnswer, myKey wgtypes.Key, remoteKey wgtypes.Key, s signal.Client,
|
||||||
|
isAnswer bool) error {
|
||||||
var t sProto.Body_Type
|
var t sProto.Body_Type
|
||||||
if isAnswer {
|
if isAnswer {
|
||||||
t = sProto.Body_ANSWER
|
t = sProto.Body_ANSWER
|
||||||
@@ -436,7 +392,7 @@ func SignalOfferAnswer(offerAnswer peer.OfferAnswer, myKey wgtypes.Key, remoteKe
|
|||||||
msg, err := signal.MarshalCredential(myKey, offerAnswer.WgListenPort, remoteKey, &signal.Credential{
|
msg, err := signal.MarshalCredential(myKey, offerAnswer.WgListenPort, remoteKey, &signal.Credential{
|
||||||
UFrag: offerAnswer.IceCredentials.UFrag,
|
UFrag: offerAnswer.IceCredentials.UFrag,
|
||||||
Pwd: offerAnswer.IceCredentials.Pwd,
|
Pwd: offerAnswer.IceCredentials.Pwd,
|
||||||
}, t)
|
}, t, offerAnswer.RosenpassPubKey, offerAnswer.RosenpassAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -538,7 +494,7 @@ func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error {
|
|||||||
if conf.GetSshConfig() != nil {
|
if conf.GetSshConfig() != nil {
|
||||||
err := e.updateSSH(conf.GetSshConfig())
|
err := e.updateSSH(conf.GetSshConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("failed handling SSH server setup %v", e)
|
log.Warnf("failed handling SSH server setup %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -575,10 +531,10 @@ func (e *Engine) updateSTUNs(stuns []*mgmProto.HostConfig) error {
|
|||||||
if len(stuns) == 0 {
|
if len(stuns) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var newSTUNs []*ice.URL
|
var newSTUNs []*stun.URI
|
||||||
log.Debugf("got STUNs update from Management Service, updating")
|
log.Debugf("got STUNs update from Management Service, updating")
|
||||||
for _, stun := range stuns {
|
for _, s := range stuns {
|
||||||
url, err := ice.ParseURL(stun.Uri)
|
url, err := stun.ParseURI(s.Uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -593,10 +549,10 @@ func (e *Engine) updateTURNs(turns []*mgmProto.ProtectedHostConfig) error {
|
|||||||
if len(turns) == 0 {
|
if len(turns) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var newTURNs []*ice.URL
|
var newTURNs []*stun.URI
|
||||||
log.Debugf("got TURNs update from Management Service, updating")
|
log.Debugf("got TURNs update from Management Service, updating")
|
||||||
for _, turn := range turns {
|
for _, turn := range turns {
|
||||||
url, err := ice.ParseURL(turn.HostConfig.Uri)
|
url, err := stun.ParseURI(turn.HostConfig.Uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -689,6 +645,7 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
e.acl.ApplyFiltering(networkMap)
|
e.acl.ApplyFiltering(networkMap)
|
||||||
}
|
}
|
||||||
e.networkSerial = serial
|
e.networkSerial = serial
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -846,7 +803,7 @@ func (e *Engine) peerExists(peerKey string) bool {
|
|||||||
|
|
||||||
func (e *Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, error) {
|
func (e *Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, error) {
|
||||||
log.Debugf("creating peer connection %s", pubKey)
|
log.Debugf("creating peer connection %s", pubKey)
|
||||||
var stunTurn []*ice.URL
|
var stunTurn []*stun.URI
|
||||||
stunTurn = append(stunTurn, e.STUNs...)
|
stunTurn = append(stunTurn, e.STUNs...)
|
||||||
stunTurn = append(stunTurn, e.TURNs...)
|
stunTurn = append(stunTurn, e.TURNs...)
|
||||||
|
|
||||||
@@ -858,6 +815,26 @@ func (e *Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, e
|
|||||||
PreSharedKey: e.config.PreSharedKey,
|
PreSharedKey: e.config.PreSharedKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.config.RosenpassEnabled {
|
||||||
|
lk := []byte(e.config.WgPrivateKey.PublicKey().String())
|
||||||
|
rk := []byte(wgConfig.RemoteKey)
|
||||||
|
var keyInput []byte
|
||||||
|
if string(lk) > string(rk) {
|
||||||
|
//nolint:gocritic
|
||||||
|
keyInput = append(lk[:16], rk[:16]...)
|
||||||
|
} else {
|
||||||
|
//nolint:gocritic
|
||||||
|
keyInput = append(rk[:16], lk[:16]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := wgtypes.NewKey(keyInput)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
wgConfig.PreSharedKey = &key
|
||||||
|
}
|
||||||
|
|
||||||
// randomize connection timeout
|
// randomize connection timeout
|
||||||
timeout := time.Duration(rand.Intn(PeerConnectionTimeoutMax-PeerConnectionTimeoutMin)+PeerConnectionTimeoutMin) * time.Millisecond
|
timeout := time.Duration(rand.Intn(PeerConnectionTimeoutMax-PeerConnectionTimeoutMin)+PeerConnectionTimeoutMin) * time.Millisecond
|
||||||
config := peer.ConnConfig{
|
config := peer.ConnConfig{
|
||||||
@@ -873,6 +850,8 @@ func (e *Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, e
|
|||||||
LocalWgPort: e.config.WgPort,
|
LocalWgPort: e.config.WgPort,
|
||||||
NATExternalIPs: e.parseNATExternalIPMappings(),
|
NATExternalIPs: e.parseNATExternalIPMappings(),
|
||||||
UserspaceBind: e.wgInterface.IsUserspaceBind(),
|
UserspaceBind: e.wgInterface.IsUserspaceBind(),
|
||||||
|
RosenpassPubKey: e.getRosenpassPubKey(),
|
||||||
|
RosenpassAddr: e.getRosenpassAddr(),
|
||||||
}
|
}
|
||||||
|
|
||||||
peerConn, err := peer.NewConn(config, e.statusRecorder, e.wgProxyFactory, e.mobileDep.TunAdapter, e.mobileDep.IFaceDiscover)
|
peerConn, err := peer.NewConn(config, e.statusRecorder, e.wgProxyFactory, e.mobileDep.TunAdapter, e.mobileDep.IFaceDiscover)
|
||||||
@@ -904,6 +883,12 @@ func (e *Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, e
|
|||||||
return sendSignal(message, e.signal)
|
return sendSignal(message, e.signal)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if e.rpManager != nil {
|
||||||
|
|
||||||
|
peerConn.SetOnConnected(e.rpManager.OnConnected)
|
||||||
|
peerConn.SetOnDisconnected(e.rpManager.OnDisconnected)
|
||||||
|
}
|
||||||
|
|
||||||
return peerConn, nil
|
return peerConn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -929,6 +914,12 @@ func (e *Engine) receiveSignalEvents() {
|
|||||||
|
|
||||||
conn.RegisterProtoSupportMeta(msg.Body.GetFeaturesSupported())
|
conn.RegisterProtoSupportMeta(msg.Body.GetFeaturesSupported())
|
||||||
|
|
||||||
|
var rosenpassPubKey []byte
|
||||||
|
rosenpassAddr := ""
|
||||||
|
if msg.GetBody().GetRosenpassConfig() != nil {
|
||||||
|
rosenpassPubKey = msg.GetBody().GetRosenpassConfig().GetRosenpassPubKey()
|
||||||
|
rosenpassAddr = msg.GetBody().GetRosenpassConfig().GetRosenpassServerAddr()
|
||||||
|
}
|
||||||
conn.OnRemoteOffer(peer.OfferAnswer{
|
conn.OnRemoteOffer(peer.OfferAnswer{
|
||||||
IceCredentials: peer.IceCredentials{
|
IceCredentials: peer.IceCredentials{
|
||||||
UFrag: remoteCred.UFrag,
|
UFrag: remoteCred.UFrag,
|
||||||
@@ -936,6 +927,8 @@ func (e *Engine) receiveSignalEvents() {
|
|||||||
},
|
},
|
||||||
WgListenPort: int(msg.GetBody().GetWgListenPort()),
|
WgListenPort: int(msg.GetBody().GetWgListenPort()),
|
||||||
Version: msg.GetBody().GetNetBirdVersion(),
|
Version: msg.GetBody().GetNetBirdVersion(),
|
||||||
|
RosenpassPubKey: rosenpassPubKey,
|
||||||
|
RosenpassAddr: rosenpassAddr,
|
||||||
})
|
})
|
||||||
case sProto.Body_ANSWER:
|
case sProto.Body_ANSWER:
|
||||||
remoteCred, err := signal.UnMarshalCredential(msg)
|
remoteCred, err := signal.UnMarshalCredential(msg)
|
||||||
@@ -943,8 +936,14 @@ func (e *Engine) receiveSignalEvents() {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.RegisterProtoSupportMeta(msg.Body.GetFeaturesSupported())
|
conn.RegisterProtoSupportMeta(msg.GetBody().GetFeaturesSupported())
|
||||||
|
|
||||||
|
var rosenpassPubKey []byte
|
||||||
|
rosenpassAddr := ""
|
||||||
|
if msg.GetBody().GetRosenpassConfig() != nil {
|
||||||
|
rosenpassPubKey = msg.GetBody().GetRosenpassConfig().GetRosenpassPubKey()
|
||||||
|
rosenpassAddr = msg.GetBody().GetRosenpassConfig().GetRosenpassServerAddr()
|
||||||
|
}
|
||||||
conn.OnRemoteAnswer(peer.OfferAnswer{
|
conn.OnRemoteAnswer(peer.OfferAnswer{
|
||||||
IceCredentials: peer.IceCredentials{
|
IceCredentials: peer.IceCredentials{
|
||||||
UFrag: remoteCred.UFrag,
|
UFrag: remoteCred.UFrag,
|
||||||
@@ -952,6 +951,8 @@ func (e *Engine) receiveSignalEvents() {
|
|||||||
},
|
},
|
||||||
WgListenPort: int(msg.GetBody().GetWgListenPort()),
|
WgListenPort: int(msg.GetBody().GetWgListenPort()),
|
||||||
Version: msg.GetBody().GetNetBirdVersion(),
|
Version: msg.GetBody().GetNetBirdVersion(),
|
||||||
|
RosenpassPubKey: rosenpassPubKey,
|
||||||
|
RosenpassAddr: rosenpassAddr,
|
||||||
})
|
})
|
||||||
case sProto.Body_CANDIDATE:
|
case sProto.Body_CANDIDATE:
|
||||||
candidate, err := ice.UnmarshalCandidate(msg.GetBody().Payload)
|
candidate, err := ice.UnmarshalCandidate(msg.GetBody().Payload)
|
||||||
@@ -1041,18 +1042,6 @@ func (e *Engine) close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.udpMux != nil {
|
|
||||||
if err := e.udpMux.Close(); err != nil {
|
|
||||||
log.Debugf("close udp mux: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.udpMuxConn != nil {
|
|
||||||
if err := e.udpMuxConn.Close(); err != nil {
|
|
||||||
log.Debugf("close udp mux connection: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isNil(e.sshServer) {
|
if !isNil(e.sshServer) {
|
||||||
err := e.sshServer.Stop()
|
err := e.sshServer.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1074,6 +1063,10 @@ func (e *Engine) close() {
|
|||||||
log.Warnf("failed to reset firewall: %s", err)
|
log.Warnf("failed to reset firewall: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.rpManager != nil {
|
||||||
|
_ = e.rpManager.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
|
func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
|
||||||
@@ -1086,6 +1079,68 @@ func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
|
|||||||
return routes, &dnsCfg, nil
|
return routes, &dnsCfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Engine) newWgIface() (*iface.WGIface, error) {
|
||||||
|
transportNet, err := e.newStdNet()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to create pion's stdnet: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mArgs *iface.MobileIFaceArguments
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "android":
|
||||||
|
mArgs = &iface.MobileIFaceArguments{
|
||||||
|
TunAdapter: e.mobileDep.TunAdapter,
|
||||||
|
TunFd: int(e.mobileDep.FileDescriptor),
|
||||||
|
}
|
||||||
|
case "ios":
|
||||||
|
mArgs = &iface.MobileIFaceArguments{
|
||||||
|
TunFd: int(e.mobileDep.FileDescriptor),
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return iface.NewWGIFace(e.config.WgIfaceName, e.config.WgAddr, e.config.WgPort, e.config.WgPrivateKey.String(), iface.DefaultMTU, transportNet, mArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) wgInterfaceCreate() (err error) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "android":
|
||||||
|
err = e.wgInterface.CreateOnAndroid(e.routeManager.InitialRouteRange(), e.dnsServer.DnsIP(), e.dnsServer.SearchDomains())
|
||||||
|
case "ios":
|
||||||
|
e.mobileDep.NetworkChangeListener.SetInterfaceIP(e.config.WgAddr)
|
||||||
|
err = e.wgInterface.Create()
|
||||||
|
default:
|
||||||
|
err = e.wgInterface.Create()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) newDnsServer() ([]*route.Route, dns.Server, error) {
|
||||||
|
// due to tests where we are using a mocked version of the DNS server
|
||||||
|
if e.dnsServer != nil {
|
||||||
|
return nil, e.dnsServer, nil
|
||||||
|
}
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "android":
|
||||||
|
routes, dnsConfig, err := e.readInitialSettings()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
dnsServer := dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses, *dnsConfig, e.mobileDep.NetworkChangeListener)
|
||||||
|
go e.mobileDep.DnsReadyListener.OnReady()
|
||||||
|
return routes, dnsServer, nil
|
||||||
|
case "ios":
|
||||||
|
dnsServer := dns.NewDefaultServerIos(e.ctx, e.wgInterface, e.mobileDep.DnsManager)
|
||||||
|
return nil, dnsServer, nil
|
||||||
|
default:
|
||||||
|
dnsServer, err := dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return nil, dnsServer, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func findIPFromInterfaceName(ifaceName string) (net.IP, error) {
|
func findIPFromInterfaceName(ifaceName string) (net.IP, error) {
|
||||||
iface, err := net.InterfaceByName(ifaceName)
|
iface, err := net.InterfaceByName(ifaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1106,3 +1161,17 @@ func findIPFromInterface(iface *net.Interface) (net.IP, error) {
|
|||||||
}
|
}
|
||||||
return nil, fmt.Errorf("interface %s don't have an ipv4 address", iface.Name)
|
return nil, fmt.Errorf("interface %s don't have an ipv4 address", iface.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Engine) getRosenpassPubKey() []byte {
|
||||||
|
if e.rpManager != nil {
|
||||||
|
return e.rpManager.GetPubKey()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Engine) getRosenpassAddr() string {
|
||||||
|
if e.rpManager != nil {
|
||||||
|
return e.rpManager.GetAddress().String()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/transport/v2/stdnet"
|
"github.com/pion/transport/v3/stdnet"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -213,7 +213,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", iface.DefaultMTU, nil, newNet)
|
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", engine.config.WgPort, key.String(), iface.DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -567,7 +567,7 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU, nil, newNet)
|
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, engine.config.WgPort, key.String(), iface.DefaultMTU, newNet, nil)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
input := struct {
|
input := struct {
|
||||||
inputSerial uint64
|
inputSerial uint64
|
||||||
@@ -736,7 +736,7 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU, nil, newNet)
|
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, 33100, key.String(), iface.DefaultMTU, newNet, nil)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
mockRouteManager := &routemanager.MockManager{
|
mockRouteManager := &routemanager.MockManager{
|
||||||
|
|||||||
@@ -9,11 +9,14 @@ import (
|
|||||||
|
|
||||||
// MobileDependency collect all dependencies for mobile platform
|
// MobileDependency collect all dependencies for mobile platform
|
||||||
type MobileDependency struct {
|
type MobileDependency struct {
|
||||||
|
// Android only
|
||||||
TunAdapter iface.TunAdapter
|
TunAdapter iface.TunAdapter
|
||||||
IFaceDiscover stdnet.ExternalIFaceDiscover
|
IFaceDiscover stdnet.ExternalIFaceDiscover
|
||||||
NetworkChangeListener listener.NetworkChangeListener
|
NetworkChangeListener listener.NetworkChangeListener
|
||||||
HostDNSAddresses []string
|
HostDNSAddresses []string
|
||||||
DnsReadyListener dns.ReadyListener
|
DnsReadyListener dns.ReadyListener
|
||||||
|
|
||||||
|
// iOS only
|
||||||
DnsManager dns.IosDnsManager
|
DnsManager dns.IosDnsManager
|
||||||
FileDescriptor int32
|
FileDescriptor int32
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/ice/v2"
|
"github.com/pion/ice/v3"
|
||||||
|
"github.com/pion/stun/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ type ConnConfig struct {
|
|||||||
LocalKey string
|
LocalKey string
|
||||||
|
|
||||||
// StunTurn is a list of STUN and TURN URLs
|
// StunTurn is a list of STUN and TURN URLs
|
||||||
StunTurn []*ice.URL
|
StunTurn []*stun.URI
|
||||||
|
|
||||||
// InterfaceBlackList is a list of machine interfaces that should be filtered out by ICE Candidate gathering
|
// InterfaceBlackList is a list of machine interfaces that should be filtered out by ICE Candidate gathering
|
||||||
// (e.g. if eth0 is in the list, host candidate of this interface won't be used)
|
// (e.g. if eth0 is in the list, host candidate of this interface won't be used)
|
||||||
@@ -66,6 +67,11 @@ type ConnConfig struct {
|
|||||||
|
|
||||||
// UsesBind indicates whether the WireGuard interface is userspace and uses bind.ICEBind
|
// UsesBind indicates whether the WireGuard interface is userspace and uses bind.ICEBind
|
||||||
UserspaceBind bool
|
UserspaceBind bool
|
||||||
|
|
||||||
|
// RosenpassPubKey is this peer's Rosenpass public key
|
||||||
|
RosenpassPubKey []byte
|
||||||
|
// RosenpassPubKey is this peer's RosenpassAddr server address (IP:port)
|
||||||
|
RosenpassAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
// OfferAnswer represents a session establishment offer or answer
|
// OfferAnswer represents a session establishment offer or answer
|
||||||
@@ -78,6 +84,12 @@ type OfferAnswer struct {
|
|||||||
|
|
||||||
// Version of NetBird Agent
|
// Version of NetBird Agent
|
||||||
Version string
|
Version string
|
||||||
|
// RosenpassPubKey is the Rosenpass public key of the remote peer when receiving this message
|
||||||
|
// This value is the local Rosenpass server public key when sending the message
|
||||||
|
RosenpassPubKey []byte
|
||||||
|
// RosenpassAddr is the Rosenpass server address (IP:port) of the remote peer when receiving this message
|
||||||
|
// This value is the local Rosenpass server address when sending the message
|
||||||
|
RosenpassAddr string
|
||||||
}
|
}
|
||||||
|
|
||||||
// IceCredentials ICE protocol credentials struct
|
// IceCredentials ICE protocol credentials struct
|
||||||
@@ -96,6 +108,8 @@ type Conn struct {
|
|||||||
signalOffer func(OfferAnswer) error
|
signalOffer func(OfferAnswer) error
|
||||||
signalAnswer func(OfferAnswer) error
|
signalAnswer func(OfferAnswer) error
|
||||||
sendSignalMessage func(message *sProto.Message) error
|
sendSignalMessage func(message *sProto.Message) error
|
||||||
|
onConnected func(remoteWireGuardKey string, remoteRosenpassPubKey []byte, wireGuardIP string, remoteRosenpassAddr string)
|
||||||
|
onDisconnected func(remotePeer string, wgIP string)
|
||||||
|
|
||||||
// remoteOffersCh is a channel used to wait for remote credentials to proceed with the connection
|
// remoteOffersCh is a channel used to wait for remote credentials to proceed with the connection
|
||||||
remoteOffersCh chan OfferAnswer
|
remoteOffersCh chan OfferAnswer
|
||||||
@@ -142,7 +156,7 @@ func (conn *Conn) WgConfig() WgConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStunTurn update the turn and stun addresses
|
// UpdateStunTurn update the turn and stun addresses
|
||||||
func (conn *Conn) UpdateStunTurn(turnStun []*ice.URL) {
|
func (conn *Conn) UpdateStunTurn(turnStun []*stun.URI) {
|
||||||
conn.config.StunTurn = turnStun
|
conn.config.StunTurn = turnStun
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +348,8 @@ func (conn *Conn) Open() error {
|
|||||||
remoteWgPort = remoteOfferAnswer.WgListenPort
|
remoteWgPort = remoteOfferAnswer.WgListenPort
|
||||||
}
|
}
|
||||||
// the ice connection has been established successfully so we are ready to start the proxy
|
// the ice connection has been established successfully so we are ready to start the proxy
|
||||||
remoteAddr, err := conn.configureConnection(remoteConn, remoteWgPort)
|
remoteAddr, err := conn.configureConnection(remoteConn, remoteWgPort, remoteOfferAnswer.RosenpassPubKey,
|
||||||
|
remoteOfferAnswer.RosenpassAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -357,7 +372,7 @@ func isRelayCandidate(candidate ice.Candidate) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// configureConnection starts proxying traffic from/to local Wireguard and sets connection status to StatusConnected
|
// configureConnection starts proxying traffic from/to local Wireguard and sets connection status to StatusConnected
|
||||||
func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int) (net.Addr, error) {
|
func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int, remoteRosenpassPubKey []byte, remoteRosenpassAddr string) (net.Addr, error) {
|
||||||
conn.mu.Lock()
|
conn.mu.Lock()
|
||||||
defer conn.mu.Unlock()
|
defer conn.mu.Unlock()
|
||||||
|
|
||||||
@@ -375,7 +390,7 @@ func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int) (ne
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// To support old version's with direct mode we attempt to punch an additional role with the remote wireguard port
|
// To support old version's with direct mode we attempt to punch an additional role with the remote WireGuard port
|
||||||
go conn.punchRemoteWGPort(pair, remoteWgPort)
|
go conn.punchRemoteWGPort(pair, remoteWgPort)
|
||||||
endpoint = remoteConn.RemoteAddr()
|
endpoint = remoteConn.RemoteAddr()
|
||||||
}
|
}
|
||||||
@@ -409,6 +424,15 @@ func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int) (ne
|
|||||||
log.Warnf("unable to save peer's state, got error: %v", err)
|
log.Warnf("unable to save peer's state, got error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, ipNet, err := net.ParseCIDR(conn.config.WgConfig.AllowedIps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if conn.onConnected != nil {
|
||||||
|
conn.onConnected(conn.config.Key, remoteRosenpassPubKey, ipNet.IP.String(), remoteRosenpassAddr)
|
||||||
|
}
|
||||||
|
|
||||||
return endpoint, nil
|
return endpoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,6 +483,10 @@ func (conn *Conn) cleanup() error {
|
|||||||
conn.notifyDisconnected = nil
|
conn.notifyDisconnected = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conn.status == StatusConnected && conn.onDisconnected != nil {
|
||||||
|
conn.onDisconnected(conn.config.WgConfig.RemoteKey, conn.config.WgConfig.AllowedIps)
|
||||||
|
}
|
||||||
|
|
||||||
conn.status = StatusDisconnected
|
conn.status = StatusDisconnected
|
||||||
|
|
||||||
peerState := State{
|
peerState := State{
|
||||||
@@ -488,6 +516,16 @@ func (conn *Conn) SetSignalOffer(handler func(offer OfferAnswer) error) {
|
|||||||
conn.signalOffer = handler
|
conn.signalOffer = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetOnConnected sets a handler function to be triggered by Conn when a new connection to a remote peer established
|
||||||
|
func (conn *Conn) SetOnConnected(handler func(remoteWireGuardKey string, remoteRosenpassPubKey []byte, wireGuardIP string, remoteRosenpassAddr string)) {
|
||||||
|
conn.onConnected = handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOnDisconnected sets a handler function to be triggered by Conn when a connection to a remote disconnected
|
||||||
|
func (conn *Conn) SetOnDisconnected(handler func(remotePeer string, wgIP string)) {
|
||||||
|
conn.onDisconnected = handler
|
||||||
|
}
|
||||||
|
|
||||||
// SetSignalAnswer sets a handler function to be triggered by Conn when a new connection answer has to be signalled to the remote peer
|
// SetSignalAnswer sets a handler function to be triggered by Conn when a new connection answer has to be signalled to the remote peer
|
||||||
func (conn *Conn) SetSignalAnswer(handler func(answer OfferAnswer) error) {
|
func (conn *Conn) SetSignalAnswer(handler func(answer OfferAnswer) error) {
|
||||||
conn.signalAnswer = handler
|
conn.signalAnswer = handler
|
||||||
@@ -545,6 +583,8 @@ func (conn *Conn) sendAnswer() error {
|
|||||||
IceCredentials: IceCredentials{localUFrag, localPwd},
|
IceCredentials: IceCredentials{localUFrag, localPwd},
|
||||||
WgListenPort: conn.config.LocalWgPort,
|
WgListenPort: conn.config.LocalWgPort,
|
||||||
Version: version.NetbirdVersion(),
|
Version: version.NetbirdVersion(),
|
||||||
|
RosenpassPubKey: conn.config.RosenpassPubKey,
|
||||||
|
RosenpassAddr: conn.config.RosenpassAddr,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -566,6 +606,8 @@ func (conn *Conn) sendOffer() error {
|
|||||||
IceCredentials: IceCredentials{localUFrag, localPwd},
|
IceCredentials: IceCredentials{localUFrag, localPwd},
|
||||||
WgListenPort: conn.config.LocalWgPort,
|
WgListenPort: conn.config.LocalWgPort,
|
||||||
Version: version.NetbirdVersion(),
|
Version: version.NetbirdVersion(),
|
||||||
|
RosenpassPubKey: conn.config.RosenpassPubKey,
|
||||||
|
RosenpassAddr: conn.config.RosenpassAddr,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/magiconair/properties/assert"
|
"github.com/magiconair/properties/assert"
|
||||||
"github.com/pion/ice/v2"
|
"github.com/pion/stun/v2"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
"github.com/netbirdio/netbird/client/internal/wgproxy"
|
"github.com/netbirdio/netbird/client/internal/wgproxy"
|
||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
var connConf = ConnConfig{
|
var connConf = ConnConfig{
|
||||||
Key: "LLHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
Key: "LLHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
||||||
LocalKey: "RRHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
LocalKey: "RRHf3Ma6z6mdLbriAJbqhX7+nM/B71lgw2+91q3LfhU=",
|
||||||
StunTurn: []*ice.URL{},
|
StunTurn: []*stun.URI{},
|
||||||
InterfaceBlackList: nil,
|
InterfaceBlackList: nil,
|
||||||
Timeout: time.Second,
|
Timeout: time.Second,
|
||||||
LocalWgPort: 51820,
|
LocalWgPort: 51820,
|
||||||
|
|||||||
204
client/internal/rosenpass/manager.go
Normal file
204
client/internal/rosenpass/manager.go
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
package rosenpass
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
rp "cunicu.li/go-rosenpass"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hashRosenpassKey(key []byte) string {
|
||||||
|
hasher := sha256.New()
|
||||||
|
hasher.Write(key)
|
||||||
|
return hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
ifaceName string
|
||||||
|
spk []byte
|
||||||
|
ssk []byte
|
||||||
|
rpKeyHash string
|
||||||
|
preSharedKey *[32]byte
|
||||||
|
rpPeerIDs map[string]*rp.PeerID
|
||||||
|
rpWgHandler *NetbirdHandler
|
||||||
|
server *rp.Server
|
||||||
|
lock sync.Mutex
|
||||||
|
port int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewManager creates a new Rosenpass manager
|
||||||
|
func NewManager(preSharedKey *wgtypes.Key, wgIfaceName string) (*Manager, error) {
|
||||||
|
public, secret, err := rp.GenerateKeyPair()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rpKeyHash := hashRosenpassKey(public)
|
||||||
|
log.Debugf("generated new rosenpass key pair with public key %s", rpKeyHash)
|
||||||
|
return &Manager{ifaceName: wgIfaceName, rpKeyHash: rpKeyHash, spk: public, ssk: secret, preSharedKey: (*[32]byte)(preSharedKey), rpPeerIDs: make(map[string]*rp.PeerID), lock: sync.Mutex{}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) GetPubKey() []byte {
|
||||||
|
return m.spk
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAddress returns the address of the Rosenpass server
|
||||||
|
func (m *Manager) GetAddress() *net.UDPAddr {
|
||||||
|
return &net.UDPAddr{Port: m.port}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPeer adds a new peer to the Rosenpass server
|
||||||
|
func (m *Manager) addPeer(rosenpassPubKey []byte, rosenpassAddr string, wireGuardIP string, wireGuardPubKey string) error {
|
||||||
|
var err error
|
||||||
|
pcfg := rp.PeerConfig{PublicKey: rosenpassPubKey}
|
||||||
|
if m.preSharedKey != nil {
|
||||||
|
pcfg.PresharedKey = *m.preSharedKey
|
||||||
|
}
|
||||||
|
if bytes.Compare(m.spk, rosenpassPubKey) == 1 {
|
||||||
|
_, strPort, err := net.SplitHostPort(rosenpassAddr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse rosenpass address: %w", err)
|
||||||
|
}
|
||||||
|
peerAddr := fmt.Sprintf("%s:%s", wireGuardIP, strPort)
|
||||||
|
if pcfg.Endpoint, err = net.ResolveUDPAddr("udp", peerAddr); err != nil {
|
||||||
|
return fmt.Errorf("failed to resolve peer endpoint address: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peerID, err := m.server.AddPeer(pcfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
key, err := wgtypes.ParseKey(wireGuardPubKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.rpWgHandler.AddPeer(peerID, m.ifaceName, rp.Key(key))
|
||||||
|
m.rpPeerIDs[wireGuardPubKey] = &peerID
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// removePeer removes a peer from the Rosenpass server
|
||||||
|
func (m *Manager) removePeer(wireGuardPubKey string) error {
|
||||||
|
err := m.server.RemovePeer(*m.rpPeerIDs[wireGuardPubKey])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.rpWgHandler.RemovePeer(*m.rpPeerIDs[wireGuardPubKey])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) generateConfig() (rp.Config, error) {
|
||||||
|
opts := &slog.HandlerOptions{
|
||||||
|
Level: slog.LevelDebug,
|
||||||
|
}
|
||||||
|
logger := slog.New(slog.NewTextHandler(os.Stdout, opts))
|
||||||
|
cfg := rp.Config{Logger: logger}
|
||||||
|
|
||||||
|
cfg.PublicKey = m.spk
|
||||||
|
cfg.SecretKey = m.ssk
|
||||||
|
|
||||||
|
cfg.Peers = []rp.PeerConfig{}
|
||||||
|
m.rpWgHandler, _ = NewNetbirdHandler(m.preSharedKey, m.ifaceName)
|
||||||
|
|
||||||
|
cfg.Handlers = []rp.Handler{m.rpWgHandler}
|
||||||
|
|
||||||
|
port, err := findRandomAvailableUDPPort()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("could not determine a random port for rosenpass server. Error: %s", err)
|
||||||
|
return rp.Config{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.port = port
|
||||||
|
|
||||||
|
cfg.ListenAddrs = []*net.UDPAddr{m.GetAddress()}
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) OnDisconnected(peerKey string, wgIP string) {
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
if _, ok := m.rpPeerIDs[peerKey]; !ok {
|
||||||
|
// if we didn't have this peer yet, just skip
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := m.removePeer(peerKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to remove rosenpass peer", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(m.rpPeerIDs, peerKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run starts the Rosenpass server
|
||||||
|
func (m *Manager) Run() error {
|
||||||
|
conf, err := m.generateConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.server, err = rp.NewUDPServer(conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("starting rosenpass server on port %d", m.port)
|
||||||
|
|
||||||
|
return m.server.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the Rosenpass server
|
||||||
|
func (m *Manager) Close() error {
|
||||||
|
if m.server != nil {
|
||||||
|
err := m.server.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed closing local rosenpass server")
|
||||||
|
}
|
||||||
|
m.server = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnConnected is a handler function that is triggered when a connection to a remote peer establishes
|
||||||
|
func (m *Manager) OnConnected(remoteWireGuardKey string, remoteRosenpassPubKey []byte, wireGuardIP string, remoteRosenpassAddr string) {
|
||||||
|
m.lock.Lock()
|
||||||
|
defer m.lock.Unlock()
|
||||||
|
|
||||||
|
if remoteRosenpassPubKey == nil {
|
||||||
|
log.Warnf("remote peer with public key %s does not support rosenpass", remoteWireGuardKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rpKeyHash := hashRosenpassKey(remoteRosenpassPubKey)
|
||||||
|
log.Debugf("received remote rosenpass key %s, my key %s", rpKeyHash, m.rpKeyHash)
|
||||||
|
|
||||||
|
err := m.addPeer(remoteRosenpassPubKey, remoteRosenpassAddr, wireGuardIP, remoteWireGuardKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to add rosenpass peer: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func findRandomAvailableUDPPort() (int, error) {
|
||||||
|
conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("could not find an available UDP port: %w", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
splitAddress := strings.Split(conn.LocalAddr().String(), ":")
|
||||||
|
return strconv.Atoi(splitAddress[len(splitAddress)-1])
|
||||||
|
}
|
||||||
126
client/internal/rosenpass/netbird_handler.go
Normal file
126
client/internal/rosenpass/netbird_handler.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package rosenpass
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
rp "cunicu.li/go-rosenpass"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wireGuardPeer struct {
|
||||||
|
Interface string
|
||||||
|
PublicKey rp.Key
|
||||||
|
}
|
||||||
|
|
||||||
|
type NetbirdHandler struct {
|
||||||
|
ifaceName string
|
||||||
|
client *wgctrl.Client
|
||||||
|
peers map[rp.PeerID]wireGuardPeer
|
||||||
|
presharedKey [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNetbirdHandler(preSharedKey *[32]byte, wgIfaceName string) (hdlr *NetbirdHandler, err error) {
|
||||||
|
hdlr = &NetbirdHandler{
|
||||||
|
ifaceName: wgIfaceName,
|
||||||
|
peers: map[rp.PeerID]wireGuardPeer{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if preSharedKey != nil {
|
||||||
|
hdlr.presharedKey = *preSharedKey
|
||||||
|
}
|
||||||
|
|
||||||
|
if hdlr.client, err = wgctrl.New(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to creat WireGuard client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hdlr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NetbirdHandler) AddPeer(pid rp.PeerID, intf string, pk rp.Key) {
|
||||||
|
h.peers[pid] = wireGuardPeer{
|
||||||
|
Interface: intf,
|
||||||
|
PublicKey: pk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NetbirdHandler) RemovePeer(pid rp.PeerID) {
|
||||||
|
delete(h.peers, pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NetbirdHandler) HandshakeCompleted(pid rp.PeerID, key rp.Key) {
|
||||||
|
log.Debug("Handshake complete")
|
||||||
|
h.outputKey(rp.KeyOutputReasonStale, pid, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NetbirdHandler) HandshakeExpired(pid rp.PeerID) {
|
||||||
|
key, _ := rp.GeneratePresharedKey()
|
||||||
|
log.Debug("Handshake expired")
|
||||||
|
h.outputKey(rp.KeyOutputReasonStale, pid, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *NetbirdHandler) outputKey(_ rp.KeyOutputReason, pid rp.PeerID, psk rp.Key) {
|
||||||
|
wg, ok := h.peers[pid]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
device, err := h.client.Device(h.ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to get WireGuard device: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config := []wgtypes.PeerConfig{
|
||||||
|
{
|
||||||
|
UpdateOnly: true,
|
||||||
|
PublicKey: wgtypes.Key(wg.PublicKey),
|
||||||
|
PresharedKey: (*wgtypes.Key)(&psk),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, peer := range device.Peers {
|
||||||
|
if peer.PublicKey == wgtypes.Key(wg.PublicKey) {
|
||||||
|
if publicKeyEmpty(peer.PresharedKey) || peer.PresharedKey == h.presharedKey {
|
||||||
|
log.Debugf("Restart wireguard connection to peer %s", peer.PublicKey)
|
||||||
|
config = []wgtypes.PeerConfig{
|
||||||
|
{
|
||||||
|
PublicKey: wgtypes.Key(wg.PublicKey),
|
||||||
|
PresharedKey: (*wgtypes.Key)(&psk),
|
||||||
|
Endpoint: peer.Endpoint,
|
||||||
|
AllowedIPs: peer.AllowedIPs,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = h.client.ConfigureDevice(wg.Interface, wgtypes.Config{
|
||||||
|
Peers: []wgtypes.PeerConfig{
|
||||||
|
{
|
||||||
|
Remove: true,
|
||||||
|
PublicKey: wgtypes.Key(wg.PublicKey),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
slog.Debug("Failed to remove peer")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = h.client.ConfigureDevice(wg.Interface, wgtypes.Config{
|
||||||
|
Peers: config,
|
||||||
|
}); err != nil {
|
||||||
|
log.Errorf("Failed to apply rosenpass key: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func publicKeyEmpty(key wgtypes.Key) bool {
|
||||||
|
for _, b := range key {
|
||||||
|
if b != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -7,7 +7,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pion/transport/v2/stdnet"
|
"github.com/pion/transport/v3/stdnet"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
@@ -399,12 +400,12 @@ func TestManagerUpdateRoutes(t *testing.T) {
|
|||||||
|
|
||||||
for n, testCase := range testCases {
|
for n, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
peerPrivateKey, _ := wgtypes.GeneratePrivateKey()
|
||||||
newNet, err := stdnet.NewNet()
|
newNet, err := stdnet.NewNet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun43%d", n), "100.65.65.2/24", iface.DefaultMTU, nil, newNet)
|
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun43%d", n), "100.65.65.2/24", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
|
||||||
require.NoError(t, err, "should create testing WGIface interface")
|
require.NoError(t, err, "should create testing WGIface interface")
|
||||||
defer wgInterface.Close()
|
defer wgInterface.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func (n *notifier) onNewRoutes(idMap map[string][]*route.Route) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(newNets)
|
sort.Strings(newNets)
|
||||||
if !n.hasDiff(n.routeRangers, newNets) {
|
if !n.hasDiff(n.initialRouteRangers, newNets) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pion/transport/v2/stdnet"
|
"github.com/pion/transport/v3/stdnet"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
)
|
)
|
||||||
@@ -41,11 +42,12 @@ func TestAddRemoveRoutes(t *testing.T) {
|
|||||||
|
|
||||||
for n, testCase := range testCases {
|
for n, testCase := range testCases {
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
peerPrivateKey, _ := wgtypes.GeneratePrivateKey()
|
||||||
newNet, err := stdnet.NewNet()
|
newNet, err := stdnet.NewNet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", iface.DefaultMTU, nil, newNet)
|
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
|
||||||
require.NoError(t, err, "should create testing WGIface interface")
|
require.NoError(t, err, "should create testing WGIface interface")
|
||||||
defer wgInterface.Close()
|
defer wgInterface.Close()
|
||||||
|
|
||||||
@@ -175,11 +177,12 @@ func TestAddExistAndRemoveRouteNonAndroid(t *testing.T) {
|
|||||||
log.SetOutput(os.Stderr)
|
log.SetOutput(os.Stderr)
|
||||||
}()
|
}()
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
peerPrivateKey, _ := wgtypes.GeneratePrivateKey()
|
||||||
newNet, err := stdnet.NewNet()
|
newNet, err := stdnet.NewNet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", iface.DefaultMTU, nil, newNet)
|
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
|
||||||
require.NoError(t, err, "should create testing WGIface interface")
|
require.NoError(t, err, "should create testing WGIface interface")
|
||||||
defer wgInterface.Close()
|
defer wgInterface.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package stdnet
|
package stdnet
|
||||||
|
|
||||||
import "github.com/pion/transport/v2"
|
import "github.com/pion/transport/v3"
|
||||||
|
|
||||||
// ExternalIFaceDiscover provide an option for external services (mobile)
|
// ExternalIFaceDiscover provide an option for external services (mobile)
|
||||||
// to collect network interface information
|
// to collect network interface information
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v3"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package stdnet
|
|||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pionDiscover struct {
|
type pionDiscover struct {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ package stdnet
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v3"
|
||||||
"github.com/pion/transport/v2/stdnet"
|
"github.com/pion/transport/v3/stdnet"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Net is an implementation of the net.Net interface
|
// Net is an implementation of the net.Net interface
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ func (a *Auth) SaveConfigIfSSOSupported() (bool, error) {
|
|||||||
supportsSSO := true
|
supportsSSO := true
|
||||||
err := a.withBackOff(a.ctx, func() (err error) {
|
err := a.withBackOff(a.ctx, func() (err error) {
|
||||||
_, err = internal.GetDeviceAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
|
_, err = internal.GetDeviceAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
|
||||||
if s, ok := gstatus.FromError(err); ok && s.Code() == codes.NotFound {
|
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.NotFound || s.Code() == codes.Unimplemented) {
|
||||||
_, err = internal.GetPKCEAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
|
_, err = internal.GetPKCEAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
|
||||||
if s, ok := gstatus.FromError(err); ok && s.Code() == codes.NotFound {
|
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.NotFound || s.Code() == codes.Unimplemented) {
|
||||||
supportsSSO = false
|
supportsSSO = false
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
<Shortcut Id="NetbirdStartMenuShortcut" Directory="StartMenuFolder" Name="NetBird" WorkingDirectory="NetbirdInstallDir" Icon="NetbirdIcon" />
|
<Shortcut Id="NetbirdStartMenuShortcut" Directory="StartMenuFolder" Name="NetBird" WorkingDirectory="NetbirdInstallDir" Icon="NetbirdIcon" />
|
||||||
</File>
|
</File>
|
||||||
<File ProcessorArchitecture="x64" Source=".\dist\netbird_windows_amd64\wintun.dll" />
|
<File ProcessorArchitecture="x64" Source=".\dist\netbird_windows_amd64\wintun.dll" />
|
||||||
|
<File ProcessorArchitecture="x64" Source=".\dist\netbird_windows_amd64\opengl32.dll" />
|
||||||
|
|
||||||
<ServiceInstall
|
<ServiceInstall
|
||||||
Id="NetBirdService"
|
Id="NetBirdService"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.26.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v4.23.4
|
// protoc v3.21.9
|
||||||
// source: daemon.proto
|
// source: daemon.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
@@ -44,6 +44,9 @@ type LoginRequest struct {
|
|||||||
CustomDNSAddress []byte `protobuf:"bytes,7,opt,name=customDNSAddress,proto3" json:"customDNSAddress,omitempty"`
|
CustomDNSAddress []byte `protobuf:"bytes,7,opt,name=customDNSAddress,proto3" json:"customDNSAddress,omitempty"`
|
||||||
IsLinuxDesktopClient bool `protobuf:"varint,8,opt,name=isLinuxDesktopClient,proto3" json:"isLinuxDesktopClient,omitempty"`
|
IsLinuxDesktopClient bool `protobuf:"varint,8,opt,name=isLinuxDesktopClient,proto3" json:"isLinuxDesktopClient,omitempty"`
|
||||||
Hostname string `protobuf:"bytes,9,opt,name=hostname,proto3" json:"hostname,omitempty"`
|
Hostname string `protobuf:"bytes,9,opt,name=hostname,proto3" json:"hostname,omitempty"`
|
||||||
|
RosenpassEnabled *bool `protobuf:"varint,10,opt,name=rosenpassEnabled,proto3,oneof" json:"rosenpassEnabled,omitempty"`
|
||||||
|
InterfaceName *string `protobuf:"bytes,11,opt,name=interfaceName,proto3,oneof" json:"interfaceName,omitempty"`
|
||||||
|
WireguardPort *int64 `protobuf:"varint,12,opt,name=wireguardPort,proto3,oneof" json:"wireguardPort,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *LoginRequest) Reset() {
|
func (x *LoginRequest) Reset() {
|
||||||
@@ -141,6 +144,27 @@ func (x *LoginRequest) GetHostname() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *LoginRequest) GetRosenpassEnabled() bool {
|
||||||
|
if x != nil && x.RosenpassEnabled != nil {
|
||||||
|
return *x.RosenpassEnabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginRequest) GetInterfaceName() string {
|
||||||
|
if x != nil && x.InterfaceName != nil {
|
||||||
|
return *x.InterfaceName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginRequest) GetWireguardPort() int64 {
|
||||||
|
if x != nil && x.WireguardPort != nil {
|
||||||
|
return *x.WireguardPort
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type LoginResponse struct {
|
type LoginResponse struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -1067,7 +1091,7 @@ var file_daemon_proto_rawDesc = []byte{
|
|||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
||||||
0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||||
0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe6, 0x02, 0x0a, 0x0c, 0x4c, 0x6f,
|
0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa6, 0x04, 0x0a, 0x0c, 0x4c, 0x6f,
|
||||||
0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65,
|
0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65,
|
||||||
0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65,
|
0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65,
|
||||||
0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61,
|
0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61,
|
||||||
@@ -1090,7 +1114,19 @@ var file_daemon_proto_rawDesc = []byte{
|
|||||||
0x52, 0x14, 0x69, 0x73, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70,
|
0x52, 0x14, 0x69, 0x73, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70,
|
||||||
0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61,
|
0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61,
|
||||||
0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61,
|
0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61,
|
||||||
0x6d, 0x65, 0x22, 0xb5, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70,
|
0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45,
|
||||||
|
0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x10,
|
||||||
|
0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||||
|
0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65,
|
||||||
|
0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0d, 0x69, 0x6e,
|
||||||
|
0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x29,
|
||||||
|
0x0a, 0x0d, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x18,
|
||||||
|
0x0c, 0x20, 0x01, 0x28, 0x03, 0x48, 0x02, 0x52, 0x0d, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61,
|
||||||
|
0x72, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x72, 0x6f,
|
||||||
|
0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x10,
|
||||||
|
0x0a, 0x0e, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65,
|
||||||
|
0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x50, 0x6f,
|
||||||
|
0x72, 0x74, 0x22, 0xb5, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70,
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x53, 0x53, 0x4f,
|
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x53, 0x53, 0x4f,
|
||||||
0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x65, 0x65,
|
0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x6e, 0x65, 0x65,
|
||||||
0x64, 0x73, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73,
|
0x64, 0x73, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73,
|
||||||
@@ -1486,6 +1522,7 @@ func file_daemon_proto_init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
file_daemon_proto_msgTypes[0].OneofWrappers = []interface{}{}
|
||||||
type x struct{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
|
|||||||
@@ -54,6 +54,13 @@ message LoginRequest {
|
|||||||
bool isLinuxDesktopClient = 8;
|
bool isLinuxDesktopClient = 8;
|
||||||
|
|
||||||
string hostname = 9;
|
string hostname = 9;
|
||||||
|
|
||||||
|
optional bool rosenpassEnabled = 10;
|
||||||
|
|
||||||
|
optional string interfaceName = 11;
|
||||||
|
|
||||||
|
optional int64 wireguardPort = 12;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message LoginResponse {
|
message LoginResponse {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ func (s *Server) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if configuration exists, we just start connections.
|
// if configuration exists, we just start connections.
|
||||||
config, _ = internal.UpdateOldManagementPort(ctx, config, s.latestConfigInput.ConfigPath)
|
config, _ = internal.UpdateOldManagementURL(ctx, config, s.latestConfigInput.ConfigPath)
|
||||||
|
|
||||||
s.config = config
|
s.config = config
|
||||||
|
|
||||||
@@ -187,6 +187,22 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro
|
|||||||
ctx = context.WithValue(ctx, system.DeviceNameCtxKey, msg.Hostname)
|
ctx = context.WithValue(ctx, system.DeviceNameCtxKey, msg.Hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if msg.RosenpassEnabled != nil {
|
||||||
|
inputConfig.RosenpassEnabled = msg.RosenpassEnabled
|
||||||
|
s.latestConfigInput.RosenpassEnabled = msg.RosenpassEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.InterfaceName != nil {
|
||||||
|
inputConfig.InterfaceName = msg.InterfaceName
|
||||||
|
s.latestConfigInput.InterfaceName = msg.InterfaceName
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.WireguardPort != nil {
|
||||||
|
port := int(*msg.WireguardPort)
|
||||||
|
inputConfig.WireguardPort = &port
|
||||||
|
s.latestConfigInput.WireguardPort = &port
|
||||||
|
}
|
||||||
|
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
|
|
||||||
inputConfig.PreSharedKey = &msg.PreSharedKey
|
inputConfig.PreSharedKey = &msg.PreSharedKey
|
||||||
@@ -197,7 +213,7 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro
|
|||||||
}
|
}
|
||||||
|
|
||||||
if msg.ManagementUrl == "" {
|
if msg.ManagementUrl == "" {
|
||||||
config, _ = internal.UpdateOldManagementPort(ctx, config, s.latestConfigInput.ConfigPath)
|
config, _ = internal.UpdateOldManagementURL(ctx, config, s.latestConfigInput.ConfigPath)
|
||||||
s.config = config
|
s.config = config
|
||||||
s.latestConfigInput.ManagementURL = config.ManagementURL.String()
|
s.latestConfigInput.ManagementURL = config.ManagementURL.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ func getOSNameAndVersion() (string, string) {
|
|||||||
query := wmi.CreateQuery(&dst, "")
|
query := wmi.CreateQuery(&dst, "")
|
||||||
err := wmi.Query(query, &dst)
|
err := wmi.Query(query, &dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Error(err)
|
||||||
|
return "Windows", getBuildVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(dst) == 0 {
|
if len(dst) == 0 {
|
||||||
|
|||||||
@@ -563,8 +563,8 @@ func (s *serviceClient) getSrvClient(timeout time.Duration) (proto.DaemonService
|
|||||||
|
|
||||||
// getSrvConfig from the service to show it in the settings window.
|
// getSrvConfig from the service to show it in the settings window.
|
||||||
func (s *serviceClient) getSrvConfig() {
|
func (s *serviceClient) getSrvConfig() {
|
||||||
s.managementURL = "https://api.wiretrustee.com:33073"
|
s.managementURL = internal.DefaultManagementURL
|
||||||
s.adminURL = "https://app.netbird.io"
|
s.adminURL = internal.DefaultAdminURL
|
||||||
|
|
||||||
conn, err := s.getSrvClient(failFastTimeout)
|
conn, err := s.getSrvClient(failFastTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
65
go.mod
65
go.mod
@@ -1,26 +1,30 @@
|
|||||||
module github.com/netbirdio/netbird
|
module github.com/netbirdio/netbird
|
||||||
|
|
||||||
go 1.20
|
go 1.21
|
||||||
|
|
||||||
|
toolchain go1.21.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
cunicu.li/go-rosenpass v0.4.0
|
||||||
github.com/cenkalti/backoff/v4 v4.1.3
|
github.com/cenkalti/backoff/v4 v4.1.3
|
||||||
|
github.com/cloudflare/circl v1.3.3 // indirect
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
github.com/golang/protobuf v1.5.3
|
github.com/golang/protobuf v1.5.3
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.1
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
|
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
|
||||||
github.com/onsi/ginkgo v1.16.5
|
github.com/onsi/ginkgo v1.16.5
|
||||||
github.com/onsi/gomega v1.18.1
|
github.com/onsi/gomega v1.18.1
|
||||||
github.com/pion/ice/v2 v2.3.1
|
github.com/pion/ice/v3 v3.0.2
|
||||||
github.com/rs/cors v1.8.0
|
github.com/rs/cors v1.8.0
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.7.0
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/vishvananda/netlink v1.1.0
|
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
|
||||||
golang.org/x/crypto v0.14.0
|
golang.org/x/crypto v0.14.0
|
||||||
golang.org/x/sys v0.13.0
|
golang.org/x/sys v0.13.0
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675
|
golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||||
google.golang.org/grpc v1.56.3
|
google.golang.org/grpc v1.56.3
|
||||||
google.golang.org/protobuf v1.30.0
|
google.golang.org/protobuf v1.30.0
|
||||||
@@ -31,7 +35,7 @@ require (
|
|||||||
fyne.io/fyne/v2 v2.1.4
|
fyne.io/fyne/v2 v2.1.4
|
||||||
github.com/TheJumpCloud/jcapi-go v3.0.0+incompatible
|
github.com/TheJumpCloud/jcapi-go v3.0.0+incompatible
|
||||||
github.com/c-robinson/iplib v1.0.3
|
github.com/c-robinson/iplib v1.0.3
|
||||||
github.com/cilium/ebpf v0.10.0
|
github.com/cilium/ebpf v0.11.0
|
||||||
github.com/coreos/go-iptables v0.7.0
|
github.com/coreos/go-iptables v0.7.0
|
||||||
github.com/creack/pty v1.1.18
|
github.com/creack/pty v1.1.18
|
||||||
github.com/eko/gocache/v3 v3.1.1
|
github.com/eko/gocache/v3 v3.1.1
|
||||||
@@ -47,32 +51,33 @@ require (
|
|||||||
github.com/libp2p/go-netroute v0.2.0
|
github.com/libp2p/go-netroute v0.2.0
|
||||||
github.com/magiconair/properties v1.8.5
|
github.com/magiconair/properties v1.8.5
|
||||||
github.com/mattn/go-sqlite3 v1.14.17
|
github.com/mattn/go-sqlite3 v1.14.17
|
||||||
github.com/mdlayher/socket v0.4.0
|
github.com/mdlayher/socket v0.4.1
|
||||||
github.com/miekg/dns v1.1.43
|
github.com/miekg/dns v1.1.43
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/nadoo/ipset v0.5.0
|
github.com/nadoo/ipset v0.5.0
|
||||||
github.com/netbirdio/management-integrations/additions v0.0.0-20231205113053-c462587ae695
|
github.com/netbirdio/management-integrations/additions v0.0.0-20231230192609-a9dcce34ff86
|
||||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20231205113053-c462587ae695
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20231230192609-a9dcce34ff86
|
||||||
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pion/logging v0.2.2
|
github.com/pion/logging v0.2.2
|
||||||
github.com/pion/stun v0.4.0
|
github.com/pion/stun/v2 v2.0.0
|
||||||
github.com/pion/transport/v2 v2.0.2
|
github.com/pion/transport/v3 v3.0.1
|
||||||
github.com/prometheus/client_golang v1.14.0
|
github.com/prometheus/client_golang v1.14.0
|
||||||
github.com/rs/xid v1.3.0
|
github.com/rs/xid v1.3.0
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.4
|
||||||
|
github.com/things-go/go-socks5 v0.0.4
|
||||||
github.com/yusufpapurcu/wmi v1.2.3
|
github.com/yusufpapurcu/wmi v1.2.3
|
||||||
go.opentelemetry.io/otel v1.11.1
|
go.opentelemetry.io/otel v1.11.1
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.33.0
|
go.opentelemetry.io/otel/exporters/prometheus v0.33.0
|
||||||
go.opentelemetry.io/otel/metric v0.33.0
|
go.opentelemetry.io/otel/metric v0.33.0
|
||||||
go.opentelemetry.io/otel/sdk/metric v0.33.0
|
go.opentelemetry.io/otel/sdk/metric v0.33.0
|
||||||
goauthentik.io/api/v3 v3.2023051.3
|
goauthentik.io/api/v3 v3.2023051.3
|
||||||
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf
|
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028
|
||||||
golang.org/x/net v0.17.0
|
golang.org/x/net v0.17.0
|
||||||
golang.org/x/oauth2 v0.8.0
|
golang.org/x/oauth2 v0.8.0
|
||||||
golang.org/x/sync v0.2.0
|
golang.org/x/sync v0.3.0
|
||||||
golang.org/x/term v0.13.0
|
golang.org/x/term v0.13.0
|
||||||
google.golang.org/api v0.126.0
|
google.golang.org/api v0.126.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
@@ -109,26 +114,28 @@ require (
|
|||||||
github.com/go-stack/stack v1.8.0 // indirect
|
github.com/go-stack/stack v1.8.0 // indirect
|
||||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
|
github.com/google/btree v1.0.1 // indirect
|
||||||
github.com/google/s2a-go v0.1.4 // indirect
|
github.com/google/s2a-go v0.1.4 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.10.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.10.0 // indirect
|
||||||
|
github.com/gopacket/gopacket v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/josharian/native v1.0.0 // indirect
|
github.com/josharian/native v1.1.0 // indirect
|
||||||
github.com/kelseyhightower/envconfig v1.4.0 // indirect
|
github.com/kelseyhightower/envconfig v1.4.0 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mdlayher/genetlink v1.1.0 // indirect
|
github.com/mdlayher/genetlink v1.3.2 // indirect
|
||||||
github.com/mdlayher/netlink v1.7.1 // indirect
|
github.com/mdlayher/netlink v1.7.2 // indirect
|
||||||
github.com/nxadm/tail v1.4.8 // indirect
|
github.com/nxadm/tail v1.4.8 // indirect
|
||||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
|
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
|
||||||
github.com/pegasus-kv/thrift v0.13.0 // indirect
|
github.com/pegasus-kv/thrift v0.13.0 // indirect
|
||||||
github.com/pion/dtls/v2 v2.2.6 // indirect
|
github.com/pion/dtls/v2 v2.2.7 // indirect
|
||||||
github.com/pion/mdns v0.0.7 // indirect
|
github.com/pion/mdns v0.0.9 // indirect
|
||||||
github.com/pion/randutil v0.1.0 // indirect
|
github.com/pion/randutil v0.1.0 // indirect
|
||||||
github.com/pion/turn/v2 v2.1.0 // indirect
|
github.com/pion/transport/v2 v2.2.1 // indirect
|
||||||
github.com/pion/udp/v2 v2.0.1 // indirect
|
github.com/pion/turn/v3 v3.0.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
github.com/prometheus/common v0.37.0 // indirect
|
||||||
@@ -142,10 +149,8 @@ require (
|
|||||||
go.opentelemetry.io/otel/sdk v1.11.1 // indirect
|
go.opentelemetry.io/otel/sdk v1.11.1 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.11.1 // indirect
|
go.opentelemetry.io/otel/trace v1.11.1 // indirect
|
||||||
golang.org/x/image v0.10.0 // indirect
|
golang.org/x/image v0.10.0 // indirect
|
||||||
golang.org/x/mod v0.8.0 // indirect
|
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||||
golang.org/x/tools v0.6.0 // indirect
|
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||||
@@ -154,12 +159,14 @@ require (
|
|||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
|
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
honnef.co/go/tools v0.2.2 // indirect
|
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect
|
||||||
k8s.io/apimachinery v0.23.5 // indirect
|
k8s.io/apimachinery v0.23.16 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/kardianos/service => github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0
|
replace github.com/kardianos/service => github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0
|
||||||
|
|
||||||
replace github.com/getlantern/systray => github.com/netbirdio/systray v0.0.0-20231030152038-ef1ed2a27949
|
replace github.com/getlantern/systray => github.com/netbirdio/systray v0.0.0-20231030152038-ef1ed2a27949
|
||||||
|
|
||||||
replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f
|
replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20240105182236-6c340dd55aed
|
||||||
|
|
||||||
|
replace github.com/cloudflare/circl => github.com/cunicu/circl v0.0.0-20230801113412-fec58fc7b5f6
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pion/stun"
|
"github.com/pion/stun/v2"
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v3"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/ipv4"
|
"golang.org/x/net/ipv4"
|
||||||
wgConn "golang.zx2c4.com/wireguard/conn"
|
wgConn "golang.zx2c4.com/wireguard/conn"
|
||||||
|
|||||||
@@ -7,13 +7,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pion/ice/v2"
|
"github.com/pion/ice/v3"
|
||||||
"github.com/pion/stun"
|
|
||||||
"github.com/pion/transport/v2/stdnet"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/pion/logging"
|
"github.com/pion/logging"
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/stun/v2"
|
||||||
|
"github.com/pion/transport/v3"
|
||||||
|
"github.com/pion/transport/v3/stdnet"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -224,6 +223,10 @@ func (m *UDPMuxDefault) GetListenAddresses() []net.Addr {
|
|||||||
// GetConn returns a PacketConn given the connection's ufrag and network address
|
// GetConn returns a PacketConn given the connection's ufrag and network address
|
||||||
// creates the connection if an existing one can't be found
|
// creates the connection if an existing one can't be found
|
||||||
func (m *UDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) {
|
func (m *UDPMuxDefault) GetConn(ufrag string, addr net.Addr) (net.PacketConn, error) {
|
||||||
|
// don't check addr for mux using unspecified address
|
||||||
|
if len(m.localAddrsForUnspecified) == 0 && m.params.UDPConn.LocalAddr().String() != addr.String() {
|
||||||
|
return nil, fmt.Errorf("invalid address %s", addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
var isIPv6 bool
|
var isIPv6 bool
|
||||||
if udpAddr, _ := addr.(*net.UDPAddr); udpAddr != nil && udpAddr.IP.To4() == nil {
|
if udpAddr, _ := addr.(*net.UDPAddr); udpAddr != nil && udpAddr.IP.To4() == nil {
|
||||||
@@ -282,15 +285,7 @@ func (m *UDPMuxDefault) RemoveConnByUfrag(ufrag string) {
|
|||||||
for _, c := range removedConns {
|
for _, c := range removedConns {
|
||||||
addresses := c.getAddresses()
|
addresses := c.getAddresses()
|
||||||
for _, addr := range addresses {
|
for _, addr := range addresses {
|
||||||
if connList, ok := m.addressMap[addr]; ok {
|
delete(m.addressMap, addr)
|
||||||
var newList []*udpMuxedConn
|
|
||||||
for _, conn := range connList {
|
|
||||||
if conn.params.Key != ufrag {
|
|
||||||
newList = append(newList, conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m.addressMap[addr] = newList
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/pion/logging"
|
"github.com/pion/logging"
|
||||||
"github.com/pion/stun"
|
"github.com/pion/stun/v2"
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UniversalUDPMuxDefault handles STUN and TURN servers packets by wrapping the original UDPConn
|
// UniversalUDPMuxDefault handles STUN and TURN servers packets by wrapping the original UDPConn
|
||||||
@@ -80,13 +80,13 @@ func (m *UniversalUDPMuxDefault) ReadFromConn(ctx context.Context) {
|
|||||||
log.Debugf("stopped reading from the UDPConn due to finished context")
|
log.Debugf("stopped reading from the UDPConn due to finished context")
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
_, a, err := m.params.UDPConn.ReadFrom(buf)
|
n, a, err := m.params.UDPConn.ReadFrom(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("error while reading packet: %s", err)
|
log.Errorf("error while reading packet: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
msg := &stun.Message{
|
msg := &stun.Message{
|
||||||
Raw: buf,
|
Raw: append([]byte{}, buf[:n]...),
|
||||||
}
|
}
|
||||||
err = msg.Decode()
|
err = msg.Decode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/logging"
|
"github.com/pion/logging"
|
||||||
"github.com/pion/transport/v2/packetio"
|
"github.com/pion/transport/v3/packetio"
|
||||||
)
|
)
|
||||||
|
|
||||||
type udpMuxedConnParams struct {
|
type udpMuxedConnParams struct {
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface/bind"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/iface/bind"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -19,10 +19,11 @@ const (
|
|||||||
|
|
||||||
// WGIface represents a interface instance
|
// WGIface represents a interface instance
|
||||||
type WGIface struct {
|
type WGIface struct {
|
||||||
tun *tunDevice
|
tun wgTunDevice
|
||||||
configurer wGConfigurer
|
|
||||||
mu sync.Mutex
|
|
||||||
userspaceBind bool
|
userspaceBind bool
|
||||||
|
mu sync.Mutex
|
||||||
|
|
||||||
|
configurer wgConfigurer
|
||||||
filter PacketFilter
|
filter PacketFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,11 +32,6 @@ func (w *WGIface) IsUserspaceBind() bool {
|
|||||||
return w.userspaceBind
|
return w.userspaceBind
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBind returns a userspace implementation of WireGuard Bind interface
|
|
||||||
func (w *WGIface) GetBind() *bind.ICEBind {
|
|
||||||
return w.tun.iceBind
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name returns the interface name
|
// Name returns the interface name
|
||||||
func (w *WGIface) Name() string {
|
func (w *WGIface) Name() string {
|
||||||
return w.tun.DeviceName()
|
return w.tun.DeviceName()
|
||||||
@@ -46,13 +42,13 @@ func (w *WGIface) Address() WGAddress {
|
|||||||
return w.tun.WgAddress()
|
return w.tun.WgAddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure configures a Wireguard interface
|
// Up configures a Wireguard interface
|
||||||
// The interface must exist before calling this method (e.g. call interface.Create() before)
|
// The interface must exist before calling this method (e.g. call interface.Create() before)
|
||||||
func (w *WGIface) Configure(privateKey string, port int) error {
|
func (w *WGIface) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
log.Debugf("configuring Wireguard interface %s", w.tun.DeviceName())
|
|
||||||
return w.configurer.configureInterface(privateKey, port)
|
return w.tun.Up()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAddr updates address of the interface
|
// UpdateAddr updates address of the interface
|
||||||
@@ -74,7 +70,7 @@ func (w *WGIface) UpdatePeer(peerKey string, allowedIps string, keepAlive time.D
|
|||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
log.Debugf("updating interface %s peer %s, endpoint %s ", w.tun.DeviceName(), peerKey, endpoint)
|
log.Debugf("updating interface %s peer %s, endpoint %s", w.tun.DeviceName(), peerKey, endpoint)
|
||||||
return w.configurer.updatePeer(peerKey, allowedIps, keepAlive, endpoint, preSharedKey)
|
return w.configurer.updatePeer(peerKey, allowedIps, keepAlive, endpoint, preSharedKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,14 +113,14 @@ func (w *WGIface) SetFilter(filter PacketFilter) error {
|
|||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
if w.tun.wrapper == nil {
|
if w.tun.Wrapper() == nil {
|
||||||
return fmt.Errorf("userspace packet filtering not handled on this device")
|
return fmt.Errorf("userspace packet filtering not handled on this device")
|
||||||
}
|
}
|
||||||
|
|
||||||
w.filter = filter
|
w.filter = filter
|
||||||
w.filter.SetNetwork(w.tun.address.Network)
|
w.filter.SetNetwork(w.tun.WgAddress().Network)
|
||||||
|
|
||||||
w.tun.wrapper.SetFilter(filter)
|
w.tun.Wrapper().SetFilter(filter)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,5 +137,5 @@ func (w *WGIface) GetDevice() *DeviceWrapper {
|
|||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
return w.tun.wrapper
|
return w.tun.Wrapper()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,47 +2,39 @@ package iface
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewWGIFace Creates a new WireGuard interface instance
|
// NewWGIFace Creates a new WireGuard interface instance
|
||||||
func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
|
||||||
wgIFace := &WGIface{
|
|
||||||
mu: sync.Mutex{},
|
|
||||||
}
|
|
||||||
|
|
||||||
wgAddress, err := parseWGAddress(address)
|
wgAddress, err := parseWGAddress(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wgIFace, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tun := newTunDevice(wgAddress, mtu, tunAdapter, transportNet)
|
wgIFace := &WGIface{
|
||||||
wgIFace.tun = tun
|
tun: newTunDevice(wgAddress, wgPort, wgPrivKey, mtu, transportNet, args.TunAdapter),
|
||||||
|
userspaceBind: true,
|
||||||
wgIFace.configurer = newWGConfigurer(tun)
|
}
|
||||||
|
|
||||||
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
|
|
||||||
|
|
||||||
return wgIFace, nil
|
return wgIFace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOnAndroid creates a new Wireguard interface, sets a given IP and brings it up.
|
// CreateOnAndroid creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
// Will reuse an existing one.
|
// Will reuse an existing one.
|
||||||
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
func (w *WGIface) CreateOnAndroid(routes []string, dns string, searchDomains []string) error {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
return w.tun.Create(mIFaceArgs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOniOS creates a new Wireguard interface, sets a given IP and brings it up.
|
cfgr, err := w.tun.Create(routes, dns, searchDomains)
|
||||||
// Will reuse an existing one.
|
if err != nil {
|
||||||
func (w *WGIface) CreateOniOS(tunFd int32) error {
|
return err
|
||||||
return fmt.Errorf("this function has not implemented on mobile")
|
}
|
||||||
|
w.configurer = cfgr
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create this function make sense on mobile only
|
// Create this function make sense on mobile only
|
||||||
func (w *WGIface) Create() error {
|
func (w *WGIface) Create() error {
|
||||||
return fmt.Errorf("this function has not implemented on mobile")
|
return fmt.Errorf("this function has not implemented on this platform")
|
||||||
}
|
}
|
||||||
|
|||||||
20
iface/iface_create.go
Normal file
20
iface/iface_create.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//go:build !android
|
||||||
|
// +build !android
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
// Create creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
|
// Will reuse an existing one.
|
||||||
|
// this function is different on Android
|
||||||
|
func (w *WGIface) Create() error {
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
|
cfgr, err := w.tun.Create()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
w.configurer = cfgr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
38
iface/iface_darwin.go
Normal file
38
iface/iface_darwin.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
//go:build !ios
|
||||||
|
// +build !ios
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pion/transport/v3"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/iface/netstack"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewWGIFace Creates a new WireGuard interface instance
|
||||||
|
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
|
||||||
|
wgAddress, err := parseWGAddress(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
wgIFace := &WGIface{
|
||||||
|
userspaceBind: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if netstack.IsEnabled() {
|
||||||
|
wgIFace.tun = newTunNetstackDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet, netstack.ListenAddr())
|
||||||
|
return wgIFace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
wgIFace.tun = newTunDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
|
||||||
|
|
||||||
|
return wgIFace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOnAndroid this function make sense on mobile only
|
||||||
|
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
|
||||||
|
return fmt.Errorf("this function has not implemented on this platform")
|
||||||
|
}
|
||||||
@@ -5,47 +5,25 @@ package iface
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewWGIFace Creates a new WireGuard interface instance
|
// NewWGIFace Creates a new WireGuard interface instance
|
||||||
func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
|
||||||
wgIFace := &WGIface{
|
|
||||||
mu: sync.Mutex{},
|
|
||||||
}
|
|
||||||
|
|
||||||
wgAddress, err := parseWGAddress(address)
|
wgAddress, err := parseWGAddress(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wgIFace, err
|
return nil, err
|
||||||
|
}
|
||||||
|
wgIFace := &WGIface{
|
||||||
|
tun: newTunDevice(iFaceName, wgAddress, wgPort, wgPrivKey, transportNet, args.TunFd),
|
||||||
|
userspaceBind: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
tun := newTunDevice(ifaceName, wgAddress, mtu, tunAdapter, transportNet)
|
|
||||||
wgIFace.tun = tun
|
|
||||||
|
|
||||||
wgIFace.configurer = newWGConfigurer(tun)
|
|
||||||
|
|
||||||
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
|
|
||||||
|
|
||||||
return wgIFace, nil
|
return wgIFace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOniOS creates a new Wireguard interface, sets a given IP and brings it up.
|
|
||||||
// Will reuse an existing one.
|
|
||||||
func (w *WGIface) CreateOniOS(tunFd int32) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
return w.tun.Create(tunFd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOnAndroid creates a new Wireguard interface, sets a given IP and brings it up.
|
// CreateOnAndroid creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
// Will reuse an existing one.
|
// Will reuse an existing one.
|
||||||
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
|
||||||
return fmt.Errorf("this function has not implemented on mobile")
|
return fmt.Errorf("this function has not implemented on this platform")
|
||||||
}
|
|
||||||
|
|
||||||
// Create this function make sense on mobile only
|
|
||||||
func (w *WGIface) Create() error {
|
|
||||||
return fmt.Errorf("this function has not implemented on mobile")
|
|
||||||
}
|
}
|
||||||
|
|||||||
48
iface/iface_linux.go
Normal file
48
iface/iface_linux.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
//go:build !android
|
||||||
|
// +build !android
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pion/transport/v3"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/iface/netstack"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewWGIFace Creates a new WireGuard interface instance
|
||||||
|
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
|
||||||
|
wgAddress, err := parseWGAddress(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
wgIFace := &WGIface{}
|
||||||
|
|
||||||
|
// move the kernel/usp/netstack preference evaluation to upper layer
|
||||||
|
if netstack.IsEnabled() {
|
||||||
|
wgIFace.tun = newTunNetstackDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet, netstack.ListenAddr())
|
||||||
|
wgIFace.userspaceBind = true
|
||||||
|
return wgIFace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if WireGuardModuleIsLoaded() {
|
||||||
|
wgIFace.tun = newTunDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
|
||||||
|
wgIFace.userspaceBind = false
|
||||||
|
return wgIFace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tunModuleIsLoaded() {
|
||||||
|
return nil, fmt.Errorf("couldn't check or load tun module")
|
||||||
|
}
|
||||||
|
wgIFace.tun = newTunUSPDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
|
||||||
|
wgIFace.userspaceBind = true
|
||||||
|
return wgIFace, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOnAndroid this function make sense on mobile only
|
||||||
|
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
|
||||||
|
return fmt.Errorf("this function has not implemented on this platform")
|
||||||
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
//go:build !android && !ios
|
|
||||||
// +build !android,!ios
|
|
||||||
|
|
||||||
package iface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewWGIFace Creates a new WireGuard interface instance
|
|
||||||
func NewWGIFace(iFaceName string, address string, mtu int, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
|
||||||
wgIFace := &WGIface{
|
|
||||||
mu: sync.Mutex{},
|
|
||||||
}
|
|
||||||
|
|
||||||
wgAddress, err := parseWGAddress(address)
|
|
||||||
if err != nil {
|
|
||||||
return wgIFace, err
|
|
||||||
}
|
|
||||||
|
|
||||||
wgIFace.tun = newTunDevice(iFaceName, wgAddress, mtu, transportNet)
|
|
||||||
|
|
||||||
wgIFace.configurer = newWGConfigurer(iFaceName)
|
|
||||||
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
|
|
||||||
return wgIFace, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOnAndroid this function make sense on mobile only
|
|
||||||
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
|
||||||
return fmt.Errorf("this function has not implemented on non mobile")
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateOniOS this function make sense on mobile only
|
|
||||||
func (w *WGIface) CreateOniOS(tunFd int32) error {
|
|
||||||
return fmt.Errorf("this function has not implemented on non mobile")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create creates a new Wireguard interface, sets a given IP and brings it up.
|
|
||||||
// Will reuse an existing one.
|
|
||||||
func (w *WGIface) Create() error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
return w.tun.Create()
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/transport/v2/stdnet"
|
"github.com/pion/transport/v3/stdnet"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
@@ -34,12 +34,13 @@ func init() {
|
|||||||
func TestWGIface_UpdateAddr(t *testing.T) {
|
func TestWGIface_UpdateAddr(t *testing.T) {
|
||||||
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+4)
|
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+4)
|
||||||
addr := "100.64.0.1/8"
|
addr := "100.64.0.1/8"
|
||||||
|
wgPort := 33100
|
||||||
newNet, err := stdnet.NewNet()
|
newNet, err := stdnet.NewNet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
iface, err := NewWGIFace(ifaceName, addr, DefaultMTU, nil, newNet)
|
iface, err := NewWGIFace(ifaceName, addr, wgPort, key, DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -52,12 +53,10 @@ func TestWGIface_UpdateAddr(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
port, err := getListenPortByName(ifaceName)
|
|
||||||
if err != nil {
|
_, err = iface.Up()
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = iface.Configure(key, port)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -103,7 +102,7 @@ func Test_CreateInterface(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -132,11 +131,13 @@ func Test_CreateInterface(t *testing.T) {
|
|||||||
func Test_Close(t *testing.T) {
|
func Test_Close(t *testing.T) {
|
||||||
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+2)
|
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+2)
|
||||||
wgIP := "10.99.99.2/32"
|
wgIP := "10.99.99.2/32"
|
||||||
|
wgPort := 33100
|
||||||
newNet, err := stdnet.NewNet()
|
newNet, err := stdnet.NewNet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
|
||||||
|
iface, err := NewWGIFace(ifaceName, wgIP, wgPort, key, DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -164,11 +165,12 @@ func Test_Close(t *testing.T) {
|
|||||||
func Test_ConfigureInterface(t *testing.T) {
|
func Test_ConfigureInterface(t *testing.T) {
|
||||||
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+3)
|
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+3)
|
||||||
wgIP := "10.99.99.5/30"
|
wgIP := "10.99.99.5/30"
|
||||||
|
wgPort := 33100
|
||||||
newNet, err := stdnet.NewNet()
|
newNet, err := stdnet.NewNet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
iface, err := NewWGIFace(ifaceName, wgIP, wgPort, key, DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -183,11 +185,7 @@ func Test_ConfigureInterface(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
port, err := getListenPortByName(ifaceName)
|
_, err = iface.Up()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = iface.Configure(key, port)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -219,7 +217,8 @@ func Test_UpdatePeer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
|
||||||
|
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -233,11 +232,8 @@ func Test_UpdatePeer(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
port, err := getListenPortByName(ifaceName)
|
|
||||||
if err != nil {
|
_, err = iface.Up()
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = iface.Configure(key, port)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -251,7 +247,7 @@ func Test_UpdatePeer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
peer, err := iface.configurer.getPeer(ifaceName, peerPubKey)
|
peer, err := getPeer(ifaceName, peerPubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -282,7 +278,8 @@ func Test_RemovePeer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
|
||||||
|
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -296,11 +293,8 @@ func Test_RemovePeer(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
port, err := getListenPortByName(ifaceName)
|
|
||||||
if err != nil {
|
_, err = iface.Up()
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = iface.Configure(key, port)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -315,7 +309,8 @@ func Test_RemovePeer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = iface.configurer.getPeer(ifaceName, peerPubKey)
|
|
||||||
|
_, err = getPeer(ifaceName, peerPubKey)
|
||||||
if err.Error() != "peer not found" {
|
if err.Error() != "peer not found" {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -325,17 +320,20 @@ func Test_ConnectPeers(t *testing.T) {
|
|||||||
peer1ifaceName := fmt.Sprintf("utun%d", WgIntNumber+400)
|
peer1ifaceName := fmt.Sprintf("utun%d", WgIntNumber+400)
|
||||||
peer1wgIP := "10.99.99.17/30"
|
peer1wgIP := "10.99.99.17/30"
|
||||||
peer1Key, _ := wgtypes.GeneratePrivateKey()
|
peer1Key, _ := wgtypes.GeneratePrivateKey()
|
||||||
|
peer1wgPort := 33100
|
||||||
|
|
||||||
peer2ifaceName := "utun500"
|
peer2ifaceName := "utun500"
|
||||||
peer2wgIP := "10.99.99.18/30"
|
peer2wgIP := "10.99.99.18/30"
|
||||||
peer2Key, _ := wgtypes.GeneratePrivateKey()
|
peer2Key, _ := wgtypes.GeneratePrivateKey()
|
||||||
|
peer2wgPort := 33200
|
||||||
|
|
||||||
keepAlive := 1 * time.Second
|
keepAlive := 1 * time.Second
|
||||||
newNet, err := stdnet.NewNet()
|
newNet, err := stdnet.NewNet()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface1, err := NewWGIFace(peer1ifaceName, peer1wgIP, DefaultMTU, nil, newNet)
|
|
||||||
|
iface1, err := NewWGIFace(peer1ifaceName, peer1wgIP, peer1wgPort, peer1Key.String(), DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -343,11 +341,13 @@ func Test_ConnectPeers(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
peer1Port, err := getListenPortByName(peer1ifaceName)
|
|
||||||
|
_, err = iface1.Up()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
peer1endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer1Port))
|
|
||||||
|
peer1endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer1wgPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -356,7 +356,7 @@ func Test_ConnectPeers(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface2, err := NewWGIFace(peer2ifaceName, peer2wgIP, DefaultMTU, nil, newNet)
|
iface2, err := NewWGIFace(peer2ifaceName, peer2wgIP, peer2wgPort, peer2Key.String(), DefaultMTU, newNet, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -364,11 +364,13 @@ func Test_ConnectPeers(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
peer2Port, err := getListenPortByName(peer2ifaceName)
|
|
||||||
|
_, err = iface2.Up()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
peer2endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer2Port))
|
|
||||||
|
peer2endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer2wgPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -383,15 +385,6 @@ func Test_ConnectPeers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = iface1.Configure(peer1Key.String(), peer1Port)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = iface2.Configure(peer2Key.String(), peer2Port)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = iface1.UpdatePeer(peer2Key.PublicKey().String(), peer2wgIP, keepAlive, peer2endpoint, nil)
|
err = iface1.UpdatePeer(peer2Key.PublicKey().String(), peer2wgIP, keepAlive, peer2endpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@@ -403,13 +396,15 @@ func Test_ConnectPeers(t *testing.T) {
|
|||||||
// todo: investigate why in some tests execution we need 30s
|
// todo: investigate why in some tests execution we need 30s
|
||||||
timeout := 30 * time.Second
|
timeout := 30 * time.Second
|
||||||
timeoutChannel := time.After(timeout)
|
timeoutChannel := time.After(timeout)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-timeoutChannel:
|
case <-timeoutChannel:
|
||||||
t.Fatalf("waiting for peer handshake timeout after %s", timeout.String())
|
t.Fatalf("waiting for peer handshake timeout after %s", timeout.String())
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
peer, gpErr := iface1.configurer.getPeer(peer1ifaceName, peer2Key.PublicKey().String())
|
|
||||||
|
peer, gpErr := getPeer(peer1ifaceName, peer2Key.PublicKey().String())
|
||||||
if gpErr != nil {
|
if gpErr != nil {
|
||||||
t.Fatal(gpErr)
|
t.Fatal(gpErr)
|
||||||
}
|
}
|
||||||
@@ -421,17 +416,26 @@ func Test_ConnectPeers(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getListenPortByName(name string) (int, error) {
|
func getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
|
||||||
wg, err := wgctrl.New()
|
wg, err := wgctrl.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return wgtypes.Peer{}, err
|
||||||
}
|
}
|
||||||
defer wg.Close()
|
defer func() {
|
||||||
|
err = wg.Close()
|
||||||
d, err := wg.Device(name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
log.Errorf("got error while closing wgctl: %v", err)
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
return d.ListenPort, nil
|
wgDevice, err := wg.Device(ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
return wgtypes.Peer{}, err
|
||||||
|
}
|
||||||
|
for _, peer := range wgDevice.Peers {
|
||||||
|
if peer.PublicKey.String() == peerPubKey {
|
||||||
|
return peer, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wgtypes.Peer{}, fmt.Errorf("peer not found")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,39 @@
|
|||||||
package iface
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pion/transport/v3"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/iface/netstack"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewWGIFace Creates a new WireGuard interface instance
|
||||||
|
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
|
||||||
|
wgAddress, err := parseWGAddress(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
wgIFace := &WGIface{
|
||||||
|
userspaceBind: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if netstack.IsEnabled() {
|
||||||
|
wgIFace.tun = newTunNetstackDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet, netstack.ListenAddr())
|
||||||
|
return wgIFace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
wgIFace.tun = newTunDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
|
||||||
|
return wgIFace, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOnAndroid this function make sense on mobile only
|
||||||
|
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
|
||||||
|
return fmt.Errorf("this function has not implemented on non mobile")
|
||||||
|
}
|
||||||
|
|
||||||
// GetInterfaceGUIDString returns an interface GUID. This is useful on Windows only
|
// GetInterfaceGUIDString returns an interface GUID. This is useful on Windows only
|
||||||
func (w *WGIface) GetInterfaceGUIDString() (string, error) {
|
func (w *WGIface) GetInterfaceGUIDString() (string, error) {
|
||||||
return w.tun.getInterfaceGUIDString()
|
return w.tun.(*tunDevice).getInterfaceGUIDString()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
//go:build android || ios
|
|
||||||
// +build android ios
|
|
||||||
|
|
||||||
package iface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
func toWgUserspaceString(wgCfg wgtypes.Config) string {
|
|
||||||
var sb strings.Builder
|
|
||||||
if wgCfg.PrivateKey != nil {
|
|
||||||
hexKey := hex.EncodeToString(wgCfg.PrivateKey[:])
|
|
||||||
sb.WriteString(fmt.Sprintf("private_key=%s\n", hexKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
if wgCfg.ListenPort != nil {
|
|
||||||
sb.WriteString(fmt.Sprintf("listen_port=%d\n", *wgCfg.ListenPort))
|
|
||||||
}
|
|
||||||
|
|
||||||
if wgCfg.ReplacePeers {
|
|
||||||
sb.WriteString("replace_peers=true\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
if wgCfg.FirewallMark != nil {
|
|
||||||
sb.WriteString(fmt.Sprintf("fwmark=%d\n", *wgCfg.FirewallMark))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range wgCfg.Peers {
|
|
||||||
hexKey := hex.EncodeToString(p.PublicKey[:])
|
|
||||||
sb.WriteString(fmt.Sprintf("public_key=%s\n", hexKey))
|
|
||||||
|
|
||||||
if p.PresharedKey != nil {
|
|
||||||
preSharedHexKey := hex.EncodeToString(p.PresharedKey[:])
|
|
||||||
sb.WriteString(fmt.Sprintf("preshared_key=%s\n", preSharedHexKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Remove {
|
|
||||||
sb.WriteString("remove=true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.ReplaceAllowedIPs {
|
|
||||||
sb.WriteString("replace_allowed_ips=true\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, aip := range p.AllowedIPs {
|
|
||||||
sb.WriteString(fmt.Sprintf("allowed_ip=%s\n", aip.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Endpoint != nil {
|
|
||||||
sb.WriteString(fmt.Sprintf("endpoint=%s\n", p.Endpoint.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.PersistentKeepaliveInterval != nil {
|
|
||||||
sb.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", int(p.PersistentKeepaliveInterval.Seconds())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
32
iface/netstack/dialer.go
Normal file
32
iface/netstack/dialer.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package netstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Dialer interface {
|
||||||
|
Dial(ctx context.Context, network, addr string) (net.Conn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NSDialer struct {
|
||||||
|
net *netstack.Net
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNSDialer(net *netstack.Net) *NSDialer {
|
||||||
|
return &NSDialer{
|
||||||
|
net: net,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *NSDialer) Dial(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
log.Debugf("dialing %s %s", network, addr)
|
||||||
|
conn, err := d.net.Dial(network, addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("failed to deal connection: %s", err)
|
||||||
|
}
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
33
iface/netstack/env.go
Normal file
33
iface/netstack/env.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package netstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsEnabled todo: move these function to cmd layer
|
||||||
|
func IsEnabled() bool {
|
||||||
|
return os.Getenv("NB_USE_NETSTACK_MODE") == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListenAddr() string {
|
||||||
|
sPort := os.Getenv("NB_SOCKS5_LISTENER_PORT")
|
||||||
|
port, err := strconv.Atoi(sPort)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("invalid socks5 listener port, unable to convert it to int, falling back to default: %d", DefaultSocks5Port)
|
||||||
|
return listenAddr(DefaultSocks5Port)
|
||||||
|
}
|
||||||
|
if port < 1 || port > 65535 {
|
||||||
|
log.Warnf("invalid socks5 listener port, it should be in the range 1-65535, falling back to default: %d", DefaultSocks5Port)
|
||||||
|
return listenAddr(DefaultSocks5Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
return listenAddr(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listenAddr(port int) string {
|
||||||
|
return fmt.Sprintf("0.0.0.0:%d", port)
|
||||||
|
}
|
||||||
65
iface/netstack/proxy.go
Normal file
65
iface/netstack/proxy.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package netstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/things-go/go-socks5"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultSocks5Port = 1080
|
||||||
|
)
|
||||||
|
|
||||||
|
// Proxy todo close server
|
||||||
|
type Proxy struct {
|
||||||
|
server *socks5.Server
|
||||||
|
|
||||||
|
listener net.Listener
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSocks5(dialer Dialer) (*Proxy, error) {
|
||||||
|
server := socks5.NewServer(
|
||||||
|
socks5.WithDial(dialer.Dial),
|
||||||
|
)
|
||||||
|
|
||||||
|
return &Proxy{
|
||||||
|
server: server,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Proxy) ListenAndServe(addr string) error {
|
||||||
|
listener, err := net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to create listener for socks5 proxy: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.listener = listener
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
if s.closed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := s.server.ServeConn(conn); err != nil {
|
||||||
|
log.Errorf("failed to serve a connection: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Proxy) Close() error {
|
||||||
|
if s.listener == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s.closed = true
|
||||||
|
return s.listener.Close()
|
||||||
|
}
|
||||||
74
iface/netstack/tun.go
Normal file
74
iface/netstack/tun.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package netstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
|
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NetStackTun struct {
|
||||||
|
address string
|
||||||
|
mtu int
|
||||||
|
listenAddress string
|
||||||
|
|
||||||
|
proxy *Proxy
|
||||||
|
tundev tun.Device
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNetStackTun(listenAddress string, address string, mtu int) *NetStackTun {
|
||||||
|
return &NetStackTun{
|
||||||
|
address: address,
|
||||||
|
mtu: mtu,
|
||||||
|
listenAddress: listenAddress,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NetStackTun) Create() (tun.Device, error) {
|
||||||
|
nsTunDev, tunNet, err := netstack.CreateNetTUN(
|
||||||
|
[]netip.Addr{netip.MustParseAddr(t.address)},
|
||||||
|
[]netip.Addr{},
|
||||||
|
t.mtu)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.tundev = nsTunDev
|
||||||
|
|
||||||
|
dialer := NewNSDialer(tunNet)
|
||||||
|
t.proxy, err = NewSocks5(dialer)
|
||||||
|
if err != nil {
|
||||||
|
_ = t.tundev.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := t.proxy.ListenAndServe(t.listenAddress)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error in socks5 proxy serving: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nsTunDev, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NetStackTun) Close() error {
|
||||||
|
var err error
|
||||||
|
if t.proxy != nil {
|
||||||
|
pErr := t.proxy.Close()
|
||||||
|
if pErr != nil {
|
||||||
|
log.Errorf("failed to close socks5 proxy: %s", pErr)
|
||||||
|
err = pErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.tundev != nil {
|
||||||
|
dErr := t.tundev.Close()
|
||||||
|
if dErr != nil {
|
||||||
|
log.Errorf("failed to close netstack tun device: %s", dErr)
|
||||||
|
err = dErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
20
iface/tun.go
20
iface/tun.go
@@ -1,12 +1,18 @@
|
|||||||
|
//go:build !android
|
||||||
|
// +build !android
|
||||||
|
|
||||||
package iface
|
package iface
|
||||||
|
|
||||||
type MobileIFaceArguments struct {
|
import (
|
||||||
Routes []string
|
"github.com/netbirdio/netbird/iface/bind"
|
||||||
Dns string
|
)
|
||||||
SearchDomains []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetInterface represents a generic network tunnel interface
|
type wgTunDevice interface {
|
||||||
type NetInterface interface {
|
Create() (wgConfigurer, error)
|
||||||
|
Up() (*bind.UniversalUDPMuxDefault, error)
|
||||||
|
UpdateAddr(address WGAddress) error
|
||||||
|
WgAddress() WGAddress
|
||||||
|
DeviceName() string
|
||||||
Close() error
|
Close() error
|
||||||
|
Wrapper() *DeviceWrapper // todo eliminate this function
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package iface
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v3"
|
||||||
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/device"
|
||||||
@@ -15,42 +15,50 @@ import (
|
|||||||
"github.com/netbirdio/netbird/iface/bind"
|
"github.com/netbirdio/netbird/iface/bind"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tunDevice struct {
|
// ignore the wgTunDevice interface on Android because the creation of the tun device is different on this platform
|
||||||
|
type wgTunDevice struct {
|
||||||
address WGAddress
|
address WGAddress
|
||||||
|
port int
|
||||||
|
key string
|
||||||
mtu int
|
mtu int
|
||||||
tunAdapter TunAdapter
|
|
||||||
iceBind *bind.ICEBind
|
iceBind *bind.ICEBind
|
||||||
|
tunAdapter TunAdapter
|
||||||
|
|
||||||
fd int
|
|
||||||
name string
|
name string
|
||||||
device *device.Device
|
device *device.Device
|
||||||
wrapper *DeviceWrapper
|
wrapper *DeviceWrapper
|
||||||
|
udpMux *bind.UniversalUDPMuxDefault
|
||||||
|
configurer wgConfigurer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTunDevice(address WGAddress, mtu int, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
|
func newTunDevice(address WGAddress, port int, key string, mtu int, transportNet transport.Net, tunAdapter TunAdapter) wgTunDevice {
|
||||||
return &tunDevice{
|
return wgTunDevice{
|
||||||
address: address,
|
address: address,
|
||||||
|
port: port,
|
||||||
|
key: key,
|
||||||
mtu: mtu,
|
mtu: mtu,
|
||||||
tunAdapter: tunAdapter,
|
|
||||||
iceBind: bind.NewICEBind(transportNet),
|
iceBind: bind.NewICEBind(transportNet),
|
||||||
|
tunAdapter: tunAdapter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
|
func (t *wgTunDevice) Create(routes []string, dns string, searchDomains []string) (wgConfigurer, error) {
|
||||||
log.Info("create tun interface")
|
log.Info("create tun interface")
|
||||||
var err error
|
|
||||||
routesString := t.routesToString(mIFaceArgs.Routes)
|
routesString := routesToString(routes)
|
||||||
searchDomainsToString := t.searchDomainsToString(mIFaceArgs.SearchDomains)
|
searchDomainsToString := searchDomainsToString(searchDomains)
|
||||||
t.fd, err = t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, mIFaceArgs.Dns, searchDomainsToString, routesString)
|
|
||||||
|
fd, err := t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, dns, searchDomainsToString, routesString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to create Android interface: %s", err)
|
log.Errorf("failed to create Android interface: %s", err)
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tunDevice, name, err := tun.CreateUnmonitoredTUNFromFD(t.fd)
|
tunDevice, name, err := tun.CreateUnmonitoredTUNFromFD(fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unix.Close(t.fd)
|
_ = unix.Close(fd)
|
||||||
return err
|
log.Errorf("failed to create Android interface: %s", err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
t.name = name
|
t.name = name
|
||||||
t.wrapper = newDeviceWrapper(tunDevice)
|
t.wrapper = newDeviceWrapper(tunDevice)
|
||||||
@@ -61,44 +69,72 @@ func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
|
|||||||
// 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()
|
||||||
|
|
||||||
err = t.device.Up()
|
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||||
|
err = t.configurer.configureInterface(t.key, t.port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.device.Close()
|
t.device.Close()
|
||||||
return err
|
t.configurer.close()
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debugf("device is ready to use: %s", name)
|
return t.configurer, nil
|
||||||
return nil
|
}
|
||||||
|
func (t *wgTunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||||
|
err := t.device.Up()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
udpMux, err := t.iceBind.GetICEMux()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.udpMux = udpMux
|
||||||
|
log.Debugf("device is ready to use: %s", t.name)
|
||||||
|
return udpMux, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunDevice) Device() *device.Device {
|
func (t *wgTunDevice) UpdateAddr(addr WGAddress) error {
|
||||||
return t.device
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunDevice) DeviceName() string {
|
|
||||||
return t.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunDevice) WgAddress() WGAddress {
|
|
||||||
return t.address
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tunDevice) UpdateAddr(addr WGAddress) error {
|
|
||||||
// todo implement
|
// todo implement
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunDevice) Close() (err error) {
|
func (t *wgTunDevice) Close() error {
|
||||||
if t.device != nil {
|
if t.configurer != nil {
|
||||||
t.device.Close()
|
t.configurer.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
if t.device != nil {
|
||||||
|
t.device.Close()
|
||||||
|
t.device = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.udpMux != nil {
|
||||||
|
return t.udpMux.Close()
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunDevice) routesToString(routes []string) string {
|
func (t *wgTunDevice) Device() *device.Device {
|
||||||
|
return t.device
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *wgTunDevice) DeviceName() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *wgTunDevice) WgAddress() WGAddress {
|
||||||
|
return t.address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *wgTunDevice) Wrapper() *DeviceWrapper {
|
||||||
|
return t.wrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func routesToString(routes []string) string {
|
||||||
return strings.Join(routes, ";")
|
return strings.Join(routes, ";")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunDevice) searchDomainsToString(searchDomains []string) string {
|
func searchDomainsToString(searchDomains []string) string {
|
||||||
return strings.Join(searchDomains, ";")
|
return strings.Join(searchDomains, ";")
|
||||||
}
|
}
|
||||||
|
|||||||
6
iface/tun_args.go
Normal file
6
iface/tun_args.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package iface
|
||||||
|
|
||||||
|
type MobileIFaceArguments struct {
|
||||||
|
TunAdapter TunAdapter // only for Android
|
||||||
|
TunFd int // only for iOS
|
||||||
|
}
|
||||||
@@ -6,32 +6,129 @@ package iface
|
|||||||
import (
|
import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
|
"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"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/iface/bind"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *tunDevice) Create() error {
|
type tunDevice struct {
|
||||||
var err error
|
name string
|
||||||
c.netInterface, err = c.createWithUserspace()
|
address WGAddress
|
||||||
|
port int
|
||||||
|
key string
|
||||||
|
mtu int
|
||||||
|
iceBind *bind.ICEBind
|
||||||
|
|
||||||
|
device *device.Device
|
||||||
|
wrapper *DeviceWrapper
|
||||||
|
udpMux *bind.UniversalUDPMuxDefault
|
||||||
|
configurer wgConfigurer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTunDevice(name string, address WGAddress, port int, key string, mtu int, transportNet transport.Net) wgTunDevice {
|
||||||
|
return &tunDevice{
|
||||||
|
name: name,
|
||||||
|
address: address,
|
||||||
|
port: port,
|
||||||
|
key: key,
|
||||||
|
mtu: mtu,
|
||||||
|
iceBind: bind.NewICEBind(transportNet),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Create() (wgConfigurer, error) {
|
||||||
|
tunDevice, err := tun.CreateTUN(t.name, t.mtu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
|
}
|
||||||
|
t.wrapper = newDeviceWrapper(tunDevice)
|
||||||
|
|
||||||
|
// We need to create a wireguard-go device and listen to configuration requests
|
||||||
|
t.device = device.NewDevice(
|
||||||
|
t.wrapper,
|
||||||
|
t.iceBind,
|
||||||
|
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
||||||
|
)
|
||||||
|
|
||||||
|
err = t.assignAddr()
|
||||||
|
if err != nil {
|
||||||
|
t.device.Close()
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.assignAddr()
|
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||||
|
err = t.configurer.configureInterface(t.key, t.port)
|
||||||
|
if err != nil {
|
||||||
|
t.device.Close()
|
||||||
|
t.configurer.close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return t.configurer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||||
|
err := t.device.Up()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
udpMux, err := t.iceBind.GetICEMux()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.udpMux = udpMux
|
||||||
|
log.Debugf("device is ready to use: %s", t.name)
|
||||||
|
return udpMux, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) UpdateAddr(address WGAddress) error {
|
||||||
|
t.address = address
|
||||||
|
return t.assignAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Close() error {
|
||||||
|
if t.configurer != nil {
|
||||||
|
t.configurer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.device != nil {
|
||||||
|
t.device.Close()
|
||||||
|
t.device = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.udpMux != nil {
|
||||||
|
return t.udpMux.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) WgAddress() WGAddress {
|
||||||
|
return t.address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) DeviceName() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Wrapper() *DeviceWrapper {
|
||||||
|
return t.wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
||||||
func (c *tunDevice) assignAddr() error {
|
func (t *tunDevice) assignAddr() error {
|
||||||
cmd := exec.Command("ifconfig", c.name, "inet", c.address.IP.String(), c.address.IP.String())
|
cmd := exec.Command("ifconfig", t.name, "inet", t.address.IP.String(), t.address.IP.String())
|
||||||
if out, err := cmd.CombinedOutput(); err != nil {
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||||||
log.Infof(`adding address command "%v" failed with output %s and error: `, cmd.String(), out)
|
log.Infof(`adding address command "%v" failed with output %s and error: `, cmd.String(), out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
routeCmd := exec.Command("route", "add", "-net", c.address.Network.String(), "-interface", c.name)
|
routeCmd := exec.Command("route", "add", "-net", t.address.Network.String(), "-interface", t.name)
|
||||||
if out, err := routeCmd.CombinedOutput(); err != nil {
|
if out, err := routeCmd.CombinedOutput(); err != nil {
|
||||||
log.Printf(`adding route command "%v" failed with output %s and error: `, routeCmd.String(), out)
|
log.Printf(`adding route command "%v" failed with output %s and error: `, routeCmd.String(), out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package iface
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v3"
|
||||||
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/device"
|
||||||
@@ -16,63 +16,82 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type tunDevice struct {
|
type tunDevice struct {
|
||||||
address WGAddress
|
|
||||||
mtu int
|
|
||||||
tunAdapter TunAdapter
|
|
||||||
iceBind *bind.ICEBind
|
|
||||||
|
|
||||||
fd int
|
|
||||||
name string
|
name string
|
||||||
|
address WGAddress
|
||||||
|
port int
|
||||||
|
key string
|
||||||
|
iceBind *bind.ICEBind
|
||||||
|
tunFd int
|
||||||
|
|
||||||
device *device.Device
|
device *device.Device
|
||||||
wrapper *DeviceWrapper
|
wrapper *DeviceWrapper
|
||||||
|
udpMux *bind.UniversalUDPMuxDefault
|
||||||
|
configurer wgConfigurer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTunDevice(name string, address WGAddress, mtu int, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
|
func newTunDevice(name string, address WGAddress, port int, key string, transportNet transport.Net, tunFd int) *tunDevice {
|
||||||
return &tunDevice{
|
return &tunDevice{
|
||||||
name: name,
|
name: name,
|
||||||
address: address,
|
address: address,
|
||||||
mtu: mtu,
|
port: port,
|
||||||
tunAdapter: tunAdapter,
|
key: key,
|
||||||
iceBind: bind.NewICEBind(transportNet),
|
iceBind: bind.NewICEBind(transportNet),
|
||||||
|
tunFd: tunFd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunDevice) Create(tunFd int32) error {
|
func (t *tunDevice) Create() (wgConfigurer, error) {
|
||||||
log.Infof("create tun interface")
|
log.Infof("create tun interface")
|
||||||
|
|
||||||
dupTunFd, err := unix.Dup(int(tunFd))
|
dupTunFd, err := unix.Dup(t.tunFd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to dup tun fd: %v", err)
|
log.Errorf("Unable to dup tun fd: %v", err)
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = unix.SetNonblock(dupTunFd, true)
|
err = unix.SetNonblock(dupTunFd, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to set tun fd as non blocking: %v", err)
|
log.Errorf("Unable to set tun fd as non blocking: %v", err)
|
||||||
unix.Close(dupTunFd)
|
_ = unix.Close(dupTunFd)
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
tun, err := tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0)
|
tunDevice, err := tun.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)
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
t.wrapper = newDeviceWrapper(tun)
|
t.wrapper = newDeviceWrapper(tunDevice)
|
||||||
log.Debug("Attaching to interface")
|
log.Debug("Attaching to interface")
|
||||||
t.device = device.NewDevice(t.wrapper, t.iceBind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
t.device = device.NewDevice(t.wrapper, t.iceBind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
||||||
// 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()
|
||||||
|
|
||||||
err = t.device.Up()
|
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||||
|
err = t.configurer.configureInterface(t.key, t.port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.device.Close()
|
t.device.Close()
|
||||||
return err
|
t.configurer.close()
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return t.configurer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||||
|
err := t.device.Up()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
udpMux, err := t.iceBind.GetICEMux()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.udpMux = udpMux
|
||||||
log.Debugf("device is ready to use: %s", t.name)
|
log.Debugf("device is ready to use: %s", t.name)
|
||||||
return nil
|
return udpMux, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunDevice) Device() *device.Device {
|
func (t *tunDevice) Device() *device.Device {
|
||||||
@@ -83,6 +102,23 @@ func (t *tunDevice) DeviceName() string {
|
|||||||
return t.name
|
return t.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Close() error {
|
||||||
|
if t.configurer != nil {
|
||||||
|
t.configurer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.device != nil {
|
||||||
|
t.device.Close()
|
||||||
|
t.device = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.udpMux != nil {
|
||||||
|
return t.udpMux.Close()
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *tunDevice) WgAddress() WGAddress {
|
func (t *tunDevice) WgAddress() WGAddress {
|
||||||
return t.address
|
return t.address
|
||||||
}
|
}
|
||||||
@@ -92,10 +128,6 @@ func (t *tunDevice) UpdateAddr(addr WGAddress) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunDevice) Close() (err error) {
|
func (t *tunDevice) Wrapper() *DeviceWrapper {
|
||||||
if t.device != nil {
|
return t.wrapper
|
||||||
t.device.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|||||||
209
iface/tun_kernel_linux.go
Normal file
209
iface/tun_kernel_linux.go
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
//go:build linux && !android
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pion/transport/v3"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/iface/bind"
|
||||||
|
"github.com/netbirdio/netbird/sharedsock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tunKernelDevice struct {
|
||||||
|
name string
|
||||||
|
address WGAddress
|
||||||
|
wgPort int
|
||||||
|
key string
|
||||||
|
mtu int
|
||||||
|
ctx context.Context
|
||||||
|
ctxCancel context.CancelFunc
|
||||||
|
transportNet transport.Net
|
||||||
|
|
||||||
|
link *wgLink
|
||||||
|
udpMuxConn net.PacketConn
|
||||||
|
udpMux *bind.UniversalUDPMuxDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTunDevice(name string, address WGAddress, wgPort int, key string, mtu int, transportNet transport.Net) wgTunDevice {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
return &tunKernelDevice{
|
||||||
|
ctx: ctx,
|
||||||
|
ctxCancel: cancel,
|
||||||
|
name: name,
|
||||||
|
address: address,
|
||||||
|
wgPort: wgPort,
|
||||||
|
key: key,
|
||||||
|
mtu: mtu,
|
||||||
|
transportNet: transportNet,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunKernelDevice) Create() (wgConfigurer, error) {
|
||||||
|
link := newWGLink(t.name)
|
||||||
|
|
||||||
|
// check if interface exists
|
||||||
|
l, err := netlink.LinkByName(t.name)
|
||||||
|
if err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case netlink.LinkNotFoundError:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove if interface exists
|
||||||
|
if l != nil {
|
||||||
|
err = netlink.LinkDel(link)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("adding device: %s", t.name)
|
||||||
|
err = netlink.LinkAdd(link)
|
||||||
|
if os.IsExist(err) {
|
||||||
|
log.Infof("interface %s already exists. Will reuse.", t.name)
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.link = link
|
||||||
|
|
||||||
|
err = t.assignAddr()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo do a discovery
|
||||||
|
log.Debugf("setting MTU: %d interface: %s", t.mtu, t.name)
|
||||||
|
err = netlink.LinkSetMTU(link, t.mtu)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error setting MTU on interface: %s", t.name)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
configurer := newWGConfigurer(t.name)
|
||||||
|
err = configurer.configureInterface(t.key, t.wgPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return configurer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunKernelDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||||
|
if t.udpMux != nil {
|
||||||
|
return t.udpMux, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.link == nil {
|
||||||
|
return nil, fmt.Errorf("device is not ready yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("bringing up interface: %s", t.name)
|
||||||
|
err := netlink.LinkSetUp(t.link)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error bringing up interface: %s", t.name)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawSock, err := sharedsock.Listen(t.wgPort, sharedsock.NewIncomingSTUNFilter())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
bindParams := bind.UniversalUDPMuxParams{
|
||||||
|
UDPConn: rawSock,
|
||||||
|
Net: t.transportNet,
|
||||||
|
}
|
||||||
|
mux := bind.NewUniversalUDPMuxDefault(bindParams)
|
||||||
|
go mux.ReadFromConn(t.ctx)
|
||||||
|
t.udpMuxConn = rawSock
|
||||||
|
t.udpMux = mux
|
||||||
|
|
||||||
|
log.Debugf("device is ready to use: %s", t.name)
|
||||||
|
return t.udpMux, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunKernelDevice) UpdateAddr(address WGAddress) error {
|
||||||
|
t.address = address
|
||||||
|
return t.assignAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunKernelDevice) Close() error {
|
||||||
|
if t.link == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
t.ctxCancel()
|
||||||
|
|
||||||
|
var closErr error
|
||||||
|
if err := t.link.Close(); err != nil {
|
||||||
|
log.Debugf("failed to close link: %s", err)
|
||||||
|
closErr = err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.udpMux != nil {
|
||||||
|
if err := t.udpMux.Close(); err != nil {
|
||||||
|
log.Debugf("failed to close udp mux: %s", err)
|
||||||
|
closErr = err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.udpMuxConn.Close(); err != nil {
|
||||||
|
log.Debugf("failed to close udp mux connection: %s", err)
|
||||||
|
closErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunKernelDevice) WgAddress() WGAddress {
|
||||||
|
return t.address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunKernelDevice) DeviceName() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunKernelDevice) Wrapper() *DeviceWrapper {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignAddr Adds IP address to the tunnel interface
|
||||||
|
func (t *tunKernelDevice) assignAddr() error {
|
||||||
|
link := newWGLink(t.name)
|
||||||
|
|
||||||
|
//delete existing addresses
|
||||||
|
list, err := netlink.AddrList(link, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(list) > 0 {
|
||||||
|
for _, a := range list {
|
||||||
|
addr := a
|
||||||
|
err = netlink.AddrDel(link, &addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("adding address %s to interface: %s", t.address.String(), t.name)
|
||||||
|
addr, _ := netlink.ParseAddr(t.address.String())
|
||||||
|
err = netlink.AddrAdd(link, addr)
|
||||||
|
if os.IsExist(err) {
|
||||||
|
log.Infof("interface %s already has the address: %s", t.name, t.address.String())
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// On linux, the link must be brought up
|
||||||
|
err = netlink.LinkSetUp(link)
|
||||||
|
return err
|
||||||
|
}
|
||||||
33
iface/tun_link_linux.go
Normal file
33
iface/tun_link_linux.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//go:build linux && !android
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import "github.com/vishvananda/netlink"
|
||||||
|
|
||||||
|
type wgLink struct {
|
||||||
|
attrs *netlink.LinkAttrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWGLink(name string) *wgLink {
|
||||||
|
attrs := netlink.NewLinkAttrs()
|
||||||
|
attrs.Name = name
|
||||||
|
|
||||||
|
return &wgLink{
|
||||||
|
attrs: &attrs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attrs returns the Wireguard's default attributes
|
||||||
|
func (l *wgLink) Attrs() *netlink.LinkAttrs {
|
||||||
|
return l.attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the interface type
|
||||||
|
func (l *wgLink) Type() string {
|
||||||
|
return "wireguard"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close deletes the link interface
|
||||||
|
func (l *wgLink) Close() error {
|
||||||
|
return netlink.LinkDel(l)
|
||||||
|
}
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
//go:build linux && !android
|
|
||||||
|
|
||||||
package iface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/vishvananda/netlink"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *tunDevice) Create() error {
|
|
||||||
if WireGuardModuleIsLoaded() {
|
|
||||||
log.Infof("create tun interface with kernel WireGuard support: %s", c.DeviceName())
|
|
||||||
return c.createWithKernel()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tunModuleIsLoaded() {
|
|
||||||
return fmt.Errorf("couldn't check or load tun module")
|
|
||||||
}
|
|
||||||
log.Infof("create tun interface with userspace WireGuard support: %s", c.DeviceName())
|
|
||||||
var err error
|
|
||||||
c.netInterface, err = c.createWithUserspace()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.assignAddr()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// createWithKernel Creates a new WireGuard interface using kernel WireGuard module.
|
|
||||||
// Works for Linux and offers much better network performance
|
|
||||||
func (c *tunDevice) createWithKernel() error {
|
|
||||||
|
|
||||||
link := newWGLink(c.name)
|
|
||||||
|
|
||||||
// check if interface exists
|
|
||||||
l, err := netlink.LinkByName(c.name)
|
|
||||||
if err != nil {
|
|
||||||
switch err.(type) {
|
|
||||||
case netlink.LinkNotFoundError:
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove if interface exists
|
|
||||||
if l != nil {
|
|
||||||
err = netlink.LinkDel(link)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("adding device: %s", c.name)
|
|
||||||
err = netlink.LinkAdd(link)
|
|
||||||
if os.IsExist(err) {
|
|
||||||
log.Infof("interface %s already exists. Will reuse.", c.name)
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.netInterface = link
|
|
||||||
|
|
||||||
err = c.assignAddr()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo do a discovery
|
|
||||||
log.Debugf("setting MTU: %d interface: %s", c.mtu, c.name)
|
|
||||||
err = netlink.LinkSetMTU(link, c.mtu)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("error setting MTU on interface: %s", c.name)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("bringing up interface: %s", c.name)
|
|
||||||
err = netlink.LinkSetUp(link)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("error bringing up interface: %s", c.name)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// assignAddr Adds IP address to the tunnel interface
|
|
||||||
func (c *tunDevice) assignAddr() error {
|
|
||||||
link := newWGLink(c.name)
|
|
||||||
|
|
||||||
//delete existing addresses
|
|
||||||
list, err := netlink.AddrList(link, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(list) > 0 {
|
|
||||||
for _, a := range list {
|
|
||||||
addr := a
|
|
||||||
err = netlink.AddrDel(link, &addr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("adding address %s to interface: %s", c.address.String(), c.name)
|
|
||||||
addr, _ := netlink.ParseAddr(c.address.String())
|
|
||||||
err = netlink.AddrAdd(link, addr)
|
|
||||||
if os.IsExist(err) {
|
|
||||||
log.Infof("interface %s already has the address: %s", c.name, c.address.String())
|
|
||||||
} else if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// On linux, the link must be brought up
|
|
||||||
err = netlink.LinkSetUp(link)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type wgLink struct {
|
|
||||||
attrs *netlink.LinkAttrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWGLink(name string) *wgLink {
|
|
||||||
attrs := netlink.NewLinkAttrs()
|
|
||||||
attrs.Name = name
|
|
||||||
|
|
||||||
return &wgLink{
|
|
||||||
attrs: &attrs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attrs returns the Wireguard's default attributes
|
|
||||||
func (l *wgLink) Attrs() *netlink.LinkAttrs {
|
|
||||||
return l.attrs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type returns the interface type
|
|
||||||
func (l *wgLink) Type() string {
|
|
||||||
return "wireguard"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close deletes the link interface
|
|
||||||
func (l *wgLink) Close() error {
|
|
||||||
return netlink.LinkDel(l)
|
|
||||||
}
|
|
||||||
119
iface/tun_netstack.go
Normal file
119
iface/tun_netstack.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
//go:build !android
|
||||||
|
// +build !android
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/pion/transport/v3"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/device"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/iface/bind"
|
||||||
|
"github.com/netbirdio/netbird/iface/netstack"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tunNetstackDevice struct {
|
||||||
|
name string
|
||||||
|
address WGAddress
|
||||||
|
port int
|
||||||
|
key string
|
||||||
|
mtu int
|
||||||
|
listenAddress string
|
||||||
|
iceBind *bind.ICEBind
|
||||||
|
|
||||||
|
device *device.Device
|
||||||
|
wrapper *DeviceWrapper
|
||||||
|
nsTun *netstack.NetStackTun
|
||||||
|
udpMux *bind.UniversalUDPMuxDefault
|
||||||
|
configurer wgConfigurer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTunNetstackDevice(name string, address WGAddress, wgPort int, key string, mtu int, transportNet transport.Net, listenAddress string) wgTunDevice {
|
||||||
|
return &tunNetstackDevice{
|
||||||
|
name: name,
|
||||||
|
address: address,
|
||||||
|
port: wgPort,
|
||||||
|
key: key,
|
||||||
|
mtu: mtu,
|
||||||
|
listenAddress: listenAddress,
|
||||||
|
iceBind: bind.NewICEBind(transportNet),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunNetstackDevice) Create() (wgConfigurer, error) {
|
||||||
|
log.Info("create netstack tun interface")
|
||||||
|
t.nsTun = netstack.NewNetStackTun(t.listenAddress, t.address.IP.String(), t.mtu)
|
||||||
|
tunIface, err := t.nsTun.Create()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.wrapper = newDeviceWrapper(tunIface)
|
||||||
|
|
||||||
|
t.device = device.NewDevice(
|
||||||
|
t.wrapper,
|
||||||
|
t.iceBind,
|
||||||
|
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||||
|
err = t.configurer.configureInterface(t.key, t.port)
|
||||||
|
if err != nil {
|
||||||
|
_ = tunIface.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("device has been created: %s", t.name)
|
||||||
|
return t.configurer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunNetstackDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||||
|
if t.device == nil {
|
||||||
|
return nil, fmt.Errorf("device is not ready yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := t.device.Up()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
udpMux, err := t.iceBind.GetICEMux()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.udpMux = udpMux
|
||||||
|
log.Debugf("netstack device is ready to use")
|
||||||
|
return udpMux, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunNetstackDevice) UpdateAddr(WGAddress) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunNetstackDevice) Close() error {
|
||||||
|
if t.configurer != nil {
|
||||||
|
t.configurer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.device != nil {
|
||||||
|
t.device.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.udpMux != nil {
|
||||||
|
return t.udpMux.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunNetstackDevice) WgAddress() WGAddress {
|
||||||
|
return t.address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunNetstackDevice) DeviceName() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunNetstackDevice) Wrapper() *DeviceWrapper {
|
||||||
|
return t.wrapper
|
||||||
|
}
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
//go:build (linux || darwin) && !android && !ios
|
|
||||||
|
|
||||||
package iface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
|
||||||
"golang.zx2c4.com/wireguard/ipc"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface/bind"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.zx2c4.com/wireguard/device"
|
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tunDevice struct {
|
|
||||||
name string
|
|
||||||
address WGAddress
|
|
||||||
mtu int
|
|
||||||
netInterface NetInterface
|
|
||||||
iceBind *bind.ICEBind
|
|
||||||
uapi net.Listener
|
|
||||||
wrapper *DeviceWrapper
|
|
||||||
close chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTunDevice(name string, address WGAddress, mtu int, transportNet transport.Net) *tunDevice {
|
|
||||||
return &tunDevice{
|
|
||||||
name: name,
|
|
||||||
address: address,
|
|
||||||
mtu: mtu,
|
|
||||||
iceBind: bind.NewICEBind(transportNet),
|
|
||||||
close: make(chan struct{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tunDevice) UpdateAddr(address WGAddress) error {
|
|
||||||
c.address = address
|
|
||||||
return c.assignAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tunDevice) WgAddress() WGAddress {
|
|
||||||
return c.address
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tunDevice) DeviceName() string {
|
|
||||||
return c.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tunDevice) Close() error {
|
|
||||||
|
|
||||||
select {
|
|
||||||
case c.close <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
var err1, err2, err3 error
|
|
||||||
if c.netInterface != nil {
|
|
||||||
err1 = c.netInterface.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.uapi != nil {
|
|
||||||
err2 = c.uapi.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
sockPath := "/var/run/wireguard/" + c.name + ".sock"
|
|
||||||
if _, statErr := os.Stat(sockPath); statErr == nil {
|
|
||||||
statErr = os.Remove(sockPath)
|
|
||||||
if statErr != nil {
|
|
||||||
err3 = statErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err1 != nil {
|
|
||||||
return err1
|
|
||||||
}
|
|
||||||
|
|
||||||
if err2 != nil {
|
|
||||||
return err2
|
|
||||||
}
|
|
||||||
|
|
||||||
return err3
|
|
||||||
}
|
|
||||||
|
|
||||||
// createWithUserspace Creates a new Wireguard interface, using wireguard-go userspace implementation
|
|
||||||
func (c *tunDevice) createWithUserspace() (NetInterface, error) {
|
|
||||||
tunIface, err := tun.CreateTUN(c.name, c.mtu)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c.wrapper = newDeviceWrapper(tunIface)
|
|
||||||
|
|
||||||
// We need to create a wireguard-go device and listen to configuration requests
|
|
||||||
tunDev := device.NewDevice(
|
|
||||||
c.wrapper,
|
|
||||||
c.iceBind,
|
|
||||||
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
|
||||||
)
|
|
||||||
err = tunDev.Up()
|
|
||||||
if err != nil {
|
|
||||||
_ = tunIface.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.uapi, err = c.getUAPI(c.name)
|
|
||||||
if err != nil {
|
|
||||||
_ = tunIface.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-c.close:
|
|
||||||
log.Debugf("exit uapi.Accept()")
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
uapiConn, uapiErr := c.uapi.Accept()
|
|
||||||
if uapiErr != nil {
|
|
||||||
log.Traceln("uapi Accept failed with error: ", uapiErr)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
tunDev.IpcHandle(uapiConn)
|
|
||||||
log.Debugf("exit tunDevice.IpcHandle")
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.Debugln("UAPI listener started")
|
|
||||||
return tunIface, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getUAPI returns a Listener
|
|
||||||
func (c *tunDevice) getUAPI(iface string) (net.Listener, error) {
|
|
||||||
tunSock, err := ipc.UAPIOpen(iface)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ipc.UAPIListen(iface, tunSock)
|
|
||||||
}
|
|
||||||
158
iface/tun_usp_linux.go
Normal file
158
iface/tun_usp_linux.go
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
//go:build linux && !android
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pion/transport/v3"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
|
"golang.zx2c4.com/wireguard/device"
|
||||||
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/iface/bind"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tunUSPDevice struct {
|
||||||
|
name string
|
||||||
|
address WGAddress
|
||||||
|
port int
|
||||||
|
key string
|
||||||
|
mtu int
|
||||||
|
iceBind *bind.ICEBind
|
||||||
|
|
||||||
|
device *device.Device
|
||||||
|
wrapper *DeviceWrapper
|
||||||
|
udpMux *bind.UniversalUDPMuxDefault
|
||||||
|
configurer wgConfigurer
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTunUSPDevice(name string, address WGAddress, port int, key string, mtu int, transportNet transport.Net) wgTunDevice {
|
||||||
|
log.Infof("using userspace bind mode")
|
||||||
|
return &tunUSPDevice{
|
||||||
|
name: name,
|
||||||
|
address: address,
|
||||||
|
port: port,
|
||||||
|
key: key,
|
||||||
|
mtu: mtu,
|
||||||
|
iceBind: bind.NewICEBind(transportNet),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunUSPDevice) Create() (wgConfigurer, error) {
|
||||||
|
log.Info("create tun interface")
|
||||||
|
tunIface, err := tun.CreateTUN(t.name, t.mtu)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("failed to create tun unterface (%s, %d): %s", t.name, t.mtu, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.wrapper = newDeviceWrapper(tunIface)
|
||||||
|
|
||||||
|
// We need to create a wireguard-go device and listen to configuration requests
|
||||||
|
t.device = device.NewDevice(
|
||||||
|
t.wrapper,
|
||||||
|
t.iceBind,
|
||||||
|
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
||||||
|
)
|
||||||
|
|
||||||
|
err = t.assignAddr()
|
||||||
|
if err != nil {
|
||||||
|
t.device.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||||
|
err = t.configurer.configureInterface(t.key, t.port)
|
||||||
|
if err != nil {
|
||||||
|
t.device.Close()
|
||||||
|
t.configurer.close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return t.configurer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunUSPDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||||
|
if t.device == nil {
|
||||||
|
return nil, fmt.Errorf("device is not ready yet")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := t.device.Up()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
udpMux, err := t.iceBind.GetICEMux()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
t.udpMux = udpMux
|
||||||
|
|
||||||
|
log.Debugf("device is ready to use: %s", t.name)
|
||||||
|
return udpMux, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunUSPDevice) UpdateAddr(address WGAddress) error {
|
||||||
|
t.address = address
|
||||||
|
return t.assignAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunUSPDevice) Close() error {
|
||||||
|
if t.configurer != nil {
|
||||||
|
t.configurer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.device != nil {
|
||||||
|
t.device.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.udpMux != nil {
|
||||||
|
return t.udpMux.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunUSPDevice) WgAddress() WGAddress {
|
||||||
|
return t.address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunUSPDevice) DeviceName() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunUSPDevice) Wrapper() *DeviceWrapper {
|
||||||
|
return t.wrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignAddr Adds IP address to the tunnel interface
|
||||||
|
func (t *tunUSPDevice) assignAddr() error {
|
||||||
|
link := newWGLink(t.name)
|
||||||
|
|
||||||
|
//delete existing addresses
|
||||||
|
list, err := netlink.AddrList(link, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(list) > 0 {
|
||||||
|
for _, a := range list {
|
||||||
|
addr := a
|
||||||
|
err = netlink.AddrDel(link, &addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("adding address %s to interface: %s", t.address.String(), t.name)
|
||||||
|
addr, _ := netlink.ParseAddr(t.address.String())
|
||||||
|
err = netlink.AddrAdd(link, addr)
|
||||||
|
if os.IsExist(err) {
|
||||||
|
log.Infof("interface %s already has the address: %s", t.name, t.address.String())
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// On linux, the link must be brought up
|
||||||
|
err = netlink.LinkSetUp(link)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -2,14 +2,12 @@ package iface
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v3"
|
||||||
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/device"
|
||||||
"golang.zx2c4.com/wireguard/ipc"
|
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||||
|
|
||||||
@@ -19,137 +17,129 @@ import (
|
|||||||
type tunDevice struct {
|
type tunDevice struct {
|
||||||
name string
|
name string
|
||||||
address WGAddress
|
address WGAddress
|
||||||
netInterface NetInterface
|
port int
|
||||||
iceBind *bind.ICEBind
|
key string
|
||||||
mtu int
|
mtu int
|
||||||
uapi net.Listener
|
iceBind *bind.ICEBind
|
||||||
|
|
||||||
|
device *device.Device
|
||||||
|
nativeTunDevice *tun.NativeTun
|
||||||
wrapper *DeviceWrapper
|
wrapper *DeviceWrapper
|
||||||
close chan struct{}
|
udpMux *bind.UniversalUDPMuxDefault
|
||||||
|
configurer wgConfigurer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTunDevice(name string, address WGAddress, mtu int, transportNet transport.Net) *tunDevice {
|
func newTunDevice(name string, address WGAddress, port int, key string, mtu int, transportNet transport.Net) wgTunDevice {
|
||||||
return &tunDevice{
|
return &tunDevice{
|
||||||
name: name,
|
name: name,
|
||||||
address: address,
|
address: address,
|
||||||
|
port: port,
|
||||||
|
key: key,
|
||||||
mtu: mtu,
|
mtu: mtu,
|
||||||
iceBind: bind.NewICEBind(transportNet),
|
iceBind: bind.NewICEBind(transportNet),
|
||||||
close: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tunDevice) Create() error {
|
func (t *tunDevice) Create() (wgConfigurer, error) {
|
||||||
var err error
|
tunDevice, err := tun.CreateTUN(t.name, t.mtu)
|
||||||
c.netInterface, err = c.createWithUserspace()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.assignAddr()
|
|
||||||
}
|
|
||||||
|
|
||||||
// createWithUserspace Creates a new Wireguard interface, using wireguard-go userspace implementation
|
|
||||||
func (c *tunDevice) createWithUserspace() (NetInterface, error) {
|
|
||||||
tunIface, err := tun.CreateTUN(c.name, c.mtu)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.wrapper = newDeviceWrapper(tunIface)
|
t.nativeTunDevice = tunDevice.(*tun.NativeTun)
|
||||||
|
t.wrapper = newDeviceWrapper(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
|
||||||
tunDev := device.NewDevice(c.wrapper, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] "))
|
t.device = device.NewDevice(
|
||||||
err = tunDev.Up()
|
t.wrapper,
|
||||||
if err != nil {
|
t.iceBind,
|
||||||
_ = tunIface.Close()
|
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
||||||
return nil, err
|
)
|
||||||
}
|
|
||||||
|
|
||||||
luid := winipcfg.LUID(tunIface.(*tun.NativeTun).LUID())
|
luid := winipcfg.LUID(t.nativeTunDevice.LUID())
|
||||||
|
|
||||||
nbiface, err := luid.IPInterface(windows.AF_INET)
|
nbiface, err := luid.IPInterface(windows.AF_INET)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tunIface.Close()
|
t.device.Close()
|
||||||
return nil, fmt.Errorf("got error when getting ip interface %s", err)
|
return nil, fmt.Errorf("got error when getting ip interface %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nbiface.NLMTU = uint32(c.mtu)
|
nbiface.NLMTU = uint32(t.mtu)
|
||||||
|
|
||||||
err = nbiface.Set()
|
err = nbiface.Set()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tunIface.Close()
|
t.device.Close()
|
||||||
return nil, fmt.Errorf("got error when getting setting the interface mtu: %s", err)
|
return nil, fmt.Errorf("got error when getting setting the interface mtu: %s", err)
|
||||||
}
|
}
|
||||||
|
err = t.assignAddr()
|
||||||
c.uapi, err = c.getUAPI(c.name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tunIface.Close()
|
t.device.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
t.configurer = newWGUSPConfigurer(t.device, t.name)
|
||||||
for {
|
err = t.configurer.configureInterface(t.key, t.port)
|
||||||
select {
|
if err != nil {
|
||||||
case <-c.close:
|
t.device.Close()
|
||||||
log.Debugf("exit uapi.Accept()")
|
t.configurer.close()
|
||||||
return
|
return nil, err
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
uapiConn, uapiErr := c.uapi.Accept()
|
return t.configurer, nil
|
||||||
if uapiErr != nil {
|
|
||||||
log.Traceln("uapi Accept failed with error: ", uapiErr)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
tunDev.IpcHandle(uapiConn)
|
|
||||||
log.Debugf("exit tunDevice.IpcHandle")
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
log.Debugln("UAPI listener started")
|
|
||||||
return tunIface, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tunDevice) UpdateAddr(address WGAddress) error {
|
func (t *tunDevice) Up() (*bind.UniversalUDPMuxDefault, error) {
|
||||||
c.address = address
|
err := t.device.Up()
|
||||||
return c.assignAddr()
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
|
||||||
func (c *tunDevice) WgAddress() WGAddress {
|
|
||||||
return c.address
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tunDevice) DeviceName() string {
|
|
||||||
return c.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *tunDevice) Close() error {
|
|
||||||
select {
|
|
||||||
case c.close <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var err1, err2 error
|
udpMux, err := t.iceBind.GetICEMux()
|
||||||
if c.netInterface != nil {
|
if err != nil {
|
||||||
err1 = c.netInterface.Close()
|
return nil, err
|
||||||
}
|
}
|
||||||
|
t.udpMux = udpMux
|
||||||
if c.uapi != nil {
|
log.Debugf("device is ready to use: %s", t.name)
|
||||||
err2 = c.uapi.Close()
|
return udpMux, nil
|
||||||
}
|
|
||||||
|
|
||||||
if err1 != nil {
|
|
||||||
return err1
|
|
||||||
}
|
|
||||||
|
|
||||||
return err2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tunDevice) getInterfaceGUIDString() (string, error) {
|
func (t *tunDevice) UpdateAddr(address WGAddress) error {
|
||||||
if c.netInterface == nil {
|
t.address = address
|
||||||
|
return t.assignAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Close() error {
|
||||||
|
if t.configurer != nil {
|
||||||
|
t.configurer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.device != nil {
|
||||||
|
t.device.Close()
|
||||||
|
t.device = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.udpMux != nil {
|
||||||
|
return t.udpMux.Close()
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (t *tunDevice) WgAddress() WGAddress {
|
||||||
|
return t.address
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) DeviceName() string {
|
||||||
|
return t.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) Wrapper() *DeviceWrapper {
|
||||||
|
return t.wrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) getInterfaceGUIDString() (string, error) {
|
||||||
|
if t.nativeTunDevice == nil {
|
||||||
return "", fmt.Errorf("interface has not been initialized yet")
|
return "", fmt.Errorf("interface has not been initialized yet")
|
||||||
}
|
}
|
||||||
windowsDevice := c.netInterface.(*tun.NativeTun)
|
|
||||||
luid := winipcfg.LUID(windowsDevice.LUID())
|
luid := winipcfg.LUID(t.nativeTunDevice.LUID())
|
||||||
guid, err := luid.GUID()
|
guid, err := luid.GUID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -158,14 +148,8 @@ func (c *tunDevice) getInterfaceGUIDString() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
// assignAddr Adds IP address to the tunnel interface and network route based on the range provided
|
||||||
func (c *tunDevice) assignAddr() error {
|
func (t *tunDevice) assignAddr() error {
|
||||||
tunDev := c.netInterface.(*tun.NativeTun)
|
luid := winipcfg.LUID(t.nativeTunDevice.LUID())
|
||||||
luid := winipcfg.LUID(tunDev.LUID())
|
log.Debugf("adding address %s to interface: %s", t.address.IP, t.name)
|
||||||
log.Debugf("adding address %s to interface: %s", c.address.IP, c.name)
|
return luid.SetIPAddresses([]netip.Prefix{netip.MustParsePrefix(t.address.String())})
|
||||||
return luid.SetIPAddresses([]netip.Prefix{netip.MustParsePrefix(c.address.String())})
|
|
||||||
}
|
|
||||||
|
|
||||||
// getUAPI returns a Listener
|
|
||||||
func (c *tunDevice) getUAPI(iface string) (net.Listener, error) {
|
|
||||||
return ipc.UAPIListen(iface)
|
|
||||||
}
|
}
|
||||||
|
|||||||
26
iface/uapi.go
Normal file
26
iface/uapi.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/ipc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func openUAPI(deviceName string) (net.Listener, error) {
|
||||||
|
uapiSock, err := ipc.UAPIOpen(deviceName)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to open uapi socket: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
listener, err := ipc.UAPIListen(deviceName, uapiSock)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to listen on uapi socket: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return listener, nil
|
||||||
|
}
|
||||||
11
iface/uapi_windows.go
Normal file
11
iface/uapi_windows.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/ipc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func openUAPI(deviceName string) (net.Listener, error) {
|
||||||
|
return ipc.UAPIListen(deviceName)
|
||||||
|
}
|
||||||
17
iface/wg_configurer.go
Normal file
17
iface/wg_configurer.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wgConfigurer interface {
|
||||||
|
configureInterface(privateKey string, port int) error
|
||||||
|
updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error
|
||||||
|
removePeer(peerKey string) error
|
||||||
|
addAllowedIP(peerKey string, allowedIP string) error
|
||||||
|
removeAllowedIP(peerKey string, allowedIP string) error
|
||||||
|
close()
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !android && !ios
|
//go:build linux && !android
|
||||||
|
|
||||||
package iface
|
package iface
|
||||||
|
|
||||||
@@ -12,17 +12,18 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type wGConfigurer struct {
|
type wgKernelConfigurer struct {
|
||||||
deviceName string
|
deviceName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWGConfigurer(deviceName string) wGConfigurer {
|
func newWGConfigurer(deviceName string) wgConfigurer {
|
||||||
return wGConfigurer{
|
wgc := &wgKernelConfigurer{
|
||||||
deviceName: deviceName,
|
deviceName: deviceName,
|
||||||
}
|
}
|
||||||
|
return wgc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wGConfigurer) configureInterface(privateKey string, port int) error {
|
func (c *wgKernelConfigurer) configureInterface(privateKey string, port int) error {
|
||||||
log.Debugf("adding Wireguard private key")
|
log.Debugf("adding Wireguard private key")
|
||||||
key, err := wgtypes.ParseKey(privateKey)
|
key, err := wgtypes.ParseKey(privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -43,7 +44,7 @@ func (c *wGConfigurer) configureInterface(privateKey string, port int) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wGConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
func (c *wgKernelConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
||||||
// parse allowed ips
|
// parse allowed ips
|
||||||
_, ipNet, err := net.ParseCIDR(allowedIps)
|
_, ipNet, err := net.ParseCIDR(allowedIps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -59,8 +60,8 @@ func (c *wGConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive t
|
|||||||
ReplaceAllowedIPs: true,
|
ReplaceAllowedIPs: true,
|
||||||
AllowedIPs: []net.IPNet{*ipNet},
|
AllowedIPs: []net.IPNet{*ipNet},
|
||||||
PersistentKeepaliveInterval: &keepAlive,
|
PersistentKeepaliveInterval: &keepAlive,
|
||||||
PresharedKey: preSharedKey,
|
|
||||||
Endpoint: endpoint,
|
Endpoint: endpoint,
|
||||||
|
PresharedKey: preSharedKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
config := wgtypes.Config{
|
config := wgtypes.Config{
|
||||||
@@ -73,7 +74,7 @@ func (c *wGConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive t
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wGConfigurer) removePeer(peerKey string) error {
|
func (c *wgKernelConfigurer) removePeer(peerKey string) error {
|
||||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -94,7 +95,7 @@ func (c *wGConfigurer) removePeer(peerKey string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wGConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
func (c *wgKernelConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
||||||
_, ipNet, err := net.ParseCIDR(allowedIP)
|
_, ipNet, err := net.ParseCIDR(allowedIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -121,7 +122,7 @@ func (c *wGConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wGConfigurer) removeAllowedIP(peerKey string, allowedIP string) error {
|
func (c *wgKernelConfigurer) removeAllowedIP(peerKey string, allowedIP string) error {
|
||||||
_, ipNet, err := net.ParseCIDR(allowedIP)
|
_, ipNet, err := net.ParseCIDR(allowedIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -163,7 +164,7 @@ func (c *wGConfigurer) removeAllowedIP(peerKey string, allowedIP string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wGConfigurer) getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
|
func (c *wgKernelConfigurer) getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {
|
||||||
wg, err := wgctrl.New()
|
wg, err := wgctrl.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wgtypes.Peer{}, err
|
return wgtypes.Peer{}, err
|
||||||
@@ -187,7 +188,7 @@ func (c *wGConfigurer) getPeer(ifaceName, peerPubKey string) (wgtypes.Peer, erro
|
|||||||
return wgtypes.Peer{}, fmt.Errorf("peer not found")
|
return wgtypes.Peer{}, fmt.Errorf("peer not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *wGConfigurer) configure(config wgtypes.Config) error {
|
func (c *wgKernelConfigurer) configure(config wgtypes.Config) error {
|
||||||
wg, err := wgctrl.New()
|
wg, err := wgctrl.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -203,3 +204,6 @@ func (c *wGConfigurer) configure(config wgtypes.Config) error {
|
|||||||
|
|
||||||
return wg.ConfigureDevice(c.deviceName, config)
|
return wg.ConfigureDevice(c.deviceName, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *wgKernelConfigurer) close() {
|
||||||
|
}
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
//go:build ios || android
|
|
||||||
// +build ios android
|
|
||||||
|
|
||||||
package iface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errFuncNotImplemented = errors.New("function not implemented")
|
|
||||||
)
|
|
||||||
|
|
||||||
type wGConfigurer struct {
|
|
||||||
tunDevice *tunDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
func newWGConfigurer(tunDevice *tunDevice) wGConfigurer {
|
|
||||||
return wGConfigurer{
|
|
||||||
tunDevice: tunDevice,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *wGConfigurer) configureInterface(privateKey string, port int) error {
|
|
||||||
log.Debugf("adding Wireguard private key")
|
|
||||||
key, err := wgtypes.ParseKey(privateKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fwmark := 0
|
|
||||||
config := wgtypes.Config{
|
|
||||||
PrivateKey: &key,
|
|
||||||
ReplacePeers: true,
|
|
||||||
FirewallMark: &fwmark,
|
|
||||||
ListenPort: &port,
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *wGConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
|
||||||
// parse allowed ips
|
|
||||||
_, ipNet, err := net.ParseCIDR(allowedIps)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
peer := wgtypes.PeerConfig{
|
|
||||||
PublicKey: peerKeyParsed,
|
|
||||||
ReplaceAllowedIPs: true,
|
|
||||||
AllowedIPs: []net.IPNet{*ipNet},
|
|
||||||
PersistentKeepaliveInterval: &keepAlive,
|
|
||||||
PresharedKey: preSharedKey,
|
|
||||||
Endpoint: endpoint,
|
|
||||||
}
|
|
||||||
|
|
||||||
config := wgtypes.Config{
|
|
||||||
Peers: []wgtypes.PeerConfig{peer},
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *wGConfigurer) removePeer(peerKey string) error {
|
|
||||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
peer := wgtypes.PeerConfig{
|
|
||||||
PublicKey: peerKeyParsed,
|
|
||||||
Remove: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
config := wgtypes.Config{
|
|
||||||
Peers: []wgtypes.PeerConfig{peer},
|
|
||||||
}
|
|
||||||
return c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *wGConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
|
||||||
_, ipNet, err := net.ParseCIDR(allowedIP)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
peer := wgtypes.PeerConfig{
|
|
||||||
PublicKey: peerKeyParsed,
|
|
||||||
UpdateOnly: true,
|
|
||||||
ReplaceAllowedIPs: false,
|
|
||||||
AllowedIPs: []net.IPNet{*ipNet},
|
|
||||||
}
|
|
||||||
|
|
||||||
config := wgtypes.Config{
|
|
||||||
Peers: []wgtypes.PeerConfig{peer},
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.tunDevice.Device().IpcSet(toWgUserspaceString(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *wGConfigurer) removeAllowedIP(peerKey string, ip string) error {
|
|
||||||
ipc, err := c.tunDevice.Device().IpcGet()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
|
||||||
hexKey := hex.EncodeToString(peerKeyParsed[:])
|
|
||||||
|
|
||||||
lines := strings.Split(ipc, "\n")
|
|
||||||
|
|
||||||
output := ""
|
|
||||||
foundPeer := false
|
|
||||||
removedAllowedIP := false
|
|
||||||
for _, line := range lines {
|
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
|
|
||||||
// If we're within the details of the found peer and encounter another public key,
|
|
||||||
// this means we're starting another peer's details. So, reset the flag.
|
|
||||||
if strings.HasPrefix(line, "public_key=") && foundPeer {
|
|
||||||
foundPeer = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify the peer with the specific public key
|
|
||||||
if line == fmt.Sprintf("public_key=%s", hexKey) {
|
|
||||||
foundPeer = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're within the details of the found peer and find the specific allowed IP, skip this line
|
|
||||||
if foundPeer && line == "allowed_ip="+ip {
|
|
||||||
removedAllowedIP = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append the line to the output string
|
|
||||||
if strings.HasPrefix(line, "private_key=") || strings.HasPrefix(line, "listen_port=") ||
|
|
||||||
strings.HasPrefix(line, "public_key=") || strings.HasPrefix(line, "preshared_key=") ||
|
|
||||||
strings.HasPrefix(line, "endpoint=") || strings.HasPrefix(line, "persistent_keepalive_interval=") ||
|
|
||||||
strings.HasPrefix(line, "allowed_ip=") {
|
|
||||||
output += line + "\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !removedAllowedIP {
|
|
||||||
return fmt.Errorf("allowedIP not found")
|
|
||||||
} else {
|
|
||||||
return c.tunDevice.Device().IpcSet(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
259
iface/wg_configurer_usp.go
Normal file
259
iface/wg_configurer_usp.go
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
package iface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/device"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type wgUSPConfigurer struct {
|
||||||
|
device *device.Device
|
||||||
|
deviceName string
|
||||||
|
|
||||||
|
uapiListener net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
func newWGUSPConfigurer(device *device.Device, deviceName string) wgConfigurer {
|
||||||
|
wgCfg := &wgUSPConfigurer{
|
||||||
|
device: device,
|
||||||
|
deviceName: deviceName,
|
||||||
|
}
|
||||||
|
wgCfg.startUAPI()
|
||||||
|
return wgCfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wgUSPConfigurer) configureInterface(privateKey string, port int) error {
|
||||||
|
log.Debugf("adding Wireguard private key")
|
||||||
|
key, err := wgtypes.ParseKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fwmark := 0
|
||||||
|
config := wgtypes.Config{
|
||||||
|
PrivateKey: &key,
|
||||||
|
ReplacePeers: true,
|
||||||
|
FirewallMark: &fwmark,
|
||||||
|
ListenPort: &port,
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.device.IpcSet(toWgUserspaceString(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wgUSPConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
||||||
|
// parse allowed ips
|
||||||
|
_, ipNet, err := net.ParseCIDR(allowedIps)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
peer := wgtypes.PeerConfig{
|
||||||
|
PublicKey: peerKeyParsed,
|
||||||
|
ReplaceAllowedIPs: true,
|
||||||
|
AllowedIPs: []net.IPNet{*ipNet},
|
||||||
|
PersistentKeepaliveInterval: &keepAlive,
|
||||||
|
PresharedKey: preSharedKey,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
config := wgtypes.Config{
|
||||||
|
Peers: []wgtypes.PeerConfig{peer},
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.device.IpcSet(toWgUserspaceString(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wgUSPConfigurer) removePeer(peerKey string) error {
|
||||||
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peer := wgtypes.PeerConfig{
|
||||||
|
PublicKey: peerKeyParsed,
|
||||||
|
Remove: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
config := wgtypes.Config{
|
||||||
|
Peers: []wgtypes.PeerConfig{peer},
|
||||||
|
}
|
||||||
|
return c.device.IpcSet(toWgUserspaceString(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wgUSPConfigurer) addAllowedIP(peerKey string, allowedIP string) error {
|
||||||
|
_, ipNet, err := net.ParseCIDR(allowedIP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
peer := wgtypes.PeerConfig{
|
||||||
|
PublicKey: peerKeyParsed,
|
||||||
|
UpdateOnly: true,
|
||||||
|
ReplaceAllowedIPs: false,
|
||||||
|
AllowedIPs: []net.IPNet{*ipNet},
|
||||||
|
}
|
||||||
|
|
||||||
|
config := wgtypes.Config{
|
||||||
|
Peers: []wgtypes.PeerConfig{peer},
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.device.IpcSet(toWgUserspaceString(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *wgUSPConfigurer) removeAllowedIP(peerKey string, ip string) error {
|
||||||
|
ipc, err := c.device.IpcGet()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hexKey := hex.EncodeToString(peerKeyParsed[:])
|
||||||
|
|
||||||
|
lines := strings.Split(ipc, "\n")
|
||||||
|
|
||||||
|
output := ""
|
||||||
|
foundPeer := false
|
||||||
|
removedAllowedIP := false
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
|
// If we're within the details of the found peer and encounter another public key,
|
||||||
|
// this means we're starting another peer's details. So, reset the flag.
|
||||||
|
if strings.HasPrefix(line, "public_key=") && foundPeer {
|
||||||
|
foundPeer = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identify the peer with the specific public key
|
||||||
|
if line == fmt.Sprintf("public_key=%s", hexKey) {
|
||||||
|
foundPeer = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're within the details of the found peer and find the specific allowed IP, skip this line
|
||||||
|
if foundPeer && line == "allowed_ip="+ip {
|
||||||
|
removedAllowedIP = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the line to the output string
|
||||||
|
if strings.HasPrefix(line, "private_key=") || strings.HasPrefix(line, "listen_port=") ||
|
||||||
|
strings.HasPrefix(line, "public_key=") || strings.HasPrefix(line, "preshared_key=") ||
|
||||||
|
strings.HasPrefix(line, "endpoint=") || strings.HasPrefix(line, "persistent_keepalive_interval=") ||
|
||||||
|
strings.HasPrefix(line, "allowed_ip=") {
|
||||||
|
output += line + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !removedAllowedIP {
|
||||||
|
return fmt.Errorf("allowedIP not found")
|
||||||
|
} else {
|
||||||
|
return c.device.IpcSet(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// startUAPI starts the UAPI listener for managing the WireGuard interface via external tool
|
||||||
|
func (t *wgUSPConfigurer) startUAPI() {
|
||||||
|
var err error
|
||||||
|
t.uapiListener, err = openUAPI(t.deviceName)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to open uapi listener: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go func(uapi net.Listener) {
|
||||||
|
for {
|
||||||
|
uapiConn, uapiErr := uapi.Accept()
|
||||||
|
if uapiErr != nil {
|
||||||
|
log.Tracef("%s", uapiErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
t.device.IpcHandle(uapiConn)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}(t.uapiListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *wgUSPConfigurer) close() {
|
||||||
|
if t.uapiListener != nil {
|
||||||
|
err := t.uapiListener.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to close uapi listener: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
|
sockPath := "/var/run/wireguard/" + t.deviceName + ".sock"
|
||||||
|
if _, statErr := os.Stat(sockPath); statErr == nil {
|
||||||
|
_ = os.Remove(sockPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toWgUserspaceString(wgCfg wgtypes.Config) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
if wgCfg.PrivateKey != nil {
|
||||||
|
hexKey := hex.EncodeToString(wgCfg.PrivateKey[:])
|
||||||
|
sb.WriteString(fmt.Sprintf("private_key=%s\n", hexKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
if wgCfg.ListenPort != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("listen_port=%d\n", *wgCfg.ListenPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
if wgCfg.ReplacePeers {
|
||||||
|
sb.WriteString("replace_peers=true\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if wgCfg.FirewallMark != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("fwmark=%d\n", *wgCfg.FirewallMark))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range wgCfg.Peers {
|
||||||
|
hexKey := hex.EncodeToString(p.PublicKey[:])
|
||||||
|
sb.WriteString(fmt.Sprintf("public_key=%s\n", hexKey))
|
||||||
|
|
||||||
|
if p.PresharedKey != nil {
|
||||||
|
preSharedHexKey := hex.EncodeToString(p.PresharedKey[:])
|
||||||
|
sb.WriteString(fmt.Sprintf("preshared_key=%s\n", preSharedHexKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Remove {
|
||||||
|
sb.WriteString("remove=true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.ReplaceAllowedIPs {
|
||||||
|
sb.WriteString("replace_allowed_ips=true\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, aip := range p.AllowedIPs {
|
||||||
|
sb.WriteString(fmt.Sprintf("allowed_ip=%s\n", aip.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Endpoint != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("endpoint=%s\n", p.Endpoint.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.PersistentKeepaliveInterval != nil {
|
||||||
|
sb.WriteString(fmt.Sprintf("persistent_keepalive_interval=%d\n", int(p.PersistentKeepaliveInterval.Seconds())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
@@ -23,6 +23,8 @@ NETBIRD_SIGNAL_PORT=${NETBIRD_SIGNAL_PORT:-10000}
|
|||||||
# Turn
|
# Turn
|
||||||
TURN_DOMAIN=${NETBIRD_TURN_DOMAIN:-$NETBIRD_DOMAIN}
|
TURN_DOMAIN=${NETBIRD_TURN_DOMAIN:-$NETBIRD_DOMAIN}
|
||||||
|
|
||||||
|
NETBIRD_TURN_EXTERNAL_IP=${NETBIRD_TURN_EXTERNAL_IP}
|
||||||
|
|
||||||
# Turn credentials
|
# Turn credentials
|
||||||
# User
|
# User
|
||||||
TURN_USER=self
|
TURN_USER=self
|
||||||
@@ -120,3 +122,4 @@ export NETBIRD_DASHBOARD_TAG
|
|||||||
export NETBIRD_SIGNAL_TAG
|
export NETBIRD_SIGNAL_TAG
|
||||||
export NETBIRD_MANAGEMENT_TAG
|
export NETBIRD_MANAGEMENT_TAG
|
||||||
export COTURN_TAG
|
export COTURN_TAG
|
||||||
|
export NETBIRD_TURN_EXTERNAL_IP
|
||||||
|
|||||||
@@ -54,6 +54,29 @@ if [[ "x-$TURN_PASSWORD" == "x-" ]]; then
|
|||||||
export TURN_PASSWORD=$(openssl rand -base64 32 | sed 's/=//g')
|
export TURN_PASSWORD=$(openssl rand -base64 32 | sed 's/=//g')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
TURN_EXTERNAL_IP_CONFIG="#"
|
||||||
|
|
||||||
|
if [[ "x-$NETBIRD_TURN_EXTERNAL_IP" == "x-" ]]; then
|
||||||
|
echo "discovering server's public IP"
|
||||||
|
IP=$(curl -s -4 https://jsonip.com | jq -r '.ip')
|
||||||
|
if [[ "x-$IP" != "x-" ]]; then
|
||||||
|
TURN_EXTERNAL_IP_CONFIG="external-ip=$IP"
|
||||||
|
else
|
||||||
|
echo "unable to discover server's public IP"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "${NETBIRD_TURN_EXTERNAL_IP}"| egrep '([0-9]{1,3}\.){3}[0-9]{1,3}$' > /dev/null
|
||||||
|
if [[ $? -eq 0 ]]; then
|
||||||
|
echo "using provided server's public IP"
|
||||||
|
TURN_EXTERNAL_IP_CONFIG="external-ip=$NETBIRD_TURN_EXTERNAL_IP"
|
||||||
|
else
|
||||||
|
echo "provided NETBIRD_TURN_EXTERNAL_IP $NETBIRD_TURN_EXTERNAL_IP is invalid, please correct it and try again"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
export TURN_EXTERNAL_IP_CONFIG
|
||||||
|
|
||||||
artifacts_path="./artifacts"
|
artifacts_path="./artifacts"
|
||||||
mkdir -p $artifacts_path
|
mkdir -p $artifacts_path
|
||||||
|
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ delete_auto_service_user() {
|
|||||||
|
|
||||||
init_zitadel() {
|
init_zitadel() {
|
||||||
echo -e "\nInitializing Zitadel with NetBird's applications\n"
|
echo -e "\nInitializing Zitadel with NetBird's applications\n"
|
||||||
INSTANCE_URL="$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT"
|
INSTANCE_URL="$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN"
|
||||||
|
|
||||||
TOKEN_PATH=./machinekey/zitadel-admin-sa.token
|
TOKEN_PATH=./machinekey/zitadel-admin-sa.token
|
||||||
|
|
||||||
@@ -402,6 +402,15 @@ read_nb_domain() {
|
|||||||
echo "$READ_NETBIRD_DOMAIN"
|
echo "$READ_NETBIRD_DOMAIN"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_turn_external_ip() {
|
||||||
|
TURN_EXTERNAL_IP_CONFIG="#external-ip="
|
||||||
|
IP=$(curl -s -4 https://jsonip.com | jq -r '.ip')
|
||||||
|
if [[ "x-$IP" != "x-" ]]; then
|
||||||
|
TURN_EXTERNAL_IP_CONFIG="external-ip=$IP"
|
||||||
|
fi
|
||||||
|
echo "$TURN_EXTERNAL_IP_CONFIG"
|
||||||
|
}
|
||||||
|
|
||||||
initEnvironment() {
|
initEnvironment() {
|
||||||
CADDY_SECURE_DOMAIN=""
|
CADDY_SECURE_DOMAIN=""
|
||||||
ZITADEL_EXTERNALSECURE="false"
|
ZITADEL_EXTERNALSECURE="false"
|
||||||
@@ -413,6 +422,7 @@ initEnvironment() {
|
|||||||
TURN_PASSWORD=$(openssl rand -base64 32 | sed 's/=//g')
|
TURN_PASSWORD=$(openssl rand -base64 32 | sed 's/=//g')
|
||||||
TURN_MIN_PORT=49152
|
TURN_MIN_PORT=49152
|
||||||
TURN_MAX_PORT=65535
|
TURN_MAX_PORT=65535
|
||||||
|
TURN_EXTERNAL_IP_CONFIG=$(get_turn_external_ip)
|
||||||
|
|
||||||
if ! check_nb_domain "$NETBIRD_DOMAIN"; then
|
if ! check_nb_domain "$NETBIRD_DOMAIN"; then
|
||||||
NETBIRD_DOMAIN=$(read_nb_domain)
|
NETBIRD_DOMAIN=$(read_nb_domain)
|
||||||
@@ -472,7 +482,7 @@ initEnvironment() {
|
|||||||
echo -e "\nStarting NetBird services\n"
|
echo -e "\nStarting NetBird services\n"
|
||||||
$DOCKER_COMPOSE_COMMAND up -d
|
$DOCKER_COMPOSE_COMMAND up -d
|
||||||
echo -e "\nDone!\n"
|
echo -e "\nDone!\n"
|
||||||
echo "You can access the NetBird dashboard at $NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT"
|
echo "You can access the NetBird dashboard at $NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN"
|
||||||
echo "Login with the following credentials:"
|
echo "Login with the following credentials:"
|
||||||
echo "Username: $ZITADEL_ADMIN_USERNAME" | tee .env
|
echo "Username: $ZITADEL_ADMIN_USERNAME" | tee .env
|
||||||
echo "Password: $ZITADEL_ADMIN_PASSWORD" | tee -a .env
|
echo "Password: $ZITADEL_ADMIN_PASSWORD" | tee -a .env
|
||||||
@@ -560,6 +570,7 @@ EOF
|
|||||||
renderTurnServerConf() {
|
renderTurnServerConf() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
listening-port=3478
|
listening-port=3478
|
||||||
|
$TURN_EXTERNAL_IP_CONFIG
|
||||||
tls-listening-port=5349
|
tls-listening-port=5349
|
||||||
min-port=$TURN_MIN_PORT
|
min-port=$TURN_MIN_PORT
|
||||||
max-port=$TURN_MAX_PORT
|
max-port=$TURN_MAX_PORT
|
||||||
@@ -608,14 +619,14 @@ renderManagementJson() {
|
|||||||
"IdpManagerConfig": {
|
"IdpManagerConfig": {
|
||||||
"ManagerType": "zitadel",
|
"ManagerType": "zitadel",
|
||||||
"ClientConfig": {
|
"ClientConfig": {
|
||||||
"Issuer": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT",
|
"Issuer": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN",
|
||||||
"TokenEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT/oauth/v2/token",
|
"TokenEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/oauth/v2/token",
|
||||||
"ClientID": "$NETBIRD_IDP_MGMT_CLIENT_ID",
|
"ClientID": "$NETBIRD_IDP_MGMT_CLIENT_ID",
|
||||||
"ClientSecret": "$NETBIRD_IDP_MGMT_CLIENT_SECRET",
|
"ClientSecret": "$NETBIRD_IDP_MGMT_CLIENT_SECRET",
|
||||||
"GrantType": "client_credentials"
|
"GrantType": "client_credentials"
|
||||||
},
|
},
|
||||||
"ExtraConfig": {
|
"ExtraConfig": {
|
||||||
"ManagementEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT/management/v1"
|
"ManagementEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/management/v1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"PKCEAuthorizationFlow": {
|
"PKCEAuthorizationFlow": {
|
||||||
@@ -633,12 +644,12 @@ EOF
|
|||||||
renderDashboardEnv() {
|
renderDashboardEnv() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
# Endpoints
|
# Endpoints
|
||||||
NETBIRD_MGMT_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT
|
NETBIRD_MGMT_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
|
||||||
NETBIRD_MGMT_GRPC_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT
|
NETBIRD_MGMT_GRPC_API_ENDPOINT=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
|
||||||
# OIDC
|
# OIDC
|
||||||
AUTH_AUDIENCE=$NETBIRD_AUTH_CLIENT_ID
|
AUTH_AUDIENCE=$NETBIRD_AUTH_CLIENT_ID
|
||||||
AUTH_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
AUTH_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
||||||
AUTH_AUTHORITY=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN:$NETBIRD_PORT
|
AUTH_AUTHORITY=$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN
|
||||||
USE_AUTH0=false
|
USE_AUTH0=false
|
||||||
AUTH_SUPPORTED_SCOPES="openid profile email offline_access"
|
AUTH_SUPPORTED_SCOPES="openid profile email offline_access"
|
||||||
AUTH_REDIRECT_URI=/nb-auth
|
AUTH_REDIRECT_URI=/nb-auth
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ NETBIRD_DOMAIN=""
|
|||||||
# if not specified it will assume NETBIRD_DOMAIN
|
# if not specified it will assume NETBIRD_DOMAIN
|
||||||
NETBIRD_TURN_DOMAIN=""
|
NETBIRD_TURN_DOMAIN=""
|
||||||
|
|
||||||
|
# TURN server public IP address
|
||||||
|
# required for a connection involving peers in
|
||||||
|
# the same network as the server and external peers
|
||||||
|
# usually matches the IP for the domain set in NETBIRD_TURN_DOMAIN
|
||||||
|
NETBIRD_TURN_EXTERNAL_IP=""
|
||||||
|
|
||||||
# -------------------------------------------
|
# -------------------------------------------
|
||||||
# OIDC
|
# OIDC
|
||||||
# e.g., https://example.eu.auth0.com/.well-known/openid-configuration
|
# e.g., https://example.eu.auth0.com/.well-known/openid-configuration
|
||||||
@@ -65,6 +71,8 @@ NETBIRD_MGMT_IDP="none"
|
|||||||
# Some IDPs requires different client id and client secret for management api
|
# Some IDPs requires different client id and client secret for management api
|
||||||
NETBIRD_IDP_MGMT_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
NETBIRD_IDP_MGMT_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
||||||
NETBIRD_IDP_MGMT_CLIENT_SECRET=""
|
NETBIRD_IDP_MGMT_CLIENT_SECRET=""
|
||||||
|
# Required when setting up with Keycloak "https://<YOUR_KEYCLOAK_HOST_AND_PORT>/admin/realms/netbird"
|
||||||
|
# NETBIRD_IDP_MGMT_EXTRA_ADMIN_ENDPOINT=
|
||||||
# With some IDPs may be needed enabling automatic refresh of signing keys on expire
|
# With some IDPs may be needed enabling automatic refresh of signing keys on expire
|
||||||
# NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=false
|
# NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=false
|
||||||
# NETBIRD_IDP_MGMT_EXTRA_ variables. See https://docs.netbird.io/selfhosted/identity-providers for more information about your IDP of choice.
|
# NETBIRD_IDP_MGMT_EXTRA_ variables. See https://docs.netbird.io/selfhosted/identity-providers for more information about your IDP of choice.
|
||||||
|
|||||||
@@ -25,3 +25,4 @@ NETBIRD_IDP_MGMT_CLIENT_SECRET=$CI_NETBIRD_IDP_MGMT_CLIENT_SECRET
|
|||||||
NETBIRD_SIGNAL_PORT=12345
|
NETBIRD_SIGNAL_PORT=12345
|
||||||
NETBIRD_STORE_CONFIG_ENGINE=$CI_NETBIRD_STORE_CONFIG_ENGINE
|
NETBIRD_STORE_CONFIG_ENGINE=$CI_NETBIRD_STORE_CONFIG_ENGINE
|
||||||
NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=$CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH
|
NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=$CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH
|
||||||
|
NETBIRD_TURN_EXTERNAL_IP=1.2.3.4
|
||||||
@@ -132,6 +132,7 @@ tls-listening-port=5349
|
|||||||
#external-ip=60.70.80.91/172.17.19.101
|
#external-ip=60.70.80.91/172.17.19.101
|
||||||
#external-ip=60.70.80.92/172.17.19.102
|
#external-ip=60.70.80.92/172.17.19.102
|
||||||
|
|
||||||
|
$TURN_EXTERNAL_IP_CONFIG
|
||||||
|
|
||||||
# Number of the relay threads to handle the established connections
|
# Number of the relay threads to handle the established connections
|
||||||
# (in addition to authentication thread and the listener thread).
|
# (in addition to authentication thread and the listener thread).
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net"
|
"net"
|
||||||
@@ -31,8 +32,6 @@ import (
|
|||||||
"github.com/netbirdio/netbird/encryption"
|
"github.com/netbirdio/netbird/encryption"
|
||||||
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity/sqlite"
|
|
||||||
httpapi "github.com/netbirdio/netbird/management/server/http"
|
httpapi "github.com/netbirdio/netbird/management/server/http"
|
||||||
"github.com/netbirdio/netbird/management/server/idp"
|
"github.com/netbirdio/netbird/management/server/idp"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
@@ -146,7 +145,7 @@ var (
|
|||||||
if disableSingleAccMode {
|
if disableSingleAccMode {
|
||||||
mgmtSingleAccModeDomain = ""
|
mgmtSingleAccModeDomain = ""
|
||||||
}
|
}
|
||||||
eventStore, key, err := initEventStore(config.Datadir, config.DataStoreEncryptionKey)
|
eventStore, key, err := integrations.InitEventStore(config.Datadir, config.DataStoreEncryptionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize database: %s", err)
|
return fmt.Errorf("failed to initialize database: %s", err)
|
||||||
}
|
}
|
||||||
@@ -301,20 +300,6 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func initEventStore(dataDir string, key string) (activity.Store, string, error) {
|
|
||||||
var err error
|
|
||||||
if key == "" {
|
|
||||||
log.Debugf("generate new activity store encryption key")
|
|
||||||
key, err = sqlite.GenerateKey()
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
store, err := sqlite.NewSQLiteStore(dataDir, key)
|
|
||||||
return store, key, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func notifyStop(msg string) {
|
func notifyStop(msg string) {
|
||||||
select {
|
select {
|
||||||
case stopCh <- 1:
|
case stopCh <- 1:
|
||||||
|
|||||||
@@ -1086,6 +1086,9 @@ func (am *DefaultAccountManager) DeleteAccount(accountID, userID string) error {
|
|||||||
log.Errorf("failed deleting account %s. error: %s", accountID, err)
|
log.Errorf("failed deleting account %s. error: %s", accountID, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
// cancel peer login expiry job
|
||||||
|
am.peerLoginExpiry.Cancel([]string{account.Id})
|
||||||
|
|
||||||
log.Debugf("account %s deleted", accountID)
|
log.Debugf("account %s deleted", accountID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -1299,6 +1302,22 @@ func (am *DefaultAccountManager) lookupCache(accountUsers map[string]struct{}, a
|
|||||||
return data, err
|
return data, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (am *DefaultAccountManager) removeUserFromCache(accountID, userID string) error {
|
||||||
|
data, err := am.getAccountFromCache(accountID, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, datum := range data {
|
||||||
|
if datum.ID == userID {
|
||||||
|
data = append(data[:i], data[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return am.cacheManager.Set(am.ctx, accountID, data, cacheStore.WithExpiration(cacheEntryExpiration()))
|
||||||
|
}
|
||||||
|
|
||||||
// updateAccountDomainAttributes updates the account domain attributes and then, saves the account
|
// updateAccountDomainAttributes updates the account domain attributes and then, saves the account
|
||||||
func (am *DefaultAccountManager) updateAccountDomainAttributes(account *Account, claims jwtclaims.AuthorizationClaims,
|
func (am *DefaultAccountManager) updateAccountDomainAttributes(account *Account, claims jwtclaims.AuthorizationClaims,
|
||||||
primaryDomain bool,
|
primaryDomain bool,
|
||||||
@@ -1795,6 +1814,7 @@ func newAccountWithId(accountID, userID, domain string) *Account {
|
|||||||
Settings: &Settings{
|
Settings: &Settings{
|
||||||
PeerLoginExpirationEnabled: true,
|
PeerLoginExpirationEnabled: true,
|
||||||
PeerLoginExpiration: DefaultPeerLoginExpiration,
|
PeerLoginExpiration: DefaultPeerLoginExpiration,
|
||||||
|
GroupsPropagationEnabled: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1822,8 +1822,9 @@ paths:
|
|||||||
/api/rules:
|
/api/rules:
|
||||||
get:
|
get:
|
||||||
summary: List all Rules
|
summary: List all Rules
|
||||||
description: Returns a list of all rules
|
description: Returns a list of all rules. This will be deprecated in favour of `/api/policies`.
|
||||||
tags: [ Rules ]
|
tags: [ Rules ]
|
||||||
|
deprecated: true
|
||||||
security:
|
security:
|
||||||
- BearerAuth: [ ]
|
- BearerAuth: [ ]
|
||||||
- TokenAuth: [ ]
|
- TokenAuth: [ ]
|
||||||
@@ -1846,7 +1847,8 @@ paths:
|
|||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
post:
|
post:
|
||||||
summary: Create a Rule
|
summary: Create a Rule
|
||||||
description: Creates a rule
|
description: Creates a rule. This will be deprecated in favour of `/api/policies`.
|
||||||
|
deprecated: true
|
||||||
tags: [ Rules ]
|
tags: [ Rules ]
|
||||||
security:
|
security:
|
||||||
- BearerAuth: [ ]
|
- BearerAuth: [ ]
|
||||||
@@ -1867,7 +1869,8 @@ paths:
|
|||||||
/api/rules/{ruleId}:
|
/api/rules/{ruleId}:
|
||||||
get:
|
get:
|
||||||
summary: Retrieve a Rule
|
summary: Retrieve a Rule
|
||||||
description: Get information about a rules
|
description: Get information about a rules. This will be deprecated in favour of `/api/policies/{policyID}`.
|
||||||
|
deprecated: true
|
||||||
tags: [ Rules ]
|
tags: [ Rules ]
|
||||||
security:
|
security:
|
||||||
- BearerAuth: [ ]
|
- BearerAuth: [ ]
|
||||||
@@ -1896,7 +1899,8 @@ paths:
|
|||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
put:
|
put:
|
||||||
summary: Update a Rule
|
summary: Update a Rule
|
||||||
description: Update/Replace a rule
|
description: Update/Replace a rule. This will be deprecated in favour of `/api/policies/{policyID}`.
|
||||||
|
deprecated: true
|
||||||
tags: [ Rules ]
|
tags: [ Rules ]
|
||||||
security:
|
security:
|
||||||
- BearerAuth: [ ]
|
- BearerAuth: [ ]
|
||||||
@@ -1931,7 +1935,8 @@ paths:
|
|||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
delete:
|
delete:
|
||||||
summary: Delete a Rule
|
summary: Delete a Rule
|
||||||
description: Delete a rule
|
description: Delete a rule. This will be deprecated in favour of `/api/policies/{policyID}`.
|
||||||
|
deprecated: true
|
||||||
tags: [ Rules ]
|
tags: [ Rules ]
|
||||||
security:
|
security:
|
||||||
- BearerAuth: [ ]
|
- BearerAuth: [ ]
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user