Compare commits
14 Commits
fix/enforc
...
test/dns-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65052e5cba | ||
|
|
64084ca130 | ||
|
|
79f60b86c4 | ||
|
|
de46393a7c | ||
|
|
c4c59ed3a7 | ||
|
|
7f958e9338 | ||
|
|
91b45eab98 | ||
|
|
ec8eb76b42 | ||
|
|
8b8e4bbc6a | ||
|
|
e733cdcf33 | ||
|
|
cdbe9c4eef | ||
|
|
8653c32367 | ||
|
|
6743054451 | ||
|
|
7f7e10121d |
1
.gitattributes
vendored
@@ -1 +0,0 @@
|
||||
*.go text eol=lf
|
||||
5
.github/workflows/golang-test-darwin.yml
vendored
@@ -12,9 +12,6 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
store: ['jsonfile', 'sqlite']
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
@@ -36,4 +33,4 @@ jobs:
|
||||
run: go mod tidy
|
||||
|
||||
- name: Test
|
||||
run: NETBIRD_STORE_ENGINE=${{ matrix.store }} go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./...
|
||||
run: go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./...
|
||||
|
||||
18
.github/workflows/golang-test-linux.yml
vendored
@@ -15,7 +15,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: ['386','amd64']
|
||||
store: ['jsonfile', 'sqlite']
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
@@ -42,16 +41,17 @@ jobs:
|
||||
run: go mod tidy
|
||||
|
||||
- name: Test
|
||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./...
|
||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./...
|
||||
|
||||
test_client_on_docker:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.20.x"
|
||||
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev gcc-multilib
|
||||
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
|
||||
|
||||
- name: Install modules
|
||||
run: go mod tidy
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
run: CGO_ENABLED=0 go test -c -o nftablesmanager-testing.bin ./client/firewall/nftables/...
|
||||
|
||||
- name: Generate Engine Test bin
|
||||
run: CGO_ENABLED=1 go test -c -o engine-testing.bin ./client/internal
|
||||
run: CGO_ENABLED=0 go test -c -o engine-testing.bin ./client/internal
|
||||
|
||||
- name: Generate Peer Test bin
|
||||
run: CGO_ENABLED=0 go test -c -o peer-testing.bin ./client/internal/peer/...
|
||||
@@ -95,17 +95,15 @@ jobs:
|
||||
- name: Run Iface tests in docker
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/iface --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/iface-testing.bin -test.timeout 5m -test.parallel 1
|
||||
|
||||
|
||||
- name: Run RouteManager tests in docker
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal/routemanager --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/routemanager-testing.bin -test.timeout 5m -test.parallel 1
|
||||
|
||||
- name: Run nftables Manager tests in docker
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/firewall --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/nftablesmanager-testing.bin -test.timeout 5m -test.parallel 1
|
||||
|
||||
- name: Run Engine tests in docker with file store
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal -e NETBIRD_STORE_ENGINE="jsonfile" --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/engine-testing.bin -test.timeout 5m -test.parallel 1
|
||||
|
||||
- name: Run Engine tests in docker with sqlite store
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal -e NETBIRD_STORE_ENGINE="sqlite" --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/engine-testing.bin -test.timeout 5m -test.parallel 1
|
||||
- name: Run Engine tests in docker
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/engine-testing.bin -test.timeout 5m -test.parallel 1
|
||||
|
||||
- name: Run Peer tests in docker
|
||||
run: docker run -t --cap-add=NET_ADMIN --privileged --rm -v $PWD:/ci -w /ci/client/internal/peer --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/peer-testing.bin -test.timeout 5m -test.parallel 1
|
||||
4
.github/workflows/golang-test-windows.yml
vendored
@@ -39,9 +39,7 @@ jobs:
|
||||
|
||||
- run: mv ${{ env.downloadPath }}/wintun/bin/amd64/wintun.dll 'C:\Windows\System32\'
|
||||
|
||||
- run: choco install -y sysinternals --ignore-checksums
|
||||
- run: choco install -y mingw
|
||||
|
||||
- run: choco install -y sysinternals
|
||||
- run: PsExec64 -s -w ${{ github.workspace }} C:\hostedtoolcache\windows\go\${{ steps.go.outputs.go-version }}\x64\bin\go.exe env -w GOMODCACHE=C:\Users\runneradmin\go\pkg\mod
|
||||
- run: PsExec64 -s -w ${{ github.workspace }} C:\hostedtoolcache\windows\go\${{ steps.go.outputs.go-version }}\x64\bin\go.exe env -w GOCACHE=C:\Users\runneradmin\AppData\Local\go-build
|
||||
|
||||
|
||||
20
.github/workflows/golangci-lint.yml
vendored
@@ -1,23 +1,12 @@
|
||||
name: golangci-lint
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
name: lint
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
@@ -25,12 +14,7 @@ jobs:
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.20.x"
|
||||
cache: false
|
||||
- name: Install dependencies
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout=12m
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
1
.github/workflows/release.yml
vendored
@@ -17,7 +17,6 @@ on:
|
||||
- 'release_files/**'
|
||||
- '**/Dockerfile'
|
||||
- '**/Dockerfile.*'
|
||||
- 'client/ui/**'
|
||||
|
||||
env:
|
||||
SIGN_PIPE_VER: "v0.0.9"
|
||||
|
||||
23
.github/workflows/test-infrastructure-files.yml
vendored
@@ -8,8 +8,6 @@ on:
|
||||
paths:
|
||||
- 'infrastructure_files/**'
|
||||
- '.github/workflows/test-infrastructure-files.yml'
|
||||
- 'management/cmd/**'
|
||||
- 'signal/cmd/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }}
|
||||
@@ -58,8 +56,6 @@ jobs:
|
||||
CI_NETBIRD_IDP_MGMT_CLIENT_ID: testing.client.id
|
||||
CI_NETBIRD_IDP_MGMT_CLIENT_SECRET: testing.client.secret
|
||||
CI_NETBIRD_AUTH_SUPPORTED_SCOPES: "openid profile email offline_access api email_verified"
|
||||
CI_NETBIRD_STORE_CONFIG_ENGINE: "sqlite"
|
||||
CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false
|
||||
|
||||
- name: check values
|
||||
working-directory: infrastructure_files
|
||||
@@ -85,8 +81,6 @@ jobs:
|
||||
CI_NETBIRD_IDP_MGMT_CLIENT_ID: testing.client.id
|
||||
CI_NETBIRD_IDP_MGMT_CLIENT_SECRET: testing.client.secret
|
||||
CI_NETBIRD_SIGNAL_PORT: 12345
|
||||
CI_NETBIRD_STORE_CONFIG_ENGINE: "sqlite"
|
||||
CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false
|
||||
|
||||
run: |
|
||||
grep AUTH_CLIENT_ID docker-compose.yml | grep $CI_NETBIRD_AUTH_CLIENT_ID
|
||||
@@ -103,9 +97,7 @@ jobs:
|
||||
grep NETBIRD_TOKEN_SOURCE docker-compose.yml | grep $CI_NETBIRD_TOKEN_SOURCE
|
||||
grep AuthUserIDClaim management.json | grep $CI_NETBIRD_AUTH_USER_ID_CLAIM
|
||||
grep -A 3 DeviceAuthorizationFlow management.json | grep -A 1 ProviderConfig | grep Audience | grep $CI_NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE
|
||||
grep -A 3 DeviceAuthorizationFlow management.json | grep -A 1 ProviderConfig | grep Audience | grep $CI_NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE
|
||||
grep Engine management.json | grep "$CI_NETBIRD_STORE_CONFIG_ENGINE"
|
||||
grep IdpSignKeyRefreshEnabled management.json | grep "$CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH"
|
||||
grep -A 8 DeviceAuthorizationFlow management.json | grep -A 6 ProviderConfig | grep Scope | grep "$CI_NETBIRD_AUTH_DEVICE_AUTH_SCOPE"
|
||||
grep UseIDToken management.json | grep false
|
||||
grep -A 1 IdpManagerConfig management.json | grep ManagerType | grep $CI_NETBIRD_MGMT_IDP
|
||||
grep -A 3 IdpManagerConfig management.json | grep -A 1 ClientConfig | grep Issuer | grep $CI_NETBIRD_AUTH_AUTHORITY
|
||||
@@ -113,13 +105,12 @@ jobs:
|
||||
grep -A 5 IdpManagerConfig management.json | grep -A 3 ClientConfig | grep ClientID | grep $CI_NETBIRD_IDP_MGMT_CLIENT_ID
|
||||
grep -A 6 IdpManagerConfig management.json | grep -A 4 ClientConfig | grep ClientSecret | grep $CI_NETBIRD_IDP_MGMT_CLIENT_SECRET
|
||||
grep -A 7 IdpManagerConfig management.json | grep -A 5 ClientConfig | grep GrantType | grep client_credentials
|
||||
grep -A 10 PKCEAuthorizationFlow management.json | grep -A 10 ProviderConfig | grep Audience | grep $CI_NETBIRD_AUTH_AUDIENCE
|
||||
grep -A 10 PKCEAuthorizationFlow management.json | grep -A 10 ProviderConfig | grep ClientID | grep $CI_NETBIRD_AUTH_CLIENT_ID
|
||||
grep -A 10 PKCEAuthorizationFlow management.json | grep -A 10 ProviderConfig | grep ClientSecret | grep $CI_NETBIRD_AUTH_CLIENT_SECRET
|
||||
grep -A 10 PKCEAuthorizationFlow management.json | grep -A 10 ProviderConfig | grep AuthorizationEndpoint | grep $CI_NETBIRD_AUTH_PKCE_AUTHORIZATION_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 -A 3 RedirectURLs | grep "http://localhost:53000"
|
||||
grep -A 2 PKCEAuthorizationFlow management.json | grep -A 1 ProviderConfig | grep Audience | grep $CI_NETBIRD_AUTH_AUDIENCE
|
||||
grep -A 3 PKCEAuthorizationFlow management.json | grep -A 2 ProviderConfig | grep ClientID | grep $CI_NETBIRD_AUTH_CLIENT_ID
|
||||
grep -A 4 PKCEAuthorizationFlow management.json | grep -A 3 ProviderConfig | grep ClientSecret | grep $CI_NETBIRD_AUTH_CLIENT_SECRET
|
||||
grep -A 5 PKCEAuthorizationFlow management.json | grep -A 4 ProviderConfig | grep AuthorizationEndpoint | grep $CI_NETBIRD_AUTH_PKCE_AUTHORIZATION_ENDPOINT
|
||||
grep -A 6 PKCEAuthorizationFlow management.json | grep -A 5 ProviderConfig | grep TokenEndpoint | grep $CI_NETBIRD_AUTH_TOKEN_ENDPOINT
|
||||
grep -A 7 PKCEAuthorizationFlow management.json | grep -A 6 ProviderConfig | grep Scope | grep "$CI_NETBIRD_AUTH_SUPPORTED_SCOPES"
|
||||
|
||||
- name: Install modules
|
||||
run: go mod tidy
|
||||
|
||||
3
.gitignore
vendored
@@ -19,5 +19,4 @@ client/.distfiles/
|
||||
infrastructure_files/setup.env
|
||||
infrastructure_files/setup-*.env
|
||||
.vscode
|
||||
.DS_Store
|
||||
*.db
|
||||
.DS_Store
|
||||
@@ -54,7 +54,7 @@ nfpms:
|
||||
contents:
|
||||
- src: client/ui/netbird.desktop
|
||||
dst: /usr/share/applications/netbird.desktop
|
||||
- src: client/ui/netbird-systemtray-default.png
|
||||
- src: client/ui/disconnected.png
|
||||
dst: /usr/share/pixmaps/netbird.png
|
||||
dependencies:
|
||||
- netbird
|
||||
@@ -71,7 +71,7 @@ nfpms:
|
||||
contents:
|
||||
- src: client/ui/netbird.desktop
|
||||
dst: /usr/share/applications/netbird.desktop
|
||||
- src: client/ui/netbird-systemtray-default.png
|
||||
- src: client/ui/disconnected.png
|
||||
dst: /usr/share/pixmaps/netbird.png
|
||||
dependencies:
|
||||
- netbird
|
||||
@@ -91,4 +91,4 @@ uploads:
|
||||
mode: archive
|
||||
target: https://pkgs.wiretrustee.com/yum/{{ .Arch }}{{ if .Arm }}{{ .Arm }}{{ end }}
|
||||
username: dev@wiretrustee.com
|
||||
method: PUT
|
||||
method: PUT
|
||||
@@ -23,6 +23,7 @@ If you haven't already, join our slack workspace [here](https://join.slack.com/t
|
||||
- [Test suite](#test-suite)
|
||||
- [Checklist before submitting a PR](#checklist-before-submitting-a-pr)
|
||||
- [Other project repositories](#other-project-repositories)
|
||||
- [Checklist before submitting a new node](#checklist-before-submitting-a-new-node)
|
||||
- [Contributor License Agreement](#contributor-license-agreement)
|
||||
|
||||
## Code of conduct
|
||||
@@ -69,7 +70,7 @@ dependencies are installed. Here is a short guide on how that can be done.
|
||||
|
||||
### Requirements
|
||||
|
||||
#### Go 1.21
|
||||
#### Go 1.19
|
||||
|
||||
Follow the installation guide from https://go.dev/
|
||||
|
||||
@@ -138,14 +139,15 @@ checked out and set up:
|
||||
### Build and start
|
||||
#### Client
|
||||
|
||||
> Windows clients have a Wireguard driver requirement. We provide a bash script that can be executed in WLS 2 with docker support [wireguard_nt.sh](/client/wireguard_nt.sh).
|
||||
|
||||
To start NetBird, execute:
|
||||
```
|
||||
cd client
|
||||
CGO_ENABLED=0 go build .
|
||||
# bash wireguard_nt.sh # if windows
|
||||
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`.
|
||||
|
||||
To start NetBird the client in the foreground:
|
||||
|
||||
```
|
||||
@@ -213,4 +215,4 @@ NetBird project is composed of 3 main repositories:
|
||||
|
||||
That we do not have any potential problems later it is sadly necessary to sign a [Contributor License Agreement](CONTRIBUTOR_LICENSE_AGREEMENT.md). That can be done literally with the push of a button.
|
||||
|
||||
A bot will automatically comment on the pull request once it got opened asking for the agreement to be signed. Before it did not get signed it is sadly not possible to merge it in.
|
||||
A bot will automatically comment on the pull request once it got opened asking for the agreement to be signed. Before it did not get signed it is sadly not possible to merge it in.
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/dns"
|
||||
"github.com/netbirdio/netbird/client/internal/listener"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||
"github.com/netbirdio/netbird/client/system"
|
||||
"github.com/netbirdio/netbird/formatter"
|
||||
@@ -31,9 +31,9 @@ type IFaceDiscover interface {
|
||||
stdnet.ExternalIFaceDiscover
|
||||
}
|
||||
|
||||
// NetworkChangeListener export internal NetworkChangeListener for mobile
|
||||
type NetworkChangeListener interface {
|
||||
listener.NetworkChangeListener
|
||||
// RouteListener export internal RouteListener for mobile
|
||||
type RouteListener interface {
|
||||
routemanager.RouteListener
|
||||
}
|
||||
|
||||
// DnsReadyListener export internal dns ReadyListener for mobile
|
||||
@@ -47,26 +47,26 @@ func init() {
|
||||
|
||||
// Client struct manage the life circle of background service
|
||||
type Client struct {
|
||||
cfgFile string
|
||||
tunAdapter iface.TunAdapter
|
||||
iFaceDiscover IFaceDiscover
|
||||
recorder *peer.Status
|
||||
ctxCancel context.CancelFunc
|
||||
ctxCancelLock *sync.Mutex
|
||||
deviceName string
|
||||
networkChangeListener listener.NetworkChangeListener
|
||||
cfgFile string
|
||||
tunAdapter iface.TunAdapter
|
||||
iFaceDiscover IFaceDiscover
|
||||
recorder *peer.Status
|
||||
ctxCancel context.CancelFunc
|
||||
ctxCancelLock *sync.Mutex
|
||||
deviceName string
|
||||
routeListener routemanager.RouteListener
|
||||
}
|
||||
|
||||
// NewClient instantiate a new Client
|
||||
func NewClient(cfgFile, deviceName string, tunAdapter TunAdapter, iFaceDiscover IFaceDiscover, networkChangeListener NetworkChangeListener) *Client {
|
||||
func NewClient(cfgFile, deviceName string, tunAdapter TunAdapter, iFaceDiscover IFaceDiscover, routeListener RouteListener) *Client {
|
||||
return &Client{
|
||||
cfgFile: cfgFile,
|
||||
deviceName: deviceName,
|
||||
tunAdapter: tunAdapter,
|
||||
iFaceDiscover: iFaceDiscover,
|
||||
recorder: peer.NewRecorder(""),
|
||||
ctxCancelLock: &sync.Mutex{},
|
||||
networkChangeListener: networkChangeListener,
|
||||
cfgFile: cfgFile,
|
||||
deviceName: deviceName,
|
||||
tunAdapter: tunAdapter,
|
||||
iFaceDiscover: iFaceDiscover,
|
||||
recorder: peer.NewRecorder(""),
|
||||
ctxCancelLock: &sync.Mutex{},
|
||||
routeListener: routeListener,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsRead
|
||||
|
||||
// todo do not throw error in case of cancelled context
|
||||
ctx = internal.CtxInitState(ctx)
|
||||
return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener)
|
||||
return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.routeListener, dns.items, dnsReadyListener)
|
||||
}
|
||||
|
||||
// RunWithoutLogin we apply this type of run function when the backed has been started without UI (i.e. after reboot).
|
||||
@@ -120,7 +120,7 @@ func (c *Client) RunWithoutLogin(dns *DNSList, dnsReadyListener DnsReadyListener
|
||||
|
||||
// todo do not throw error in case of cancelled context
|
||||
ctx = internal.CtxInitState(ctx)
|
||||
return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener)
|
||||
return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.routeListener, dns.items, dnsReadyListener)
|
||||
}
|
||||
|
||||
// Stop the internal client and free the resources
|
||||
|
||||
@@ -57,11 +57,11 @@ func TestPreferences_ReadUncommitedValues(t *testing.T) {
|
||||
p.SetManagementURL(exampleString)
|
||||
resp, err = p.GetManagementURL()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read management url: %s", err)
|
||||
t.Fatalf("failed to read managmenet url: %s", err)
|
||||
}
|
||||
|
||||
if resp != exampleString {
|
||||
t.Errorf("unexpected management url: %s", resp)
|
||||
t.Errorf("unexpected managemenet url: %s", resp)
|
||||
}
|
||||
|
||||
p.SetPreSharedKey(exampleString)
|
||||
@@ -102,11 +102,11 @@ func TestPreferences_Commit(t *testing.T) {
|
||||
|
||||
resp, err = p.GetManagementURL()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read management url: %s", err)
|
||||
t.Fatalf("failed to read managmenet url: %s", err)
|
||||
}
|
||||
|
||||
if resp != exampleURL {
|
||||
t.Errorf("unexpected management url: %s", resp)
|
||||
t.Errorf("unexpected managemenet url: %s", resp)
|
||||
}
|
||||
|
||||
resp, err = p.GetPreSharedKey()
|
||||
|
||||
@@ -65,7 +65,7 @@ func startManagement(t *testing.T, config *mgmt.Config) (*grpc.Server, net.Liste
|
||||
t.Fatal(err)
|
||||
}
|
||||
s := grpc.NewServer()
|
||||
store, err := mgmt.NewStoreFromJson(config.Datadir, nil)
|
||||
store, err := mgmt.NewFileStore(config.Datadir, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
||||
defer func() {
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
log.Warnf("failed closing daemon gRPC client connection %v", err)
|
||||
log.Warnf("failed closing dameon gRPC client connection %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
@@ -200,11 +200,11 @@ func validateNATExternalIPs(list []string) error {
|
||||
|
||||
subElements := strings.Split(element, "/")
|
||||
if len(subElements) > 2 {
|
||||
return fmt.Errorf("%s is not a valid input for %s. it should be formatted as \"String\" or \"String/String\"", element, externalIPMapFlag)
|
||||
return fmt.Errorf("%s is not a valid input for %s. it should be formated as \"String\" or \"String/String\"", element, externalIPMapFlag)
|
||||
}
|
||||
|
||||
if len(subElements) == 1 && !isValidIP(subElements[0]) {
|
||||
return fmt.Errorf("%s is not a valid input for %s. it should be formatted as \"IP\" or \"IP/IP\", or \"IP/Interface Name\"", element, externalIPMapFlag)
|
||||
return fmt.Errorf("%s is not a valid input for %s. it should be formated as \"IP\" or \"IP/IP\", or \"IP/Interface Name\"", element, externalIPMapFlag)
|
||||
}
|
||||
|
||||
last := 0
|
||||
@@ -259,7 +259,7 @@ func parseCustomDNSAddress(modified bool) ([]byte, error) {
|
||||
var parsed []byte
|
||||
if modified {
|
||||
if !isValidAddrPort(customDNSAddress) {
|
||||
return nil, fmt.Errorf("%s is invalid, it should be formatted as IP:Port string or as an empty string like \"\"", customDNSAddress)
|
||||
return nil, fmt.Errorf("%s is invalid, it should be formated as IP:Port string or as an empty string like \"\"", customDNSAddress)
|
||||
}
|
||||
if customDNSAddress == "" && logFile != "console" {
|
||||
parsed = []byte("empty")
|
||||
|
||||
@@ -47,7 +47,16 @@ type Manager interface {
|
||||
//
|
||||
// If comment argument is empty firewall manager should set
|
||||
// rule ID as comment for the rule
|
||||
AddFiltering(ruleRequest RuleRequest) ([]Rule, error)
|
||||
AddFiltering(
|
||||
ip net.IP,
|
||||
proto Protocol,
|
||||
sPort *Port,
|
||||
dPort *Port,
|
||||
direction RuleDirection,
|
||||
action Action,
|
||||
ipsetName string,
|
||||
comment string,
|
||||
) (Rule, error)
|
||||
|
||||
// DeleteRule from the firewall by rule definition
|
||||
DeleteRule(rule Rule) error
|
||||
@@ -60,23 +69,3 @@ type Manager interface {
|
||||
|
||||
// TODO: migrate routemanager firewal actions to this interface
|
||||
}
|
||||
|
||||
// RuleRequest is the request to create a rule
|
||||
type RuleRequest struct {
|
||||
// IP is the IP address of the rule
|
||||
IP net.IP
|
||||
// Proto is the protocol of the rule
|
||||
Proto Protocol
|
||||
// SrcPort is the source port of the rule
|
||||
SrcPort *Port
|
||||
// DstPort is the destination port of the rule
|
||||
DstPort *Port
|
||||
// Direction is the direction of the rule
|
||||
Direction RuleDirection
|
||||
// Action is the action of the rule
|
||||
Action Action
|
||||
// IPSetName is the name of the IPSet
|
||||
IPSetName string
|
||||
// Comment is the comment of the rule
|
||||
Comment string
|
||||
}
|
||||
|
||||
@@ -94,24 +94,33 @@ func Create(wgIface iFaceMapper, ipv6Supported bool) (*Manager, error) {
|
||||
// AddFiltering rule to the firewall
|
||||
//
|
||||
// Comment will be ignored because some system this feature is not supported
|
||||
func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
func (m *Manager) AddFiltering(
|
||||
ip net.IP,
|
||||
protocol fw.Protocol,
|
||||
sPort *fw.Port,
|
||||
dPort *fw.Port,
|
||||
direction fw.RuleDirection,
|
||||
action fw.Action,
|
||||
ipsetName string,
|
||||
comment string,
|
||||
) (fw.Rule, error) {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
client, err := m.client(request.IP)
|
||||
client, err := m.client(ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dPortVal, sPortVal string
|
||||
if request.DstPort != nil && request.DstPort.Values != nil {
|
||||
if dPort != nil && dPort.Values != nil {
|
||||
// TODO: we support only one port per rule in current implementation of ACLs
|
||||
dPortVal = strconv.Itoa(request.DstPort.Values[0])
|
||||
dPortVal = strconv.Itoa(dPort.Values[0])
|
||||
}
|
||||
if request.SrcPort != nil && request.SrcPort.Values != nil {
|
||||
sPortVal = strconv.Itoa(request.SrcPort.Values[0])
|
||||
if sPort != nil && sPort.Values != nil {
|
||||
sPortVal = strconv.Itoa(sPort.Values[0])
|
||||
}
|
||||
ipsetName := m.transformIPsetName(request.IPSetName, sPortVal, dPortVal)
|
||||
ipsetName = m.transformIPsetName(ipsetName, sPortVal, dPortVal)
|
||||
|
||||
ruleID := uuid.New().String()
|
||||
|
||||
@@ -126,28 +135,28 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if err := ipset.Add(ipsetName, request.IP.String()); err != nil {
|
||||
if err := ipset.Add(ipsetName, ip.String()); err != nil {
|
||||
return nil, fmt.Errorf("failed to add IP to ipset: %w", err)
|
||||
}
|
||||
|
||||
if rsExists {
|
||||
// if ruleset already exists it means we already have the firewall rule
|
||||
// so we need to update IPs in the ruleset and return new fw.Rule object for ACL manager.
|
||||
rs.ips[request.IP.String()] = ruleID
|
||||
return []fw.Rule{&Rule{
|
||||
rs.ips[ip.String()] = ruleID
|
||||
return &Rule{
|
||||
ruleID: ruleID,
|
||||
ipsetName: ipsetName,
|
||||
ip: request.IP.String(),
|
||||
dst: request.Direction == fw.RuleDirectionOUT,
|
||||
v6: request.IP.To4() == nil,
|
||||
}}, nil
|
||||
ip: ip.String(),
|
||||
dst: direction == fw.RuleDirectionOUT,
|
||||
v6: ip.To4() == nil,
|
||||
}, nil
|
||||
}
|
||||
// this is new ipset so we need to create firewall rule for it
|
||||
}
|
||||
|
||||
specs := m.filterRuleSpecs(request.IP, string(request.Proto), sPortVal, dPortVal, request.Direction, request.Action, ipsetName)
|
||||
specs := m.filterRuleSpecs(ip, string(protocol), sPortVal, dPortVal, direction, action, ipsetName)
|
||||
|
||||
if request.Direction == fw.RuleDirectionOUT {
|
||||
if direction == fw.RuleDirectionOUT {
|
||||
ok, err := client.Exists("filter", ChainOutputFilterName, specs...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check is output rule already exists: %w", err)
|
||||
@@ -177,20 +186,20 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
ruleID: ruleID,
|
||||
specs: specs,
|
||||
ipsetName: ipsetName,
|
||||
ip: request.IP.String(),
|
||||
dst: request.Direction == fw.RuleDirectionOUT,
|
||||
v6: request.IP.To4() == nil,
|
||||
ip: ip.String(),
|
||||
dst: direction == fw.RuleDirectionOUT,
|
||||
v6: ip.To4() == nil,
|
||||
}
|
||||
if ipsetName != "" {
|
||||
// ipset name is defined and it means that this rule was created
|
||||
// for it, need to associate it with ruleset
|
||||
// for it, need to assosiate it with ruleset
|
||||
m.rulesets[ipsetName] = ruleset{
|
||||
rule: rule,
|
||||
ips: map[string]string{rule.ip: ruleID},
|
||||
}
|
||||
}
|
||||
|
||||
return []fw.Rule{rule}, nil
|
||||
return rule, nil
|
||||
}
|
||||
|
||||
// DeleteRule from the firewall by rule definition
|
||||
@@ -227,7 +236,7 @@ func (m *Manager) DeleteRule(rule fw.Rule) error {
|
||||
}
|
||||
|
||||
// we delete last IP from the set, that means we need to delete
|
||||
// set itself and associated firewall rule too
|
||||
// set itself and assosiated firewall rule too
|
||||
delete(m.rulesets, r.ipsetName)
|
||||
|
||||
if err := ipset.Destroy(r.ipsetName); err != nil {
|
||||
@@ -263,31 +272,27 @@ func (m *Manager) Reset() error {
|
||||
func (m *Manager) AllowNetbird() error {
|
||||
if m.wgIface.IsUserspaceBind() {
|
||||
_, err := m.AddFiltering(
|
||||
fw.RuleRequest{
|
||||
IP: net.ParseIP("0.0.0.0"),
|
||||
Proto: "all",
|
||||
SrcPort: nil,
|
||||
DstPort: nil,
|
||||
Direction: fw.RuleDirectionIN,
|
||||
Action: fw.ActionAccept,
|
||||
IPSetName: "",
|
||||
Comment: "",
|
||||
},
|
||||
net.ParseIP("0.0.0.0"),
|
||||
"all",
|
||||
nil,
|
||||
nil,
|
||||
fw.RuleDirectionIN,
|
||||
fw.ActionAccept,
|
||||
"",
|
||||
"",
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to allow netbird interface traffic: %w", err)
|
||||
}
|
||||
_, err = m.AddFiltering(
|
||||
fw.RuleRequest{
|
||||
IP: net.ParseIP("0.0.0.0"),
|
||||
Proto: "all",
|
||||
SrcPort: nil,
|
||||
DstPort: nil,
|
||||
Direction: fw.RuleDirectionOUT,
|
||||
Action: fw.ActionAccept,
|
||||
IPSetName: "",
|
||||
Comment: "",
|
||||
},
|
||||
net.ParseIP("0.0.0.0"),
|
||||
"all",
|
||||
nil,
|
||||
nil,
|
||||
fw.RuleDirectionOUT,
|
||||
fw.ActionAccept,
|
||||
"",
|
||||
"",
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -96,7 +96,16 @@ func Create(wgIface iFaceMapper) (*Manager, error) {
|
||||
//
|
||||
// If comment argument is empty firewall manager should set
|
||||
// rule ID as comment for the rule
|
||||
func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
func (m *Manager) AddFiltering(
|
||||
ip net.IP,
|
||||
proto fw.Protocol,
|
||||
sPort *fw.Port,
|
||||
dPort *fw.Port,
|
||||
direction fw.RuleDirection,
|
||||
action fw.Action,
|
||||
ipsetName string,
|
||||
comment string,
|
||||
) (fw.Rule, error) {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
@@ -107,16 +116,16 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
chain *nftables.Chain
|
||||
)
|
||||
|
||||
if request.Direction == fw.RuleDirectionOUT {
|
||||
if direction == fw.RuleDirectionOUT {
|
||||
table, chain, err = m.chain(
|
||||
request.IP,
|
||||
ip,
|
||||
FilterOutputChainName,
|
||||
nftables.ChainHookOutput,
|
||||
nftables.ChainPriorityFilter,
|
||||
nftables.ChainTypeFilter)
|
||||
} else {
|
||||
table, chain, err = m.chain(
|
||||
request.IP,
|
||||
ip,
|
||||
FilterInputChainName,
|
||||
nftables.ChainHookInput,
|
||||
nftables.ChainPriorityFilter,
|
||||
@@ -126,22 +135,22 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawIP := request.IP.To4()
|
||||
rawIP := ip.To4()
|
||||
if rawIP == nil {
|
||||
rawIP = request.IP.To16()
|
||||
rawIP = ip.To16()
|
||||
}
|
||||
|
||||
rulesetID := m.getRulesetID(request.IP, request.Proto, request.SrcPort, request.DstPort, request.Direction, request.Action, request.IPSetName)
|
||||
rulesetID := m.getRulesetID(ip, proto, sPort, dPort, direction, action, ipsetName)
|
||||
|
||||
if request.IPSetName != "" {
|
||||
if ipsetName != "" {
|
||||
// if we already have set with given name, just add ip to the set
|
||||
// and return rule with new ID in other case let's create rule
|
||||
// with fresh created set and set element
|
||||
|
||||
var isSetNew bool
|
||||
ipset, err = m.rConn.GetSetByName(table, request.IPSetName)
|
||||
ipset, err = m.rConn.GetSetByName(table, ipsetName)
|
||||
if err != nil {
|
||||
if ipset, err = m.createSet(table, rawIP, request.IPSetName); err != nil {
|
||||
if ipset, err = m.createSet(table, rawIP, ipsetName); err != nil {
|
||||
return nil, fmt.Errorf("get set name: %v", err)
|
||||
}
|
||||
isSetNew = true
|
||||
@@ -159,11 +168,7 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
// just add new rule to the ruleset and return new fw.Rule object
|
||||
|
||||
if ruleset, ok := m.rulesetManager.getRuleset(rulesetID); ok {
|
||||
r, err := m.rulesetManager.addRule(ruleset, rawIP)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("add rule: %v", err)
|
||||
}
|
||||
return []fw.Rule{r}, nil
|
||||
return m.rulesetManager.addRule(ruleset, rawIP)
|
||||
}
|
||||
// if ipset exists but it is not linked to rule for given direction
|
||||
// create new rule for direction and bind ipset to it later
|
||||
@@ -171,7 +176,7 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
}
|
||||
|
||||
ifaceKey := expr.MetaKeyIIFNAME
|
||||
if request.Direction == fw.RuleDirectionOUT {
|
||||
if direction == fw.RuleDirectionOUT {
|
||||
ifaceKey = expr.MetaKeyOIFNAME
|
||||
}
|
||||
expressions := []expr.Any{
|
||||
@@ -183,7 +188,7 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
},
|
||||
}
|
||||
|
||||
if request.Proto != "all" {
|
||||
if proto != "all" {
|
||||
expressions = append(expressions, &expr.Payload{
|
||||
DestRegister: 1,
|
||||
Base: expr.PayloadBaseNetworkHeader,
|
||||
@@ -192,7 +197,7 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
})
|
||||
|
||||
var protoData []byte
|
||||
switch request.Proto {
|
||||
switch proto {
|
||||
case fw.ProtocolTCP:
|
||||
protoData = []byte{unix.IPPROTO_TCP}
|
||||
case fw.ProtocolUDP:
|
||||
@@ -200,7 +205,7 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
case fw.ProtocolICMP:
|
||||
protoData = []byte{unix.IPPROTO_ICMP}
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported protocol: %s", request.Proto)
|
||||
return nil, fmt.Errorf("unsupported protocol: %s", proto)
|
||||
}
|
||||
expressions = append(expressions, &expr.Cmp{
|
||||
Register: 1,
|
||||
@@ -220,7 +225,7 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
}
|
||||
|
||||
// change to destination address position if need
|
||||
if request.Direction == fw.RuleDirectionOUT {
|
||||
if direction == fw.RuleDirectionOUT {
|
||||
addrOffset += addrLen
|
||||
}
|
||||
|
||||
@@ -245,14 +250,14 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
expressions = append(expressions,
|
||||
&expr.Lookup{
|
||||
SourceRegister: 1,
|
||||
SetName: request.IPSetName,
|
||||
SetName: ipsetName,
|
||||
SetID: ipset.ID,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if request.SrcPort != nil && len(request.SrcPort.Values) != 0 {
|
||||
if sPort != nil && len(sPort.Values) != 0 {
|
||||
expressions = append(expressions,
|
||||
&expr.Payload{
|
||||
DestRegister: 1,
|
||||
@@ -263,12 +268,12 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: encodePort(*request.SrcPort),
|
||||
Data: encodePort(*sPort),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if request.DstPort != nil && len(request.DstPort.Values) != 0 {
|
||||
if dPort != nil && len(dPort.Values) != 0 {
|
||||
expressions = append(expressions,
|
||||
&expr.Payload{
|
||||
DestRegister: 1,
|
||||
@@ -279,18 +284,18 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
&expr.Cmp{
|
||||
Op: expr.CmpOpEq,
|
||||
Register: 1,
|
||||
Data: encodePort(*request.DstPort),
|
||||
Data: encodePort(*dPort),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if request.Action == fw.ActionAccept {
|
||||
if action == fw.ActionAccept {
|
||||
expressions = append(expressions, &expr.Verdict{Kind: expr.VerdictAccept})
|
||||
} else {
|
||||
expressions = append(expressions, &expr.Verdict{Kind: expr.VerdictDrop})
|
||||
}
|
||||
|
||||
userData := []byte(strings.Join([]string{rulesetID, request.Comment}, " "))
|
||||
userData := []byte(strings.Join([]string{rulesetID, comment}, " "))
|
||||
|
||||
rule := m.rConn.InsertRule(&nftables.Rule{
|
||||
Table: table,
|
||||
@@ -304,11 +309,7 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
}
|
||||
|
||||
ruleset := m.rulesetManager.createRuleset(rulesetID, rule, ipset)
|
||||
r, err := m.rulesetManager.addRule(ruleset, rawIP)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("add rule: %v", err)
|
||||
}
|
||||
return []fw.Rule{r}, nil
|
||||
return m.rulesetManager.addRule(ruleset, rawIP)
|
||||
}
|
||||
|
||||
// getRulesetID returns ruleset ID based on given parameters
|
||||
@@ -753,7 +754,7 @@ func (m *Manager) AllowNetbird() error {
|
||||
}
|
||||
|
||||
if chain == nil {
|
||||
log.Debugf("chain INPUT not found. Skipping add allow netbird rule")
|
||||
log.Debugf("chain INPUT not found. Skiping add allow netbird rule")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ func TestNftablesManager(t *testing.T) {
|
||||
// test expectations:
|
||||
// 1) "accept extra routed traffic rule" for the interface
|
||||
// 2) "drop all rule" for the interface
|
||||
require.Len(t, rules, 2, "expected 2 rules after deletion")
|
||||
require.Len(t, rules, 2, "expected 2 rules after deleteion")
|
||||
|
||||
err = manager.Reset()
|
||||
require.NoError(t, err, "failed to reset")
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
package uspfilter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type action string
|
||||
|
||||
const (
|
||||
addRule action = "add"
|
||||
deleteRule action = "delete"
|
||||
firewallRuleName = "Netbird"
|
||||
addRule action = "add"
|
||||
deleteRule action = "delete"
|
||||
|
||||
firewallRuleName = "Netbird"
|
||||
noRulesMatchCriteria = "No rules match the specified criteria"
|
||||
)
|
||||
|
||||
// Reset firewall to the default state
|
||||
@@ -24,14 +26,6 @@ func (m *Manager) Reset() error {
|
||||
m.outgoingRules = make(map[string]RuleSet)
|
||||
m.incomingRules = make(map[string]RuleSet)
|
||||
|
||||
if !isWindowsFirewallReachable() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !isFirewallRuleActive(firewallRuleName) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := manageFirewallRule(firewallRuleName, deleteRule); err != nil {
|
||||
return fmt.Errorf("couldn't remove windows firewall: %w", err)
|
||||
}
|
||||
@@ -41,13 +35,6 @@ func (m *Manager) Reset() error {
|
||||
|
||||
// AllowNetbird allows netbird interface traffic
|
||||
func (m *Manager) AllowNetbird() error {
|
||||
if !isWindowsFirewallReachable() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isFirewallRuleActive(firewallRuleName) {
|
||||
return nil
|
||||
}
|
||||
return manageFirewallRule(firewallRuleName,
|
||||
addRule,
|
||||
"dir=in",
|
||||
@@ -58,37 +45,47 @@ func (m *Manager) AllowNetbird() error {
|
||||
)
|
||||
}
|
||||
|
||||
func manageFirewallRule(ruleName string, action action, extraArgs ...string) error {
|
||||
|
||||
args := []string{"advfirewall", "firewall", string(action), "rule", "name=" + ruleName}
|
||||
if action == addRule {
|
||||
args = append(args, extraArgs...)
|
||||
}
|
||||
|
||||
cmd := exec.Command("netsh", args...)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func isWindowsFirewallReachable() bool {
|
||||
args := []string{"advfirewall", "show", "allprofiles", "state"}
|
||||
cmd := exec.Command("netsh", args...)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
|
||||
_, err := cmd.Output()
|
||||
func manageFirewallRule(ruleName string, action action, args ...string) error {
|
||||
active, err := isFirewallRuleActive(ruleName)
|
||||
if err != nil {
|
||||
log.Infof("Windows firewall is not reachable, skipping default rule management. Using only user space rules. Error: %s", err)
|
||||
return false
|
||||
return err
|
||||
}
|
||||
|
||||
return true
|
||||
if (action == addRule && !active) || (action == deleteRule && active) {
|
||||
baseArgs := []string{"advfirewall", "firewall", string(action), "rule", "name=" + ruleName}
|
||||
args := append(baseArgs, args...)
|
||||
|
||||
cmd := exec.Command("netsh", args...)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isFirewallRuleActive(ruleName string) bool {
|
||||
func isFirewallRuleActive(ruleName string) (bool, error) {
|
||||
args := []string{"advfirewall", "firewall", "show", "rule", "name=" + ruleName}
|
||||
|
||||
cmd := exec.Command("netsh", args...)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
_, err := cmd.Output()
|
||||
return err == nil
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
var exitError *exec.ExitError
|
||||
if errors.As(err, &exitError) {
|
||||
// if the firewall rule is not active, we expect last exit code to be 1
|
||||
exitStatus := exitError.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
if exitStatus == 1 {
|
||||
if strings.Contains(string(output), noRulesMatchCriteria) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
if strings.Contains(string(output), noRulesMatchCriteria) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ type Manager struct {
|
||||
wgNetwork *net.IPNet
|
||||
decoders sync.Pool
|
||||
wgIface IFaceMapper
|
||||
resetHook func() error
|
||||
resetHook func() error
|
||||
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
@@ -81,17 +81,26 @@ func Create(iface IFaceMapper) (*Manager, error) {
|
||||
//
|
||||
// If comment argument is empty firewall manager should set
|
||||
// rule ID as comment for the rule
|
||||
func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
func (m *Manager) AddFiltering(
|
||||
ip net.IP,
|
||||
proto fw.Protocol,
|
||||
sPort *fw.Port,
|
||||
dPort *fw.Port,
|
||||
direction fw.RuleDirection,
|
||||
action fw.Action,
|
||||
ipsetName string,
|
||||
comment string,
|
||||
) (fw.Rule, error) {
|
||||
r := Rule{
|
||||
id: uuid.New().String(),
|
||||
ip: request.IP,
|
||||
ip: ip,
|
||||
ipLayer: layers.LayerTypeIPv6,
|
||||
matchByIP: true,
|
||||
direction: request.Direction,
|
||||
drop: request.Action == fw.ActionDrop,
|
||||
comment: request.Comment,
|
||||
direction: direction,
|
||||
drop: action == fw.ActionDrop,
|
||||
comment: comment,
|
||||
}
|
||||
if ipNormalized := request.IP.To4(); ipNormalized != nil {
|
||||
if ipNormalized := ip.To4(); ipNormalized != nil {
|
||||
r.ipLayer = layers.LayerTypeIPv4
|
||||
r.ip = ipNormalized
|
||||
}
|
||||
@@ -100,15 +109,15 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
r.matchByIP = false
|
||||
}
|
||||
|
||||
if request.SrcPort != nil && len(request.SrcPort.Values) == 1 {
|
||||
r.sPort = uint16(request.SrcPort.Values[0])
|
||||
if sPort != nil && len(sPort.Values) == 1 {
|
||||
r.sPort = uint16(sPort.Values[0])
|
||||
}
|
||||
|
||||
if request.DstPort != nil && len(request.DstPort.Values) == 1 {
|
||||
r.dPort = uint16(request.DstPort.Values[0])
|
||||
if dPort != nil && len(dPort.Values) == 1 {
|
||||
r.dPort = uint16(dPort.Values[0])
|
||||
}
|
||||
|
||||
switch request.Proto {
|
||||
switch proto {
|
||||
case fw.ProtocolTCP:
|
||||
r.protoLayer = layers.LayerTypeTCP
|
||||
case fw.ProtocolUDP:
|
||||
@@ -123,7 +132,7 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
}
|
||||
|
||||
m.mutex.Lock()
|
||||
if request.Direction == fw.RuleDirectionIN {
|
||||
if direction == fw.RuleDirectionIN {
|
||||
if _, ok := m.incomingRules[r.ip.String()]; !ok {
|
||||
m.incomingRules[r.ip.String()] = make(RuleSet)
|
||||
}
|
||||
@@ -136,7 +145,7 @@ func (m *Manager) AddFiltering(request fw.RuleRequest) ([]fw.Rule, error) {
|
||||
}
|
||||
m.mutex.Unlock()
|
||||
|
||||
return []fw.Rule{&r}, nil
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
// DeleteRule from the firewall by rule definition
|
||||
@@ -179,7 +188,7 @@ func (m *Manager) DropIncoming(packetData []byte) bool {
|
||||
return m.dropFilter(packetData, m.incomingRules, true)
|
||||
}
|
||||
|
||||
// dropFilter implements same logic for booth direction of the traffic
|
||||
// dropFilter imlements same logic for booth direction of the traffic
|
||||
func (m *Manager) dropFilter(packetData []byte, rules map[string]RuleSet, isIncomingPacket bool) bool {
|
||||
m.mutex.RLock()
|
||||
defer m.mutex.RUnlock()
|
||||
|
||||
@@ -53,7 +53,7 @@ func newDefaultManager(fm firewall.Manager) *DefaultManager {
|
||||
|
||||
// ApplyFiltering firewall rules to the local firewall manager processed by ACL policy.
|
||||
//
|
||||
// If allowByDefault is true it appends allow ALL traffic rules to input and output chains.
|
||||
// If allowByDefault is ture it appends allow ALL traffic rules to input and output chains.
|
||||
func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
|
||||
d.mutex.Lock()
|
||||
defer d.mutex.Unlock()
|
||||
@@ -287,14 +287,13 @@ func (d *DefaultManager) addOutRules(
|
||||
ipsetName string,
|
||||
comment string,
|
||||
) ([]firewall.Rule, error) {
|
||||
|
||||
var rules []firewall.Rule
|
||||
rule, err := d.manager.AddFiltering(
|
||||
ip, protocol, nil, port, firewall.RuleDirectionOUT, action, ipsetName, comment)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
|
||||
}
|
||||
rules = append(rules, rule...)
|
||||
rules = append(rules, rule)
|
||||
|
||||
if shouldSkipInvertedRule(protocol, port) {
|
||||
return rules, nil
|
||||
@@ -306,7 +305,7 @@ func (d *DefaultManager) addOutRules(
|
||||
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
|
||||
}
|
||||
|
||||
return append(rules, rule...), nil
|
||||
return append(rules, rule), nil
|
||||
}
|
||||
|
||||
// getRuleID() returns unique ID for the rule based on its parameters.
|
||||
@@ -367,7 +366,7 @@ func (d *DefaultManager) squashAcceptRules(
|
||||
protocols[r.Protocol] = map[string]int{}
|
||||
}
|
||||
|
||||
// special case, when we receive this all network IP address
|
||||
// special case, when we recieve this all network IP address
|
||||
// it means that rules for that protocol was already optimized on the
|
||||
// management side
|
||||
if r.PeerIP == "0.0.0.0" {
|
||||
@@ -394,7 +393,7 @@ func (d *DefaultManager) squashAcceptRules(
|
||||
}
|
||||
|
||||
// order of squashing by protocol is important
|
||||
// only for their first element ALL, it must be done first
|
||||
// only for ther first element ALL, it must be done first
|
||||
protocolOrders := []mgmProto.FirewallRuleProtocol{
|
||||
mgmProto.FirewallRule_ALL,
|
||||
mgmProto.FirewallRule_ICMP,
|
||||
|
||||
@@ -20,7 +20,7 @@ func Create(iface IFaceMapper) (manager *DefaultManager, err error) {
|
||||
return nil, err
|
||||
}
|
||||
if err := fm.AllowNetbird(); err != nil {
|
||||
log.Warnf("failed to allow netbird interface traffic: %v", err)
|
||||
log.Errorf("failed to allow netbird interface traffic: %v", err)
|
||||
}
|
||||
return newDefaultManager(fm), nil
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
gstatus "google.golang.org/grpc/status"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/dns"
|
||||
"github.com/netbirdio/netbird/client/internal/listener"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||
"github.com/netbirdio/netbird/client/ssh"
|
||||
"github.com/netbirdio/netbird/client/system"
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
mgm "github.com/netbirdio/netbird/management/client"
|
||||
mgmProto "github.com/netbirdio/netbird/management/proto"
|
||||
signal "github.com/netbirdio/netbird/signal/client"
|
||||
"github.com/netbirdio/netbird/version"
|
||||
)
|
||||
|
||||
// RunClient with main logic.
|
||||
@@ -31,21 +30,29 @@ func RunClient(ctx context.Context, config *Config, statusRecorder *peer.Status)
|
||||
}
|
||||
|
||||
// RunClientMobile with main logic on mobile system
|
||||
func RunClientMobile(ctx context.Context, config *Config, statusRecorder *peer.Status, tunAdapter iface.TunAdapter, iFaceDiscover stdnet.ExternalIFaceDiscover, networkChangeListener listener.NetworkChangeListener, dnsAddresses []string, dnsReadyListener dns.ReadyListener) error {
|
||||
func RunClientMobile(ctx context.Context, config *Config, statusRecorder *peer.Status, tunAdapter iface.TunAdapter, iFaceDiscover stdnet.ExternalIFaceDiscover, routeListener routemanager.RouteListener, dnsAddresses []string, dnsReadyListener dns.ReadyListener) error {
|
||||
// in case of non Android os these variables will be nil
|
||||
mobileDependency := MobileDependency{
|
||||
TunAdapter: tunAdapter,
|
||||
IFaceDiscover: iFaceDiscover,
|
||||
NetworkChangeListener: networkChangeListener,
|
||||
HostDNSAddresses: dnsAddresses,
|
||||
DnsReadyListener: dnsReadyListener,
|
||||
TunAdapter: tunAdapter,
|
||||
IFaceDiscover: iFaceDiscover,
|
||||
RouteListener: routeListener,
|
||||
HostDNSAddresses: dnsAddresses,
|
||||
DnsReadyListener: dnsReadyListener,
|
||||
}
|
||||
return runClient(ctx, config, statusRecorder, mobileDependency)
|
||||
}
|
||||
|
||||
func RunClientiOS(ctx context.Context, config *Config, statusRecorder *peer.Status, fileDescriptor int32, routeListener routemanager.RouteListener, dnsManager dns.IosDnsManager, interfaceName string) error {
|
||||
mobileDependency := MobileDependency{
|
||||
FileDescriptor: fileDescriptor,
|
||||
InterfaceName: interfaceName,
|
||||
RouteListener: routeListener,
|
||||
DnsManager: dnsManager,
|
||||
}
|
||||
return runClient(ctx, config, statusRecorder, mobileDependency)
|
||||
}
|
||||
|
||||
func runClient(ctx context.Context, config *Config, statusRecorder *peer.Status, mobileDependency MobileDependency) error {
|
||||
log.Infof("starting NetBird client version %s", version.NetbirdVersion())
|
||||
|
||||
backOff := &backoff.ExponentialBackOff{
|
||||
InitialInterval: time.Second,
|
||||
RandomizationFactor: 1,
|
||||
@@ -99,7 +106,7 @@ func runClient(ctx context.Context, config *Config, statusRecorder *peer.Status,
|
||||
cancel()
|
||||
}()
|
||||
|
||||
log.Debugf("connecting to the Management service %s", config.ManagementURL.Host)
|
||||
log.Debugf("conecting to the Management service %s", config.ManagementURL.Host)
|
||||
mgmClient, err := mgm.NewClient(engineCtx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
|
||||
if err != nil {
|
||||
return wrapErr(gstatus.Errorf(codes.FailedPrecondition, "failed connecting to Management Service : %s", err))
|
||||
|
||||
@@ -39,7 +39,7 @@ func (f *fileConfigurator) supportCustomPort() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||
func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
||||
backupFileExist := false
|
||||
_, err := os.Stat(fileDefaultResolvConfBackupLocation)
|
||||
if err == nil {
|
||||
@@ -69,7 +69,7 @@ func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||
}
|
||||
default:
|
||||
// todo improve this and maybe restart DNS manager from scratch
|
||||
return fmt.Errorf("something happened and file manager is not your preferred host dns configurator, restart the agent")
|
||||
return fmt.Errorf("something happened and file manager is not your prefered host dns configurator, restart the agent")
|
||||
}
|
||||
|
||||
var searchDomains string
|
||||
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
)
|
||||
|
||||
type hostManager interface {
|
||||
applyDNSConfig(config hostDNSConfig) error
|
||||
applyDNSConfig(config HostDNSConfig) error
|
||||
restoreHostDNS() error
|
||||
supportCustomPort() bool
|
||||
}
|
||||
|
||||
type hostDNSConfig struct {
|
||||
type HostDNSConfig struct {
|
||||
domains []domainConfig
|
||||
routeAll bool
|
||||
serverIP string
|
||||
@@ -27,12 +27,12 @@ type domainConfig struct {
|
||||
}
|
||||
|
||||
type mockHostConfigurator struct {
|
||||
applyDNSConfigFunc func(config hostDNSConfig) error
|
||||
applyDNSConfigFunc func(config HostDNSConfig) error
|
||||
restoreHostDNSFunc func() error
|
||||
supportCustomPortFunc func() bool
|
||||
}
|
||||
|
||||
func (m *mockHostConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||
func (m *mockHostConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
||||
if m.applyDNSConfigFunc != nil {
|
||||
return m.applyDNSConfigFunc(config)
|
||||
}
|
||||
@@ -55,14 +55,14 @@ func (m *mockHostConfigurator) supportCustomPort() bool {
|
||||
|
||||
func newNoopHostMocker() hostManager {
|
||||
return &mockHostConfigurator{
|
||||
applyDNSConfigFunc: func(config hostDNSConfig) error { return nil },
|
||||
applyDNSConfigFunc: func(config HostDNSConfig) error { return nil },
|
||||
restoreHostDNSFunc: func() error { return nil },
|
||||
supportCustomPortFunc: func() bool { return true },
|
||||
}
|
||||
}
|
||||
|
||||
func dnsConfigToHostDNSConfig(dnsConfig nbdns.Config, ip string, port int) hostDNSConfig {
|
||||
config := hostDNSConfig{
|
||||
func dnsConfigToHostDNSConfig(dnsConfig nbdns.Config, ip string, port int) HostDNSConfig {
|
||||
config := HostDNSConfig{
|
||||
routeAll: false,
|
||||
serverIP: ip,
|
||||
serverPort: port,
|
||||
@@ -78,7 +78,7 @@ func dnsConfigToHostDNSConfig(dnsConfig nbdns.Config, ip string, port int) hostD
|
||||
for _, domain := range nsConfig.Domains {
|
||||
config.domains = append(config.domains, domainConfig{
|
||||
domain: strings.TrimSuffix(domain, "."),
|
||||
matchOnly: !nsConfig.SearchDomainsEnabled,
|
||||
matchOnly: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ func newHostManager(wgInterface WGIface) (hostManager, error) {
|
||||
return &androidHostManager{}, nil
|
||||
}
|
||||
|
||||
func (a androidHostManager) applyDNSConfig(config hostDNSConfig) error {
|
||||
func (a androidHostManager) applyDNSConfig(config HostDNSConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build !ios
|
||||
|
||||
package dns
|
||||
|
||||
import (
|
||||
@@ -42,7 +44,7 @@ func (s *systemConfigurator) supportCustomPort() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *systemConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||
func (s *systemConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
||||
var err error
|
||||
|
||||
if config.routeAll {
|
||||
|
||||
48
client/internal/dns/host_ios.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type iosHostManager struct {
|
||||
dnsManager IosDnsManager
|
||||
config HostDNSConfig
|
||||
}
|
||||
|
||||
func newHostManager(wgInterface WGIface, dnsManager IosDnsManager) (hostManager, error) {
|
||||
return &iosHostManager{
|
||||
dnsManager: dnsManager,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a iosHostManager) applyDNSConfig(config HostDNSConfig) error {
|
||||
var configAsString []string
|
||||
configAsString = append(configAsString, config.serverIP)
|
||||
configAsString = append(configAsString, strconv.Itoa(config.serverPort))
|
||||
configAsString = append(configAsString, strconv.FormatBool(config.routeAll))
|
||||
var domainConfigAsString []string
|
||||
for _, domain := range config.domains {
|
||||
var domainAsString []string
|
||||
domainAsString = append(domainAsString, strconv.FormatBool(domain.disabled))
|
||||
domainAsString = append(domainAsString, domain.domain)
|
||||
domainAsString = append(domainAsString, strconv.FormatBool(domain.matchOnly))
|
||||
domainConfigAsString = append(domainConfigAsString, strings.Join(domainAsString, "|"))
|
||||
}
|
||||
domainConfig := strings.Join(domainConfigAsString, ";")
|
||||
configAsString = append(configAsString, domainConfig)
|
||||
outputString := strings.Join(configAsString, ",")
|
||||
log.Debug("applyDNSConfig: " + outputString)
|
||||
a.dnsManager.ApplyDns(outputString)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a iosHostManager) restoreHostDNS() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a iosHostManager) supportCustomPort() bool {
|
||||
return false
|
||||
}
|
||||
@@ -22,11 +22,13 @@ const (
|
||||
interfaceConfigPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"
|
||||
interfaceConfigNameServerKey = "NameServer"
|
||||
interfaceConfigSearchListKey = "SearchList"
|
||||
tcpipParametersPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
|
||||
)
|
||||
|
||||
type registryConfigurator struct {
|
||||
guid string
|
||||
routingAll bool
|
||||
guid string
|
||||
routingAll bool
|
||||
existingSearchDomains []string
|
||||
}
|
||||
|
||||
func newHostManager(wgInterface WGIface) (hostManager, error) {
|
||||
@@ -43,7 +45,7 @@ func (s *registryConfigurator) supportCustomPort() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *registryConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||
func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
||||
var err error
|
||||
if config.routeAll {
|
||||
err = r.addDNSSetupForAll(config.serverIP)
|
||||
@@ -146,11 +148,30 @@ func (r *registryConfigurator) restoreHostDNS() error {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
return r.deleteInterfaceRegistryKeyProperty(interfaceConfigSearchListKey)
|
||||
return r.updateSearchDomains([]string{})
|
||||
}
|
||||
|
||||
func (r *registryConfigurator) updateSearchDomains(domains []string) error {
|
||||
err := r.setInterfaceRegistryKeyStringValue(interfaceConfigSearchListKey, strings.Join(domains, ","))
|
||||
value, err := getLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get current search domains failed with error: %s", err)
|
||||
}
|
||||
|
||||
valueList := strings.Split(value, ",")
|
||||
setExisting := false
|
||||
if len(r.existingSearchDomains) == 0 {
|
||||
r.existingSearchDomains = valueList
|
||||
setExisting = true
|
||||
}
|
||||
|
||||
if len(domains) == 0 && setExisting {
|
||||
log.Infof("added %d search domains to the registry. Domain list: %s", len(domains), domains)
|
||||
return nil
|
||||
}
|
||||
|
||||
newList := append(r.existingSearchDomains, domains...)
|
||||
|
||||
err = setLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey, strings.Join(newList, ","))
|
||||
if err != nil {
|
||||
return fmt.Errorf("adding search domain failed with error: %s", err)
|
||||
}
|
||||
@@ -214,3 +235,33 @@ func removeRegistryKeyFromDNSPolicyConfig(regKeyPath string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLocalMachineRegistryKeyStringValue(keyPath, key string) (string, error) {
|
||||
regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to open existing key from registry, key path: HKEY_LOCAL_MACHINE\\%s, error: %s", keyPath, err)
|
||||
}
|
||||
defer regKey.Close()
|
||||
|
||||
val, _, err := regKey.GetStringValue(key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getting %s value for key path HKEY_LOCAL_MACHINE\\%s failed with error: %s", key, keyPath, err)
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func setLocalMachineRegistryKeyStringValue(keyPath, key, value string) error {
|
||||
regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.SET_VALUE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to open existing key from registry, key path: HKEY_LOCAL_MACHINE\\%s, error: %s", keyPath, err)
|
||||
}
|
||||
defer regKey.Close()
|
||||
|
||||
err = regKey.SetStringValue(key, value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting %s value %s for key path HKEY_LOCAL_MACHINE\\%s failed with error: %s", key, value, keyPath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
)
|
||||
|
||||
@@ -44,7 +43,3 @@ func (m *MockServer) UpdateDNSServer(serial uint64, update nbdns.Config) error {
|
||||
}
|
||||
return fmt.Errorf("method UpdateDNSServer is not implemented")
|
||||
}
|
||||
|
||||
func (m *MockServer) SearchDomains() []string {
|
||||
return make([]string, 0)
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ func (n *networkManagerDbusConfigurator) supportCustomPort() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (n *networkManagerDbusConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||
func (n *networkManagerDbusConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
||||
connSettings, configVersion, err := n.getAppliedConnectionSettings()
|
||||
if err != nil {
|
||||
return fmt.Errorf("got an error while retrieving the applied connection settings, error: %s", err)
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/listener"
|
||||
)
|
||||
|
||||
type notifier struct {
|
||||
listener listener.NetworkChangeListener
|
||||
listenerMux sync.Mutex
|
||||
searchDomains []string
|
||||
}
|
||||
|
||||
func newNotifier(initialSearchDomains []string) *notifier {
|
||||
sort.Strings(initialSearchDomains)
|
||||
return ¬ifier{
|
||||
searchDomains: initialSearchDomains,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *notifier) setListener(listener listener.NetworkChangeListener) {
|
||||
n.listenerMux.Lock()
|
||||
defer n.listenerMux.Unlock()
|
||||
n.listener = listener
|
||||
}
|
||||
|
||||
func (n *notifier) onNewSearchDomains(searchDomains []string) {
|
||||
sort.Strings(searchDomains)
|
||||
|
||||
if len(n.searchDomains) != len(searchDomains) {
|
||||
n.searchDomains = searchDomains
|
||||
n.notify()
|
||||
return
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(n.searchDomains, searchDomains) {
|
||||
return
|
||||
}
|
||||
|
||||
n.searchDomains = searchDomains
|
||||
n.notify()
|
||||
}
|
||||
|
||||
func (n *notifier) notify() {
|
||||
n.listenerMux.Lock()
|
||||
defer n.listenerMux.Unlock()
|
||||
if n.listener == nil {
|
||||
return
|
||||
}
|
||||
|
||||
go func(l listener.NetworkChangeListener) {
|
||||
l.OnNetworkChanged()
|
||||
}(n.listener)
|
||||
}
|
||||
@@ -27,7 +27,7 @@ func (r *resolvconf) supportCustomPort() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *resolvconf) applyDNSConfig(config hostDNSConfig) error {
|
||||
func (r *resolvconf) applyDNSConfig(config HostDNSConfig) error {
|
||||
var err error
|
||||
if !config.routeAll {
|
||||
err = r.restoreHostDNS()
|
||||
@@ -89,7 +89,7 @@ func (r *resolvconf) applyConfig(content string) error {
|
||||
cmd.Stdin = strings.NewReader(content)
|
||||
_, err := cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("got an error while applying resolvconf configuration for %s interface, error: %s", r.ifaceName, err)
|
||||
return fmt.Errorf("got an error while appying resolvconf configuration for %s interface, error: %s", r.ifaceName, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/mitchellh/hashstructure/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/listener"
|
||||
nbdns "github.com/netbirdio/netbird/dns"
|
||||
)
|
||||
|
||||
@@ -19,14 +19,18 @@ type ReadyListener interface {
|
||||
OnReady()
|
||||
}
|
||||
|
||||
// IosDnsManager is a dns manager interface for iosß
|
||||
type IosDnsManager interface {
|
||||
ApplyDns(string)
|
||||
}
|
||||
|
||||
// Server is a dns server interface
|
||||
type Server interface {
|
||||
Initialize() error
|
||||
Initialize(manager IosDnsManager) error
|
||||
Stop()
|
||||
DnsIP() string
|
||||
UpdateDNSServer(serial uint64, update nbdns.Config) error
|
||||
OnUpdatedHostDNSServer(strings []string)
|
||||
SearchDomains() []string
|
||||
}
|
||||
|
||||
type registeredHandlerMap map[string]handlerWithStop
|
||||
@@ -43,15 +47,15 @@ type DefaultServer struct {
|
||||
hostManager hostManager
|
||||
updateSerial uint64
|
||||
previousConfigHash uint64
|
||||
currentConfig hostDNSConfig
|
||||
currentConfig HostDNSConfig
|
||||
|
||||
// permanent related properties
|
||||
permanent bool
|
||||
hostsDnsList []string
|
||||
hostsDnsListLock sync.Mutex
|
||||
|
||||
// make sense on mobile only
|
||||
searchDomainNotifier *notifier
|
||||
interfaceName string
|
||||
wgAddr string
|
||||
}
|
||||
|
||||
type handlerWithStop interface {
|
||||
@@ -65,7 +69,7 @@ type muxUpdate struct {
|
||||
}
|
||||
|
||||
// NewDefaultServer returns a new dns server
|
||||
func NewDefaultServer(ctx context.Context, wgInterface WGIface, customAddress string) (*DefaultServer, error) {
|
||||
func NewDefaultServer(ctx context.Context, wgInterface WGIface, customAddress string, interfaceName string, wgAddr string) (*DefaultServer, error) {
|
||||
var addrPort *netip.AddrPort
|
||||
if customAddress != "" {
|
||||
parsedAddrPort, err := netip.ParseAddrPort(customAddress)
|
||||
@@ -82,24 +86,21 @@ func NewDefaultServer(ctx context.Context, wgInterface WGIface, customAddress st
|
||||
dnsService = newServiceViaListener(wgInterface, addrPort)
|
||||
}
|
||||
|
||||
return newDefaultServer(ctx, wgInterface, dnsService), nil
|
||||
return newDefaultServer(ctx, wgInterface, dnsService, interfaceName, wgAddr), nil
|
||||
}
|
||||
|
||||
// NewDefaultServerPermanentUpstream returns a new dns server. It optimized for mobile systems
|
||||
func NewDefaultServerPermanentUpstream(ctx context.Context, wgInterface WGIface, hostsDnsList []string, config nbdns.Config, listener listener.NetworkChangeListener) *DefaultServer {
|
||||
func NewDefaultServerPermanentUpstream(ctx context.Context, wgInterface WGIface, hostsDnsList []string) *DefaultServer {
|
||||
log.Debugf("host dns address list is: %v", hostsDnsList)
|
||||
ds := newDefaultServer(ctx, wgInterface, newServiceViaMemory(wgInterface))
|
||||
ds := newDefaultServer(ctx, wgInterface, newServiceViaMemory(wgInterface), "", "")
|
||||
ds.permanent = true
|
||||
ds.hostsDnsList = hostsDnsList
|
||||
ds.addHostRootZone()
|
||||
ds.currentConfig = dnsConfigToHostDNSConfig(config, ds.service.RuntimeIP(), ds.service.RuntimePort())
|
||||
ds.searchDomainNotifier = newNotifier(ds.SearchDomains())
|
||||
ds.searchDomainNotifier.setListener(listener)
|
||||
setServerDns(ds)
|
||||
return ds
|
||||
}
|
||||
|
||||
func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService service) *DefaultServer {
|
||||
func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService service, interfaceName string, wgAddr string) *DefaultServer {
|
||||
ctx, stop := context.WithCancel(ctx)
|
||||
defaultServer := &DefaultServer{
|
||||
ctx: ctx,
|
||||
@@ -109,14 +110,16 @@ func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService servi
|
||||
localResolver: &localResolver{
|
||||
registeredMap: make(registrationMap),
|
||||
},
|
||||
wgInterface: wgInterface,
|
||||
wgInterface: wgInterface,
|
||||
interfaceName: interfaceName,
|
||||
wgAddr: wgAddr,
|
||||
}
|
||||
|
||||
return defaultServer
|
||||
}
|
||||
|
||||
// Initialize instantiate host manager and the dns service
|
||||
func (s *DefaultServer) Initialize() (err error) {
|
||||
func (s *DefaultServer) Initialize(manager IosDnsManager) (err error) {
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
|
||||
@@ -131,7 +134,11 @@ func (s *DefaultServer) Initialize() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
s.hostManager, err = newHostManager(s.wgInterface)
|
||||
if runtime.GOOS == "ios" {
|
||||
s.hostManager, err = newHostManager(nil, manager)
|
||||
} else {
|
||||
s.hostManager, err = newHostManager(s.wgInterface, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -220,21 +227,6 @@ func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) erro
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DefaultServer) SearchDomains() []string {
|
||||
var searchDomains []string
|
||||
|
||||
for _, dConf := range s.currentConfig.domains {
|
||||
if dConf.disabled {
|
||||
continue
|
||||
}
|
||||
if dConf.matchOnly {
|
||||
continue
|
||||
}
|
||||
searchDomains = append(searchDomains, dConf.domain)
|
||||
}
|
||||
return searchDomains
|
||||
}
|
||||
|
||||
func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
|
||||
// is the service should be disabled, we stop the listener or fake resolver
|
||||
// and proceed with a regular update to clean up the handlers and records
|
||||
@@ -269,10 +261,6 @@ func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
if s.searchDomainNotifier != nil {
|
||||
s.searchDomainNotifier.onNewSearchDomains(s.SearchDomains())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -312,10 +300,10 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
|
||||
continue
|
||||
}
|
||||
|
||||
handler := newUpstreamResolver(s.ctx)
|
||||
handler := newUpstreamResolver(s.ctx, s.interfaceName, s.wgAddr)
|
||||
for _, ns := range nsGroup.NameServers {
|
||||
if ns.NSType != nbdns.UDPNameServerType {
|
||||
log.Warnf("skipping nameserver %s with type %s, this peer supports only %s",
|
||||
log.Warnf("skiping nameserver %s with type %s, this peer supports only %s",
|
||||
ns.IP.String(), ns.NSType.String(), nbdns.UDPNameServerType.String())
|
||||
continue
|
||||
}
|
||||
@@ -333,7 +321,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
|
||||
// reapply DNS settings, but it not touch the original configuration and serial number
|
||||
// because it is temporal deactivation until next try
|
||||
//
|
||||
// after some period defined by upstream it tries to reactivate self by calling this hook
|
||||
// after some period defined by upstream it trys to reactivate self by calling this hook
|
||||
// everything we need here is just to re-apply current configuration because it already
|
||||
// contains this upstream settings (temporal deactivation not removed it)
|
||||
handler.deactivate, handler.reactivate = s.upstreamCallbacks(nsGroup, handler)
|
||||
@@ -485,7 +473,7 @@ func (s *DefaultServer) upstreamCallbacks(
|
||||
}
|
||||
|
||||
func (s *DefaultServer) addHostRootZone() {
|
||||
handler := newUpstreamResolver(s.ctx)
|
||||
handler := newUpstreamResolver(s.ctx, s.interfaceName, s.wgAddr)
|
||||
handler.upstreamServers = make([]string, len(s.hostsDnsList))
|
||||
for n, ua := range s.hostsDnsList {
|
||||
handler.upstreamServers[n] = fmt.Sprintf("%s:53", ua)
|
||||
|
||||
@@ -19,6 +19,6 @@ func TestGetServerDns(t *testing.T) {
|
||||
}
|
||||
|
||||
if srvB != srv {
|
||||
t.Errorf("mismatch dns instances")
|
||||
t.Errorf("missmatch dns instances")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,7 +527,7 @@ func TestDNSServerUpstreamDeactivateCallback(t *testing.T) {
|
||||
registeredMap: make(registrationMap),
|
||||
},
|
||||
hostManager: hostManager,
|
||||
currentConfig: hostDNSConfig{
|
||||
currentConfig: HostDNSConfig{
|
||||
domains: []domainConfig{
|
||||
{false, "domain0", false},
|
||||
{false, "domain1", false},
|
||||
@@ -537,7 +537,7 @@ func TestDNSServerUpstreamDeactivateCallback(t *testing.T) {
|
||||
}
|
||||
|
||||
var domainsUpdate string
|
||||
hostManager.applyDNSConfigFunc = func(config hostDNSConfig) error {
|
||||
hostManager.applyDNSConfigFunc = func(config HostDNSConfig) error {
|
||||
domains := []string{}
|
||||
for _, item := range config.domains {
|
||||
if item.disabled {
|
||||
@@ -593,8 +593,7 @@ func TestDNSPermanent_updateHostDNS_emptyUpstream(t *testing.T) {
|
||||
defer wgIFace.Close()
|
||||
|
||||
var dnsList []string
|
||||
dnsConfig := nbdns.Config{}
|
||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, dnsList, dnsConfig, nil)
|
||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, dnsList)
|
||||
err = dnsServer.Initialize()
|
||||
if err != nil {
|
||||
t.Errorf("failed to initialize DNS server: %v", err)
|
||||
@@ -617,8 +616,8 @@ func TestDNSPermanent_updateUpstream(t *testing.T) {
|
||||
t.Fatal("failed to initialize wg interface")
|
||||
}
|
||||
defer wgIFace.Close()
|
||||
dnsConfig := nbdns.Config{}
|
||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
|
||||
|
||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"})
|
||||
err = dnsServer.Initialize()
|
||||
if err != nil {
|
||||
t.Errorf("failed to initialize DNS server: %v", err)
|
||||
@@ -709,8 +708,8 @@ func TestDNSPermanent_matchOnly(t *testing.T) {
|
||||
t.Fatal("failed to initialize wg interface")
|
||||
}
|
||||
defer wgIFace.Close()
|
||||
dnsConfig := nbdns.Config{}
|
||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
|
||||
|
||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"})
|
||||
err = dnsServer.Initialize()
|
||||
if err != nil {
|
||||
t.Errorf("failed to initialize DNS server: %v", err)
|
||||
|
||||
@@ -81,7 +81,7 @@ func (s *systemdDbusConfigurator) supportCustomPort() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *systemdDbusConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||
func (s *systemdDbusConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
||||
parsedIP, err := netip.ParseAddr(config.serverIP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse ip address, error: %s", err)
|
||||
|
||||
@@ -5,13 +5,16 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/miekg/dns"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,25 +38,95 @@ type upstreamResolver struct {
|
||||
mutex sync.Mutex
|
||||
reactivatePeriod time.Duration
|
||||
upstreamTimeout time.Duration
|
||||
lIP net.IP
|
||||
lName string
|
||||
iIndex int
|
||||
|
||||
deactivate func()
|
||||
reactivate func()
|
||||
}
|
||||
|
||||
func newUpstreamResolver(parentCTX context.Context) *upstreamResolver {
|
||||
// func newUpstreamResolver(parentCTX context.Context) *upstreamResolver {
|
||||
// ctx, cancel := context.WithCancel(parentCTX)
|
||||
// return &upstreamResolver{
|
||||
// ctx: ctx,
|
||||
// cancel: cancel,
|
||||
// upstreamClient: &dns.Client{},
|
||||
// upstreamTimeout: upstreamTimeout,
|
||||
// reactivatePeriod: reactivatePeriod,
|
||||
// failsTillDeact: failsTillDeact,
|
||||
// }
|
||||
// }
|
||||
|
||||
func getInterfaceIndex(interfaceName string) (int, error) {
|
||||
iface, err := net.InterfaceByName(interfaceName)
|
||||
if err != nil {
|
||||
log.Errorf("unable to get interface by name error: %s", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return iface.Index, nil
|
||||
}
|
||||
|
||||
func newUpstreamResolver(parentCTX context.Context, interfaceName string, wgAddr string) *upstreamResolver {
|
||||
ctx, cancel := context.WithCancel(parentCTX)
|
||||
|
||||
// Specify the local IP address you want to bind to
|
||||
localIP, _, err := net.ParseCIDR(wgAddr) // Should be our interface IP
|
||||
if err != nil {
|
||||
log.Errorf("error while parsing CIDR: %s", err)
|
||||
}
|
||||
index, err := getInterfaceIndex(interfaceName)
|
||||
log.Debugf("UpstreamResolver interface name: %s, index: %d, ip: %s", interfaceName, index, localIP)
|
||||
if err != nil {
|
||||
log.Debugf("unable to get interface index for %s: %s", interfaceName, err)
|
||||
}
|
||||
localIFaceIndex := index // Should be our interface index
|
||||
|
||||
return &upstreamResolver{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
upstreamClient: &dns.Client{},
|
||||
upstreamTimeout: upstreamTimeout,
|
||||
reactivatePeriod: reactivatePeriod,
|
||||
failsTillDeact: failsTillDeact,
|
||||
lIP: localIP,
|
||||
iIndex: localIFaceIndex,
|
||||
lName: interfaceName,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *upstreamResolver) getClient() *dns.Client {
|
||||
dialer := &net.Dialer{
|
||||
LocalAddr: &net.UDPAddr{
|
||||
IP: u.lIP,
|
||||
Port: 0, // Let the OS pick a free port
|
||||
},
|
||||
Timeout: upstreamTimeout,
|
||||
Control: func(network, address string, c syscall.RawConn) error {
|
||||
var operr error
|
||||
fn := func(s uintptr) {
|
||||
operr = unix.SetsockoptInt(int(s), unix.IPPROTO_IP, unix.IP_BOUND_IF, u.iIndex)
|
||||
}
|
||||
|
||||
if err := c.Control(fn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if operr != nil {
|
||||
log.Errorf("error while setting socket option: %s", operr)
|
||||
}
|
||||
|
||||
return operr
|
||||
},
|
||||
}
|
||||
client := &dns.Client{
|
||||
Dialer: dialer,
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
func (u *upstreamResolver) stop() {
|
||||
log.Debugf("stopping serving DNS for upstreams %s", u.upstreamServers)
|
||||
log.Debugf("stoping serving DNS for upstreams %s", u.upstreamServers)
|
||||
u.cancel()
|
||||
}
|
||||
|
||||
@@ -61,7 +134,7 @@ func (u *upstreamResolver) stop() {
|
||||
func (u *upstreamResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
defer u.checkUpstreamFails()
|
||||
|
||||
log.WithField("question", r.Question[0]).Trace("received an upstream question")
|
||||
//log.WithField("question", r.Question[0]).Debug("received an upstream question")
|
||||
|
||||
select {
|
||||
case <-u.ctx.Done():
|
||||
@@ -70,10 +143,20 @@ func (u *upstreamResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
}
|
||||
|
||||
for _, upstream := range u.upstreamServers {
|
||||
ctx, cancel := context.WithTimeout(u.ctx, u.upstreamTimeout)
|
||||
rm, t, err := u.upstreamClient.ExchangeContext(ctx, r, upstream)
|
||||
|
||||
cancel()
|
||||
var (
|
||||
err error
|
||||
t time.Duration
|
||||
rm *dns.Msg
|
||||
)
|
||||
upstreamExchangeClient := u.getClient()
|
||||
if runtime.GOOS != "ios" {
|
||||
ctx, cancel := context.WithTimeout(u.ctx, u.upstreamTimeout)
|
||||
rm, t, err = upstreamExchangeClient.ExchangeContext(ctx, r, upstream)
|
||||
cancel()
|
||||
} else {
|
||||
log.Debugf("ios upstream resolver: %s", upstream)
|
||||
rm, t, err = upstreamExchangeClient.Exchange(r, upstream)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err == context.DeadlineExceeded || isTimeout(err) {
|
||||
@@ -83,11 +166,11 @@ func (u *upstreamResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||
}
|
||||
u.failsCount.Add(1)
|
||||
log.WithError(err).WithField("upstream", upstream).
|
||||
Error("got an error while querying the upstream")
|
||||
Error("got other error while querying the upstream")
|
||||
return
|
||||
}
|
||||
|
||||
log.Tracef("took %s to query the upstream %s", t, upstream)
|
||||
log.Debugf("took %s to query the upstream %s", t, upstream)
|
||||
|
||||
err = w.WriteMsg(rm)
|
||||
if err != nil {
|
||||
@@ -118,10 +201,11 @@ func (u *upstreamResolver) checkUpstreamFails() {
|
||||
case <-u.ctx.Done():
|
||||
return
|
||||
default:
|
||||
log.Warnf("upstream resolving is disabled for %v", reactivatePeriod)
|
||||
u.deactivate()
|
||||
u.disabled = true
|
||||
go u.waitUntilResponse()
|
||||
//todo test the deactivation logic, it seems to affect the client
|
||||
//log.Warnf("upstream resolving is disabled for %v", reactivatePeriod)
|
||||
//u.deactivate()
|
||||
//u.disabled = true
|
||||
//go u.waitUntilResponse()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -195,19 +195,18 @@ func (e *Engine) Start() error {
|
||||
var routes []*route.Route
|
||||
|
||||
if runtime.GOOS == "android" {
|
||||
var dnsConfig *nbdns.Config
|
||||
routes, dnsConfig, err = e.readInitialSettings()
|
||||
routes, err = e.readInitialSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.dnsServer == nil {
|
||||
e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses, *dnsConfig, e.mobileDep.NetworkChangeListener)
|
||||
e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses)
|
||||
go e.mobileDep.DnsReadyListener.OnReady()
|
||||
}
|
||||
} else {
|
||||
// todo fix custom address
|
||||
if e.dnsServer == nil {
|
||||
e.dnsServer, err = dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress)
|
||||
e.dnsServer, err = dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress, e.mobileDep.InterfaceName, wgAddr)
|
||||
if err != nil {
|
||||
e.close()
|
||||
return err
|
||||
@@ -215,16 +214,21 @@ func (e *Engine) Start() error {
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("Initial routes contain %d routes", len(routes))
|
||||
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, routes)
|
||||
e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
|
||||
e.mobileDep.RouteListener.SetInterfaceIP(wgAddr)
|
||||
|
||||
if runtime.GOOS == "android" {
|
||||
err = e.wgInterface.CreateOnMobile(iface.MobileIFaceArguments{
|
||||
Routes: e.routeManager.InitialRouteRange(),
|
||||
Dns: e.dnsServer.DnsIP(),
|
||||
SearchDomains: e.dnsServer.SearchDomains(),
|
||||
e.routeManager.SetRouteChangeListener(e.mobileDep.RouteListener)
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "android":
|
||||
err = e.wgInterface.CreateOnAndroid(iface.MobileIFaceArguments{
|
||||
Routes: e.routeManager.InitialRouteRange(),
|
||||
Dns: e.dnsServer.DnsIP(),
|
||||
})
|
||||
} else {
|
||||
case "ios":
|
||||
err = e.wgInterface.CreateOniOS(e.mobileDep.FileDescriptor)
|
||||
default:
|
||||
err = e.wgInterface.Create()
|
||||
}
|
||||
if err != nil {
|
||||
@@ -266,7 +270,11 @@ func (e *Engine) Start() error {
|
||||
e.acl = acl
|
||||
}
|
||||
|
||||
err = e.dnsServer.Initialize()
|
||||
if runtime.GOOS == "ios" {
|
||||
err = e.dnsServer.Initialize(e.mobileDep.DnsManager)
|
||||
} else {
|
||||
err = e.dnsServer.Initialize(nil)
|
||||
}
|
||||
if err != nil {
|
||||
e.close()
|
||||
return err
|
||||
@@ -468,7 +476,7 @@ func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error {
|
||||
}
|
||||
// start SSH server if it wasn't running
|
||||
if isNil(e.sshServer) {
|
||||
//nil sshServer means it has not yet been started
|
||||
// nil sshServer means it has not yet been started
|
||||
var err error
|
||||
e.sshServer, err = e.sshServerFunc(e.config.SSHKey,
|
||||
fmt.Sprintf("%s:%d", e.wgInterface.Address().IP.String(), nbssh.DefaultSSHPort))
|
||||
@@ -716,9 +724,8 @@ func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig) nbdns.Config {
|
||||
|
||||
for _, nsGroup := range protoDNSConfig.GetNameServerGroups() {
|
||||
dnsNSGroup := &nbdns.NameServerGroup{
|
||||
Primary: nsGroup.GetPrimary(),
|
||||
Domains: nsGroup.GetDomains(),
|
||||
SearchDomainsEnabled: nsGroup.GetSearchDomainsEnabled(),
|
||||
Primary: nsGroup.GetPrimary(),
|
||||
Domains: nsGroup.GetDomains(),
|
||||
}
|
||||
for _, ns := range nsGroup.GetNameServers() {
|
||||
dnsNS := nbdns.NameServer{
|
||||
@@ -1053,14 +1060,13 @@ func (e *Engine) close() {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
|
||||
func (e *Engine) readInitialSettings() ([]*route.Route, error) {
|
||||
netMap, err := e.mgmClient.GetNetworkMap()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
routes := toRoutes(netMap.GetRoutes())
|
||||
dnsCfg := toDNSConfig(netMap.GetDNSConfig())
|
||||
return routes, &dnsCfg, nil
|
||||
return routes, nil
|
||||
}
|
||||
|
||||
func findIPFromInterfaceName(ifaceName string) (net.IP, error) {
|
||||
|
||||
@@ -1039,11 +1039,10 @@ func startManagement(dataDir string) (*grpc.Server, string, error) {
|
||||
return nil, "", err
|
||||
}
|
||||
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
|
||||
store, err := server.NewStoreFromJson(config.Datadir, nil)
|
||||
store, err := server.NewFileStore(config.Datadir, nil)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
|
||||
}
|
||||
|
||||
peersUpdateManager := server.NewPeersUpdateManager()
|
||||
eventStore := &activity.InMemoryEventStore{}
|
||||
if err != nil {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package listener
|
||||
|
||||
// NetworkChangeListener is a callback interface for mobile system
|
||||
type NetworkChangeListener interface {
|
||||
// OnNetworkChanged invoke when network settings has been changed
|
||||
OnNetworkChanged()
|
||||
}
|
||||
@@ -2,16 +2,19 @@ package internal
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/client/internal/dns"
|
||||
"github.com/netbirdio/netbird/client/internal/listener"
|
||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||
"github.com/netbirdio/netbird/iface"
|
||||
)
|
||||
|
||||
// MobileDependency collect all dependencies for mobile platform
|
||||
type MobileDependency struct {
|
||||
TunAdapter iface.TunAdapter
|
||||
IFaceDiscover stdnet.ExternalIFaceDiscover
|
||||
NetworkChangeListener listener.NetworkChangeListener
|
||||
HostDNSAddresses []string
|
||||
DnsReadyListener dns.ReadyListener
|
||||
TunAdapter iface.TunAdapter
|
||||
IFaceDiscover stdnet.ExternalIFaceDiscover
|
||||
RouteListener routemanager.RouteListener
|
||||
HostDNSAddresses []string
|
||||
DnsReadyListener dns.ReadyListener
|
||||
DnsManager dns.IosDnsManager
|
||||
FileDescriptor int32
|
||||
InterfaceName string
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[string]ro
|
||||
log.Warnf("the network %s has not been assigned a routing peer as no peers from the list %s are currently connected", c.network, peers)
|
||||
|
||||
} else if chosen != currID {
|
||||
log.Infof("new chosen route is %s with peer %s with score %d for network %s", chosen, c.routes[chosen].Peer, chosenScore, c.network)
|
||||
log.Infof("new chosen route is %s with peer %s with score %d", chosen, c.routes[chosen].Peer, chosenScore)
|
||||
}
|
||||
|
||||
return chosen
|
||||
|
||||
31
client/internal/routemanager/firewall_ios.go
Normal file
@@ -0,0 +1,31 @@
|
||||
//go:build ios
|
||||
|
||||
package routemanager
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// newFirewall returns a nil manager
|
||||
func newFirewall(context.Context) (firewallManager, error) {
|
||||
return iOSFirewallManager{}, nil
|
||||
}
|
||||
|
||||
type iOSFirewallManager struct {
|
||||
}
|
||||
|
||||
func (i iOSFirewallManager) RestoreOrCreateContainers() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i iOSFirewallManager) InsertRoutingRules(pair routerPair) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i iOSFirewallManager) RemoveRoutingRules(pair routerPair) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i iOSFirewallManager) CleanRoutingRules() {
|
||||
return
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
//go:build !linux && !ios
|
||||
// +build !linux,!ios
|
||||
|
||||
package routemanager
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/listener"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/iface"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
@@ -17,7 +16,7 @@ import (
|
||||
// Manager is a route manager interface
|
||||
type Manager interface {
|
||||
UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error
|
||||
SetRouteChangeListener(listener listener.NetworkChangeListener)
|
||||
SetRouteChangeListener(listener RouteListener)
|
||||
InitialRouteRange() []string
|
||||
Stop()
|
||||
}
|
||||
@@ -97,7 +96,7 @@ func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Ro
|
||||
}
|
||||
|
||||
// SetRouteChangeListener set RouteListener for route change notifier
|
||||
func (m *DefaultManager) SetRouteChangeListener(listener listener.NetworkChangeListener) {
|
||||
func (m *DefaultManager) SetRouteChangeListener(listener RouteListener) {
|
||||
m.notifier.setListener(listener)
|
||||
}
|
||||
|
||||
@@ -156,7 +155,7 @@ func (m *DefaultManager) classifiesRoutes(newRoutes []*route.Route) (map[string]
|
||||
// if prefix is too small, lets assume is a possible default route which is not yet supported
|
||||
// we skip this route management
|
||||
if newRoute.Network.Bits() < 7 {
|
||||
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skipping this route",
|
||||
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skiping this route",
|
||||
version.NetbirdVersion(), newRoute.Network)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/listener"
|
||||
"github.com/netbirdio/netbird/iface"
|
||||
"github.com/netbirdio/netbird/route"
|
||||
)
|
||||
@@ -33,7 +32,7 @@ func (m *MockManager) Start(ctx context.Context, iface *iface.WGIface) {
|
||||
}
|
||||
|
||||
// SetRouteChangeListener mock implementation of SetRouteChangeListener from Manager interface
|
||||
func (m *MockManager) SetRouteChangeListener(listener listener.NetworkChangeListener) {
|
||||
func (m *MockManager) SetRouteChangeListener(listener RouteListener) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -135,8 +135,7 @@ func (n *nftablesManager) RestoreOrCreateContainers() error {
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
if table.Name == "filter" && table.Family == nftables.TableFamilyIPv4 {
|
||||
log.Debugf("nftables: found filter table for ipv4")
|
||||
if table.Name == "filter" {
|
||||
n.filterTable = table
|
||||
continue
|
||||
}
|
||||
@@ -487,7 +486,7 @@ func (n *nftablesManager) RemoveRoutingRules(pair routerPair) error {
|
||||
if len(n.rules) == 2 && n.defaultForwardRules[0] != nil {
|
||||
err := n.eraseDefaultForwardRule()
|
||||
if err != nil {
|
||||
log.Errorf("failed to delete default fwd rule: %s", err)
|
||||
log.Errorf("failed to delte default fwd rule: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,28 +2,37 @@ package routemanager
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal/listener"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/route"
|
||||
)
|
||||
|
||||
// RouteListener is a callback interface for mobile system
|
||||
type RouteListener interface {
|
||||
// OnNewRouteSetting invoke when new route setting has been arrived
|
||||
OnNewRouteSetting(string)
|
||||
SetInterfaceIP(string)
|
||||
}
|
||||
|
||||
type notifier struct {
|
||||
initialRouteRangers []string
|
||||
routeRangers []string
|
||||
|
||||
listener listener.NetworkChangeListener
|
||||
listenerMux sync.Mutex
|
||||
routeListener RouteListener
|
||||
routeListenerMux sync.Mutex
|
||||
}
|
||||
|
||||
func newNotifier() *notifier {
|
||||
return ¬ifier{}
|
||||
}
|
||||
|
||||
func (n *notifier) setListener(listener listener.NetworkChangeListener) {
|
||||
n.listenerMux.Lock()
|
||||
defer n.listenerMux.Unlock()
|
||||
n.listener = listener
|
||||
func (n *notifier) setListener(listener RouteListener) {
|
||||
n.routeListenerMux.Lock()
|
||||
defer n.routeListenerMux.Unlock()
|
||||
n.routeListener = listener
|
||||
}
|
||||
|
||||
func (n *notifier) setInitialClientRoutes(clientRoutes []*route.Route) {
|
||||
@@ -50,22 +59,20 @@ func (n *notifier) onNewRoutes(idMap map[string][]*route.Route) {
|
||||
|
||||
n.routeRangers = newNets
|
||||
|
||||
if !n.hasDiff(n.initialRouteRangers, newNets) {
|
||||
return
|
||||
}
|
||||
n.notify()
|
||||
}
|
||||
|
||||
func (n *notifier) notify() {
|
||||
n.listenerMux.Lock()
|
||||
defer n.listenerMux.Unlock()
|
||||
if n.listener == nil {
|
||||
n.routeListenerMux.Lock()
|
||||
defer n.routeListenerMux.Unlock()
|
||||
if n.routeListener == nil {
|
||||
return
|
||||
}
|
||||
|
||||
go func(l listener.NetworkChangeListener) {
|
||||
l.OnNetworkChanged()
|
||||
}(n.listener)
|
||||
go func(l RouteListener) {
|
||||
log.Debugf("notifying route listener with route ranges: %s", strings.Join(n.routeRangers, ","))
|
||||
l.OnNewRouteSetting(strings.Join(n.routeRangers, ","))
|
||||
}(n.routeListener)
|
||||
}
|
||||
|
||||
func (n *notifier) hasDiff(a []string, b []string) bool {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build android
|
||||
|
||||
package routemanager
|
||||
|
||||
import (
|
||||
|
||||
15
client/internal/routemanager/systemops_ios.go
Normal file
@@ -0,0 +1,15 @@
|
||||
//go:build ios
|
||||
|
||||
package routemanager
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
func addToRouteTableIfNoExists(prefix netip.Prefix, addr string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeFromRouteTableIfNonSystem(prefix netip.Prefix, addr string) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !android
|
||||
//go:build !android && !ios
|
||||
|
||||
package routemanager
|
||||
|
||||
|
||||
200
client/ios/NetBirdSDK/client.go
Normal file
@@ -0,0 +1,200 @@
|
||||
package NetBirdSDK
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/auth"
|
||||
"github.com/netbirdio/netbird/client/internal/dns"
|
||||
"github.com/netbirdio/netbird/client/internal/peer"
|
||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||
"github.com/netbirdio/netbird/client/system"
|
||||
"github.com/netbirdio/netbird/formatter"
|
||||
)
|
||||
|
||||
// RouteListener export internal RouteListener for mobile
|
||||
type RouteListener interface {
|
||||
routemanager.RouteListener
|
||||
}
|
||||
|
||||
// DnsManager export internal dns Manager for mobile
|
||||
type DnsManager interface {
|
||||
dns.IosDnsManager
|
||||
}
|
||||
|
||||
// CustomLogger export internal CustomLogger for mobile
|
||||
type CustomLogger interface {
|
||||
Debug(message string)
|
||||
Info(message string)
|
||||
Error(message string)
|
||||
}
|
||||
|
||||
func init() {
|
||||
formatter.SetLogcatFormatter(log.StandardLogger())
|
||||
}
|
||||
|
||||
// Client struct manage the life circle of background service
|
||||
type Client struct {
|
||||
cfgFile string
|
||||
recorder *peer.Status
|
||||
ctxCancel context.CancelFunc
|
||||
ctxCancelLock *sync.Mutex
|
||||
deviceName string
|
||||
osName string
|
||||
osVersion string
|
||||
routeListener routemanager.RouteListener
|
||||
onHostDnsFn func([]string)
|
||||
dnsManager dns.IosDnsManager
|
||||
loginComplete bool
|
||||
}
|
||||
|
||||
// NewClient instantiate a new Client
|
||||
func NewClient(cfgFile, deviceName string, osVersion string, osName string, routeListener RouteListener, dnsManager DnsManager) *Client {
|
||||
return &Client{
|
||||
cfgFile: cfgFile,
|
||||
deviceName: deviceName,
|
||||
osName: osName,
|
||||
osVersion: osVersion,
|
||||
recorder: peer.NewRecorder(""),
|
||||
ctxCancelLock: &sync.Mutex{},
|
||||
routeListener: routeListener,
|
||||
dnsManager: dnsManager,
|
||||
}
|
||||
}
|
||||
|
||||
// Run start the internal client. It is a blocker function
|
||||
func (c *Client) Run(fd int32, interfaceName string) error {
|
||||
log.Infof("Starting NetBird client")
|
||||
log.Debugf("Tunnel uses interface: %s", interfaceName)
|
||||
cfg, err := internal.UpdateOrCreateConfig(internal.ConfigInput{
|
||||
ConfigPath: c.cfgFile,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
|
||||
|
||||
var ctx context.Context
|
||||
//nolint
|
||||
ctxWithValues := context.WithValue(context.Background(), system.DeviceNameCtxKey, c.deviceName)
|
||||
ctxWithValues = context.WithValue(ctxWithValues, system.OsNameCtxKey, c.osName)
|
||||
ctxWithValues = context.WithValue(ctxWithValues, system.OsVersionCtxKey, c.osVersion)
|
||||
c.ctxCancelLock.Lock()
|
||||
ctx, c.ctxCancel = context.WithCancel(ctxWithValues)
|
||||
defer c.ctxCancel()
|
||||
c.ctxCancelLock.Unlock()
|
||||
|
||||
auth := NewAuthWithConfig(ctx, cfg)
|
||||
err = auth.Login()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Auth successful")
|
||||
// todo do not throw error in case of cancelled context
|
||||
ctx = internal.CtxInitState(ctx)
|
||||
c.onHostDnsFn = func([]string) {}
|
||||
return internal.RunClientiOS(ctx, cfg, c.recorder, fd, c.routeListener, c.dnsManager, interfaceName)
|
||||
}
|
||||
|
||||
// Stop the internal client and free the resources
|
||||
func (c *Client) Stop() {
|
||||
c.ctxCancelLock.Lock()
|
||||
defer c.ctxCancelLock.Unlock()
|
||||
if c.ctxCancel == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.ctxCancel()
|
||||
}
|
||||
|
||||
// ÏSetTraceLogLevel configure the logger to trace level
|
||||
func (c *Client) SetTraceLogLevel() {
|
||||
log.SetLevel(log.TraceLevel)
|
||||
}
|
||||
|
||||
// getStatusDetails return with the list of the PeerInfos
|
||||
func (c *Client) GetStatusDetails() *StatusDetails {
|
||||
|
||||
fullStatus := c.recorder.GetFullStatus()
|
||||
|
||||
peerInfos := make([]PeerInfo, len(fullStatus.Peers))
|
||||
for n, p := range fullStatus.Peers {
|
||||
pi := PeerInfo{
|
||||
p.IP,
|
||||
p.FQDN,
|
||||
p.ConnStatus.String(),
|
||||
}
|
||||
peerInfos[n] = pi
|
||||
}
|
||||
return &StatusDetails{items: peerInfos, fqdn: fullStatus.LocalPeerState.FQDN, ip: fullStatus.LocalPeerState.IP}
|
||||
}
|
||||
|
||||
func (c *Client) GetManagementStatus() bool {
|
||||
return c.recorder.GetFullStatus().ManagementState.Connected
|
||||
}
|
||||
|
||||
func (c *Client) IsLoginRequired() bool {
|
||||
var ctx context.Context
|
||||
ctxWithValues := context.WithValue(context.Background(), system.DeviceNameCtxKey, c.deviceName)
|
||||
c.ctxCancelLock.Lock()
|
||||
defer c.ctxCancelLock.Unlock()
|
||||
ctx, c.ctxCancel = context.WithCancel(ctxWithValues)
|
||||
|
||||
cfg, _ := internal.UpdateOrCreateConfig(internal.ConfigInput{
|
||||
ConfigPath: c.cfgFile,
|
||||
})
|
||||
|
||||
needsLogin, _ := internal.IsLoginRequired(ctx, cfg.PrivateKey, cfg.ManagementURL, cfg.SSHKey)
|
||||
return needsLogin
|
||||
}
|
||||
|
||||
func (c *Client) LoginForMobile() string {
|
||||
var ctx context.Context
|
||||
ctxWithValues := context.WithValue(context.Background(), system.DeviceNameCtxKey, c.deviceName)
|
||||
c.ctxCancelLock.Lock()
|
||||
defer c.ctxCancelLock.Unlock()
|
||||
ctx, c.ctxCancel = context.WithCancel(ctxWithValues)
|
||||
|
||||
cfg, _ := internal.UpdateOrCreateConfig(internal.ConfigInput{
|
||||
ConfigPath: c.cfgFile,
|
||||
})
|
||||
|
||||
oAuthFlow, err := auth.NewOAuthFlow(ctx, cfg, false)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
flowInfo, err := oAuthFlow.RequestAuthInfo(context.TODO())
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
// This could cause a potential race condition with loading the extension which need to be handled on swift side
|
||||
go func() {
|
||||
waitTimeout := time.Duration(flowInfo.ExpiresIn)
|
||||
waitCTX, cancel := context.WithTimeout(ctx, waitTimeout*time.Second)
|
||||
defer cancel()
|
||||
tokenInfo, err := oAuthFlow.WaitToken(waitCTX, flowInfo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
jwtToken := tokenInfo.GetTokenToUse()
|
||||
_ = internal.Login(ctx, cfg, "", jwtToken)
|
||||
c.loginComplete = true
|
||||
}()
|
||||
|
||||
return flowInfo.VerificationURIComplete
|
||||
}
|
||||
|
||||
func (c *Client) IsLoginComplete() bool {
|
||||
return c.loginComplete
|
||||
}
|
||||
|
||||
func (c *Client) ClearLoginComplete() {
|
||||
c.loginComplete = false
|
||||
}
|
||||
5
client/ios/NetBirdSDK/gomobile.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package NetBirdSDK
|
||||
|
||||
import _ "golang.org/x/mobile/bind"
|
||||
|
||||
// to keep our CI/CD that checks go.mod and go.sum files happy, we need to import the package above
|
||||
16
client/ios/NetBirdSDK/logger.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package NetBirdSDK
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/netbirdio/netbird/util"
|
||||
)
|
||||
|
||||
var logFile *os.File
|
||||
|
||||
// InitializeLog initializes the log file.
|
||||
func InitializeLog(logLevel string, filePath string) error {
|
||||
var err error
|
||||
err = util.InitLog(logLevel, filePath)
|
||||
return err
|
||||
}
|
||||
184
client/ios/NetBirdSDK/login.go
Normal file
@@ -0,0 +1,184 @@
|
||||
package NetBirdSDK
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
gstatus "google.golang.org/grpc/status"
|
||||
|
||||
"github.com/netbirdio/netbird/client/cmd"
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
"github.com/netbirdio/netbird/client/internal/auth"
|
||||
"github.com/netbirdio/netbird/client/system"
|
||||
)
|
||||
|
||||
// SSOListener is async listener for mobile framework
|
||||
type SSOListener interface {
|
||||
OnSuccess(bool)
|
||||
OnError(error)
|
||||
}
|
||||
|
||||
// ErrListener is async listener for mobile framework
|
||||
type ErrListener interface {
|
||||
OnSuccess()
|
||||
OnError(error)
|
||||
}
|
||||
|
||||
// URLOpener it is a callback interface. The Open function will be triggered if
|
||||
// the backend want to show an url for the user
|
||||
type URLOpener interface {
|
||||
Open(string)
|
||||
}
|
||||
|
||||
// Auth can register or login new client
|
||||
type Auth struct {
|
||||
ctx context.Context
|
||||
config *internal.Config
|
||||
cfgPath string
|
||||
}
|
||||
|
||||
// NewAuth instantiate Auth struct and validate the management URL
|
||||
func NewAuth(cfgPath string, mgmURL string) (*Auth, error) {
|
||||
inputCfg := internal.ConfigInput{
|
||||
ManagementURL: mgmURL,
|
||||
}
|
||||
|
||||
cfg, err := internal.CreateInMemoryConfig(inputCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Auth{
|
||||
ctx: context.Background(),
|
||||
config: cfg,
|
||||
cfgPath: cfgPath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewAuthWithConfig instantiate Auth based on existing config
|
||||
func NewAuthWithConfig(ctx context.Context, config *internal.Config) *Auth {
|
||||
return &Auth{
|
||||
ctx: ctx,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// SaveConfigIfSSOSupported test the connectivity with the management server by retrieving the server device flow info.
|
||||
// If it returns a flow info than save the configuration and return true. If it gets a codes.NotFound, it means that SSO
|
||||
// is not supported and returns false without saving the configuration. For other errors return false.
|
||||
func (a *Auth) SaveConfigIfSSOSupported() (bool, error) {
|
||||
supportsSSO := true
|
||||
err := a.withBackOff(a.ctx, func() (err error) {
|
||||
_, err = internal.GetDeviceAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
|
||||
if s, ok := gstatus.FromError(err); ok && s.Code() == codes.NotFound {
|
||||
_, err = internal.GetPKCEAuthorizationFlowInfo(a.ctx, a.config.PrivateKey, a.config.ManagementURL)
|
||||
if s, ok := gstatus.FromError(err); ok && s.Code() == codes.NotFound {
|
||||
supportsSSO = false
|
||||
err = nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
if !supportsSSO {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("backoff cycle failed: %v", err)
|
||||
}
|
||||
|
||||
err = internal.WriteOutConfig(a.cfgPath, a.config)
|
||||
return true, err
|
||||
}
|
||||
|
||||
// LoginWithSetupKeyAndSaveConfig test the connectivity with the management server with the setup key.
|
||||
func (a *Auth) LoginWithSetupKeyAndSaveConfig(setupKey string, deviceName string) error {
|
||||
//nolint
|
||||
ctxWithValues := context.WithValue(a.ctx, system.DeviceNameCtxKey, deviceName)
|
||||
|
||||
err := a.withBackOff(a.ctx, func() error {
|
||||
backoffErr := internal.Login(ctxWithValues, a.config, setupKey, "")
|
||||
if s, ok := gstatus.FromError(backoffErr); ok && (s.Code() == codes.PermissionDenied) {
|
||||
// we got an answer from management, exit backoff earlier
|
||||
return backoff.Permanent(backoffErr)
|
||||
}
|
||||
return backoffErr
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("backoff cycle failed: %v", err)
|
||||
}
|
||||
|
||||
return internal.WriteOutConfig(a.cfgPath, a.config)
|
||||
}
|
||||
|
||||
func (a *Auth) Login() error {
|
||||
var needsLogin bool
|
||||
|
||||
// check if we need to generate JWT token
|
||||
err := a.withBackOff(a.ctx, func() (err error) {
|
||||
needsLogin, err = internal.IsLoginRequired(a.ctx, a.config.PrivateKey, a.config.ManagementURL, a.config.SSHKey)
|
||||
return
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("backoff cycle failed: %v", err)
|
||||
}
|
||||
|
||||
jwtToken := ""
|
||||
if needsLogin {
|
||||
return fmt.Errorf("Not authenticated")
|
||||
}
|
||||
|
||||
err = a.withBackOff(a.ctx, func() error {
|
||||
err := internal.Login(a.ctx, a.config, "", jwtToken)
|
||||
if s, ok := gstatus.FromError(err); ok && (s.Code() == codes.InvalidArgument || s.Code() == codes.PermissionDenied) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("backoff cycle failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Auth) foregroundGetTokenInfo(urlOpener URLOpener) (*auth.TokenInfo, error) {
|
||||
oAuthFlow, err := auth.NewOAuthFlow(a.ctx, a.config, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
flowInfo, err := oAuthFlow.RequestAuthInfo(context.TODO())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting a request OAuth flow info failed: %v", err)
|
||||
}
|
||||
|
||||
go urlOpener.Open(flowInfo.VerificationURIComplete)
|
||||
|
||||
waitTimeout := time.Duration(flowInfo.ExpiresIn)
|
||||
waitCTX, cancel := context.WithTimeout(a.ctx, waitTimeout*time.Second)
|
||||
defer cancel()
|
||||
tokenInfo, err := oAuthFlow.WaitToken(waitCTX, flowInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("waiting for browser login failed: %v", err)
|
||||
}
|
||||
|
||||
return &tokenInfo, nil
|
||||
}
|
||||
|
||||
func (a *Auth) withBackOff(ctx context.Context, bf func() error) error {
|
||||
return backoff.RetryNotify(
|
||||
bf,
|
||||
backoff.WithContext(cmd.CLIBackOffSettings, ctx),
|
||||
func(err error, duration time.Duration) {
|
||||
log.Warnf("retrying Login to the Management service in %v due to error %v", duration, err)
|
||||
})
|
||||
}
|
||||
50
client/ios/NetBirdSDK/peer_notifier.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package NetBirdSDK
|
||||
|
||||
// PeerInfo describe information about the peers. It designed for the UI usage
|
||||
type PeerInfo struct {
|
||||
IP string
|
||||
FQDN string
|
||||
ConnStatus string // Todo replace to enum
|
||||
}
|
||||
|
||||
// PeerInfoCollection made for Java layer to get non default types as collection
|
||||
type PeerInfoCollection interface {
|
||||
Add(s string) PeerInfoCollection
|
||||
Get(i int) string
|
||||
Size() int
|
||||
GetFQDN() string
|
||||
GetIP() string
|
||||
}
|
||||
|
||||
// StatusDetails is the implementation of the PeerInfoCollection
|
||||
type StatusDetails struct {
|
||||
items []PeerInfo
|
||||
fqdn string
|
||||
ip string
|
||||
}
|
||||
|
||||
// Add new PeerInfo to the collection
|
||||
func (array StatusDetails) Add(s PeerInfo) StatusDetails {
|
||||
array.items = append(array.items, s)
|
||||
return array
|
||||
}
|
||||
|
||||
// Get return an element of the collection
|
||||
func (array StatusDetails) Get(i int) *PeerInfo {
|
||||
return &array.items[i]
|
||||
}
|
||||
|
||||
// Size return with the size of the collection
|
||||
func (array StatusDetails) Size() int {
|
||||
return len(array.items)
|
||||
}
|
||||
|
||||
// GetFQDN return with the FQDN of the local peer
|
||||
func (array StatusDetails) GetFQDN() string {
|
||||
return array.fqdn
|
||||
}
|
||||
|
||||
// GetIP return with the IP of the local peer
|
||||
func (array StatusDetails) GetIP() string {
|
||||
return array.ip
|
||||
}
|
||||
78
client/ios/NetBirdSDK/preferences.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package NetBirdSDK
|
||||
|
||||
import (
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
)
|
||||
|
||||
// Preferences export a subset of the internal config for gomobile
|
||||
type Preferences struct {
|
||||
configInput internal.ConfigInput
|
||||
}
|
||||
|
||||
// NewPreferences create new Preferences instance
|
||||
func NewPreferences(configPath string) *Preferences {
|
||||
ci := internal.ConfigInput{
|
||||
ConfigPath: configPath,
|
||||
}
|
||||
return &Preferences{ci}
|
||||
}
|
||||
|
||||
// GetManagementURL read url from config file
|
||||
func (p *Preferences) GetManagementURL() (string, error) {
|
||||
if p.configInput.ManagementURL != "" {
|
||||
return p.configInput.ManagementURL, nil
|
||||
}
|
||||
|
||||
cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cfg.ManagementURL.String(), err
|
||||
}
|
||||
|
||||
// SetManagementURL store the given url and wait for commit
|
||||
func (p *Preferences) SetManagementURL(url string) {
|
||||
p.configInput.ManagementURL = url
|
||||
}
|
||||
|
||||
// GetAdminURL read url from config file
|
||||
func (p *Preferences) GetAdminURL() (string, error) {
|
||||
if p.configInput.AdminURL != "" {
|
||||
return p.configInput.AdminURL, nil
|
||||
}
|
||||
|
||||
cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cfg.AdminURL.String(), err
|
||||
}
|
||||
|
||||
// SetAdminURL store the given url and wait for commit
|
||||
func (p *Preferences) SetAdminURL(url string) {
|
||||
p.configInput.AdminURL = url
|
||||
}
|
||||
|
||||
// GetPreSharedKey read preshared key from config file
|
||||
func (p *Preferences) GetPreSharedKey() (string, error) {
|
||||
if p.configInput.PreSharedKey != nil {
|
||||
return *p.configInput.PreSharedKey, nil
|
||||
}
|
||||
|
||||
cfg, err := internal.ReadConfig(p.configInput.ConfigPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return cfg.PreSharedKey, err
|
||||
}
|
||||
|
||||
// SetPreSharedKey store the given key and wait for commit
|
||||
func (p *Preferences) SetPreSharedKey(key string) {
|
||||
p.configInput.PreSharedKey = &key
|
||||
}
|
||||
|
||||
// Commit write out the changes into config file
|
||||
func (p *Preferences) Commit() error {
|
||||
_, err := internal.UpdateOrCreateConfig(p.configInput)
|
||||
return err
|
||||
}
|
||||
120
client/ios/NetBirdSDK/preferences_test.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package NetBirdSDK
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/netbirdio/netbird/client/internal"
|
||||
)
|
||||
|
||||
func TestPreferences_DefaultValues(t *testing.T) {
|
||||
cfgFile := filepath.Join(t.TempDir(), "netbird.json")
|
||||
p := NewPreferences(cfgFile)
|
||||
defaultVar, err := p.GetAdminURL()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read default value: %s", err)
|
||||
}
|
||||
|
||||
if defaultVar != internal.DefaultAdminURL {
|
||||
t.Errorf("invalid default admin url: %s", defaultVar)
|
||||
}
|
||||
|
||||
defaultVar, err = p.GetManagementURL()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read default management URL: %s", err)
|
||||
}
|
||||
|
||||
if defaultVar != internal.DefaultManagementURL {
|
||||
t.Errorf("invalid default management url: %s", defaultVar)
|
||||
}
|
||||
|
||||
var preSharedKey string
|
||||
preSharedKey, err = p.GetPreSharedKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read default preshared key: %s", err)
|
||||
}
|
||||
|
||||
if preSharedKey != "" {
|
||||
t.Errorf("invalid preshared key: %s", preSharedKey)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreferences_ReadUncommitedValues(t *testing.T) {
|
||||
exampleString := "exampleString"
|
||||
cfgFile := filepath.Join(t.TempDir(), "netbird.json")
|
||||
p := NewPreferences(cfgFile)
|
||||
|
||||
p.SetAdminURL(exampleString)
|
||||
resp, err := p.GetAdminURL()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read admin url: %s", err)
|
||||
}
|
||||
|
||||
if resp != exampleString {
|
||||
t.Errorf("unexpected admin url: %s", resp)
|
||||
}
|
||||
|
||||
p.SetManagementURL(exampleString)
|
||||
resp, err = p.GetManagementURL()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read managmenet url: %s", err)
|
||||
}
|
||||
|
||||
if resp != exampleString {
|
||||
t.Errorf("unexpected managemenet url: %s", resp)
|
||||
}
|
||||
|
||||
p.SetPreSharedKey(exampleString)
|
||||
resp, err = p.GetPreSharedKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read preshared key: %s", err)
|
||||
}
|
||||
|
||||
if resp != exampleString {
|
||||
t.Errorf("unexpected preshared key: %s", resp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreferences_Commit(t *testing.T) {
|
||||
exampleURL := "https://myurl.com:443"
|
||||
examplePresharedKey := "topsecret"
|
||||
cfgFile := filepath.Join(t.TempDir(), "netbird.json")
|
||||
p := NewPreferences(cfgFile)
|
||||
|
||||
p.SetAdminURL(exampleURL)
|
||||
p.SetManagementURL(exampleURL)
|
||||
p.SetPreSharedKey(examplePresharedKey)
|
||||
|
||||
err := p.Commit()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to save changes: %s", err)
|
||||
}
|
||||
|
||||
p = NewPreferences(cfgFile)
|
||||
resp, err := p.GetAdminURL()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read admin url: %s", err)
|
||||
}
|
||||
|
||||
if resp != exampleURL {
|
||||
t.Errorf("unexpected admin url: %s", resp)
|
||||
}
|
||||
|
||||
resp, err = p.GetManagementURL()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read managmenet url: %s", err)
|
||||
}
|
||||
|
||||
if resp != exampleURL {
|
||||
t.Errorf("unexpected managemenet url: %s", resp)
|
||||
}
|
||||
|
||||
resp, err = p.GetPreSharedKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read preshared key: %s", err)
|
||||
}
|
||||
|
||||
if resp != examplePresharedKey {
|
||||
t.Errorf("unexpected preshared key: %s", resp)
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,12 @@ import (
|
||||
// DeviceNameCtxKey context key for device name
|
||||
const DeviceNameCtxKey = "deviceName"
|
||||
|
||||
// OsVersionCtxKey context key for operating system version
|
||||
const OsVersionCtxKey = "OsVersion"
|
||||
|
||||
// OsNameCtxKey context key for operating system name
|
||||
const OsNameCtxKey = "OsName"
|
||||
|
||||
// Info is an object that contains machine information
|
||||
// Most of the code is taken from https://github.com/matishsiao/goInfo
|
||||
type Info struct {
|
||||
@@ -52,6 +58,24 @@ func extractDeviceName(ctx context.Context, defaultName string) string {
|
||||
return v
|
||||
}
|
||||
|
||||
// extractOsVersion extracts operating system version from context or returns the default
|
||||
func extractOsVersion(ctx context.Context, defaultName string) string {
|
||||
v, ok := ctx.Value(OsVersionCtxKey).(string)
|
||||
if !ok {
|
||||
return defaultName
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// extractOsName extracts operating system name from context or returns the default
|
||||
func extractOsName(ctx context.Context, defaultName string) string {
|
||||
v, ok := ctx.Value(OsNameCtxKey).(string)
|
||||
if !ok {
|
||||
return defaultName
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GetDesktopUIUserAgent returns the Desktop ui user agent
|
||||
func GetDesktopUIUserAgent() string {
|
||||
return "netbird-desktop-ui/" + version.NetbirdVersion()
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build !ios
|
||||
// +build !ios
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
|
||||
27
client/system/info_ios.go
Normal file
@@ -0,0 +1,27 @@
|
||||
//go:build ios
|
||||
// +build ios
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
|
||||
"github.com/netbirdio/netbird/version"
|
||||
)
|
||||
|
||||
// GetInfo retrieves and parses the system information
|
||||
func GetInfo(ctx context.Context) *Info {
|
||||
|
||||
// Convert fixed-size byte arrays to Go strings
|
||||
sysName := extractOsName(ctx, "sysName")
|
||||
swVersion := extractOsVersion(ctx, "swVersion")
|
||||
|
||||
gio := &Info{Kernel: sysName, OSVersion: swVersion, Core: swVersion, Platform: "unknown", OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
||||
// systemHostname, _ := os.Hostname()
|
||||
gio.Hostname = extractDeviceName(ctx, "hostname")
|
||||
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||
gio.UIVersion = extractUserAgent(ctx)
|
||||
|
||||
return gio
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
sudo apt update
|
||||
sudo apt remove gir1.2-appindicator3-0.1
|
||||
sudo apt install -y libayatana-appindicator3-dev
|
||||
go build
|
||||
@@ -15,10 +15,8 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/app"
|
||||
@@ -76,30 +74,18 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed netbird-systemtray-connected.ico
|
||||
//go:embed connected.ico
|
||||
var iconConnectedICO []byte
|
||||
|
||||
//go:embed netbird-systemtray-connected.png
|
||||
//go:embed connected.png
|
||||
var iconConnectedPNG []byte
|
||||
|
||||
//go:embed netbird-systemtray-default.ico
|
||||
//go:embed disconnected.ico
|
||||
var iconDisconnectedICO []byte
|
||||
|
||||
//go:embed netbird-systemtray-default.png
|
||||
//go:embed disconnected.png
|
||||
var iconDisconnectedPNG []byte
|
||||
|
||||
//go:embed netbird-systemtray-update.ico
|
||||
var iconUpdateICO []byte
|
||||
|
||||
//go:embed netbird-systemtray-update.png
|
||||
var iconUpdatePNG []byte
|
||||
|
||||
//go:embed netbird-systemtray-update-cloud.ico
|
||||
var iconUpdateCloudICO []byte
|
||||
|
||||
//go:embed netbird-systemtray-update-cloud.png
|
||||
var iconUpdateCloudPNG []byte
|
||||
|
||||
type serviceClient struct {
|
||||
ctx context.Context
|
||||
addr string
|
||||
@@ -107,20 +93,14 @@ type serviceClient struct {
|
||||
|
||||
icConnected []byte
|
||||
icDisconnected []byte
|
||||
icUpdate []byte
|
||||
icUpdateCloud []byte
|
||||
|
||||
// systray menu items
|
||||
mStatus *systray.MenuItem
|
||||
mUp *systray.MenuItem
|
||||
mDown *systray.MenuItem
|
||||
mAdminPanel *systray.MenuItem
|
||||
mSettings *systray.MenuItem
|
||||
mAbout *systray.MenuItem
|
||||
mVersionUI *systray.MenuItem
|
||||
mVersionDaemon *systray.MenuItem
|
||||
mUpdate *systray.MenuItem
|
||||
mQuit *systray.MenuItem
|
||||
mStatus *systray.MenuItem
|
||||
mUp *systray.MenuItem
|
||||
mDown *systray.MenuItem
|
||||
mAdminPanel *systray.MenuItem
|
||||
mSettings *systray.MenuItem
|
||||
mQuit *systray.MenuItem
|
||||
|
||||
// application with main windows.
|
||||
app fyne.App
|
||||
@@ -138,11 +118,6 @@ type serviceClient struct {
|
||||
managementURL string
|
||||
preSharedKey string
|
||||
adminURL string
|
||||
|
||||
update *version.Update
|
||||
daemonVersion string
|
||||
updateIndicationLock sync.Mutex
|
||||
isUpdateIconActive bool
|
||||
}
|
||||
|
||||
// newServiceClient instance constructor
|
||||
@@ -155,20 +130,14 @@ func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient
|
||||
app: a,
|
||||
|
||||
showSettings: showSettings,
|
||||
update: version.NewUpdate(),
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
s.icConnected = iconConnectedICO
|
||||
s.icDisconnected = iconDisconnectedICO
|
||||
s.icUpdate = iconUpdateICO
|
||||
s.icUpdateCloud = iconUpdateCloudICO
|
||||
|
||||
} else {
|
||||
s.icConnected = iconConnectedPNG
|
||||
s.icDisconnected = iconDisconnectedPNG
|
||||
s.icUpdate = iconUpdatePNG
|
||||
s.icUpdateCloud = iconUpdateCloudPNG
|
||||
}
|
||||
|
||||
if showSettings {
|
||||
@@ -233,10 +202,9 @@ func (s *serviceClient) getSettingsForm() *widget.Form {
|
||||
}
|
||||
|
||||
_, err = client.Login(s.ctx, &proto.LoginRequest{
|
||||
ManagementUrl: s.iMngURL.Text,
|
||||
AdminURL: s.iAdminURL.Text,
|
||||
PreSharedKey: s.iPreSharedKey.Text,
|
||||
IsLinuxDesktopClient: runtime.GOOS == "linux",
|
||||
ManagementUrl: s.iMngURL.Text,
|
||||
AdminURL: s.iAdminURL.Text,
|
||||
PreSharedKey: s.iPreSharedKey.Text,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("login to management URL: %v", err)
|
||||
@@ -265,9 +233,7 @@ func (s *serviceClient) login() error {
|
||||
return err
|
||||
}
|
||||
|
||||
loginResp, err := conn.Login(s.ctx, &proto.LoginRequest{
|
||||
IsLinuxDesktopClient: runtime.GOOS == "linux",
|
||||
})
|
||||
loginResp, err := conn.Login(s.ctx, &proto.LoginRequest{})
|
||||
if err != nil {
|
||||
log.Errorf("login to management URL with: %v", err)
|
||||
return err
|
||||
@@ -359,53 +325,19 @@ func (s *serviceClient) updateStatus() error {
|
||||
return err
|
||||
}
|
||||
|
||||
s.updateIndicationLock.Lock()
|
||||
defer s.updateIndicationLock.Unlock()
|
||||
|
||||
var systrayIconState bool
|
||||
if status.Status == string(internal.StatusConnected) && !s.mUp.Disabled() {
|
||||
if !s.isUpdateIconActive {
|
||||
systray.SetIcon(s.icConnected)
|
||||
}
|
||||
systray.SetIcon(s.icConnected)
|
||||
systray.SetTooltip("NetBird (Connected)")
|
||||
s.mStatus.SetTitle("Connected")
|
||||
s.mUp.Disable()
|
||||
s.mDown.Enable()
|
||||
systrayIconState = true
|
||||
} else if status.Status != string(internal.StatusConnected) && s.mUp.Disabled() {
|
||||
if !s.isUpdateIconActive {
|
||||
systray.SetIcon(s.icDisconnected)
|
||||
}
|
||||
systray.SetIcon(s.icDisconnected)
|
||||
systray.SetTooltip("NetBird (Disconnected)")
|
||||
s.mStatus.SetTitle("Disconnected")
|
||||
s.mDown.Disable()
|
||||
s.mUp.Enable()
|
||||
systrayIconState = false
|
||||
}
|
||||
|
||||
// the updater struct notify by the upgrades available only, but if meanwhile the daemon has successfully
|
||||
// updated must reset the mUpdate visibility state
|
||||
if s.daemonVersion != status.DaemonVersion {
|
||||
s.mUpdate.Hide()
|
||||
s.daemonVersion = status.DaemonVersion
|
||||
|
||||
s.isUpdateIconActive = s.update.SetDaemonVersion(status.DaemonVersion)
|
||||
if !s.isUpdateIconActive {
|
||||
if systrayIconState {
|
||||
systray.SetIcon(s.icConnected)
|
||||
s.mAbout.SetIcon(s.icConnected)
|
||||
} else {
|
||||
systray.SetIcon(s.icDisconnected)
|
||||
s.mAbout.SetIcon(s.icDisconnected)
|
||||
}
|
||||
}
|
||||
|
||||
daemonVersionTitle := normalizedVersion(s.daemonVersion)
|
||||
s.mVersionDaemon.SetTitle(fmt.Sprintf("Daemon: %s", daemonVersionTitle))
|
||||
s.mVersionDaemon.SetTooltip(fmt.Sprintf("Daemon version: %s", daemonVersionTitle))
|
||||
s.mVersionDaemon.Show()
|
||||
}
|
||||
|
||||
return nil
|
||||
}, &backoff.ExponentialBackOff{
|
||||
InitialInterval: time.Second,
|
||||
@@ -439,24 +371,11 @@ func (s *serviceClient) onTrayReady() {
|
||||
systray.AddSeparator()
|
||||
s.mSettings = systray.AddMenuItem("Settings", "Settings of the application")
|
||||
systray.AddSeparator()
|
||||
|
||||
s.mAbout = systray.AddMenuItem("About", "About")
|
||||
s.mAbout.SetIcon(s.icDisconnected)
|
||||
versionString := normalizedVersion(version.NetbirdVersion())
|
||||
s.mVersionUI = s.mAbout.AddSubMenuItem(fmt.Sprintf("GUI: %s", versionString), fmt.Sprintf("GUI Version: %s", versionString))
|
||||
s.mVersionUI.Disable()
|
||||
|
||||
s.mVersionDaemon = s.mAbout.AddSubMenuItem("", "")
|
||||
s.mVersionDaemon.Disable()
|
||||
s.mVersionDaemon.Hide()
|
||||
|
||||
s.mUpdate = s.mAbout.AddSubMenuItem("Download latest version", "Download latest version")
|
||||
s.mUpdate.Hide()
|
||||
|
||||
v := systray.AddMenuItem("v"+version.NetbirdVersion(), "Client Version: "+version.NetbirdVersion())
|
||||
v.Disable()
|
||||
systray.AddSeparator()
|
||||
s.mQuit = systray.AddMenuItem("Quit", "Quit the client app")
|
||||
|
||||
s.update.SetOnUpdateListener(s.onUpdateAvailable)
|
||||
go func() {
|
||||
s.getSrvConfig()
|
||||
for {
|
||||
@@ -514,11 +433,6 @@ func (s *serviceClient) onTrayReady() {
|
||||
case <-s.mQuit.ClickedCh:
|
||||
systray.Quit()
|
||||
return
|
||||
case <-s.mUpdate.ClickedCh:
|
||||
err := openURL(version.DownloadUrl())
|
||||
if err != nil {
|
||||
log.Errorf("%s", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("process connection: %v", err)
|
||||
@@ -527,14 +441,6 @@ func (s *serviceClient) onTrayReady() {
|
||||
}()
|
||||
}
|
||||
|
||||
func normalizedVersion(version string) string {
|
||||
versionString := version
|
||||
if unicode.IsDigit(rune(versionString[0])) {
|
||||
versionString = fmt.Sprintf("v%s", versionString)
|
||||
}
|
||||
return versionString
|
||||
}
|
||||
|
||||
func (s *serviceClient) onTrayExit() {}
|
||||
|
||||
// getSrvClient connection to the service.
|
||||
@@ -595,32 +501,6 @@ func (s *serviceClient) getSrvConfig() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *serviceClient) onUpdateAvailable() {
|
||||
s.updateIndicationLock.Lock()
|
||||
defer s.updateIndicationLock.Unlock()
|
||||
|
||||
s.mUpdate.Show()
|
||||
s.mAbout.SetIcon(s.icUpdateCloud)
|
||||
|
||||
s.isUpdateIconActive = true
|
||||
systray.SetIcon(s.icUpdate)
|
||||
}
|
||||
|
||||
func openURL(url string) error {
|
||||
var err error
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", url).Start()
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", url).Start()
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// checkPIDFile exists and return error, or write new.
|
||||
func checkPIDFile() error {
|
||||
pidFile := path.Join(os.TempDir(), "wiretrustee-ui.pid")
|
||||
|
||||
BIN
client/ui/connected.ico
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
client/ui/connected.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
client/ui/disconnected.ico
Normal file
|
After Width: | Height: | Size: 104 KiB |
BIN
client/ui/disconnected.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
@@ -50,25 +50,21 @@ func ToNameServerType(typeString string) NameServerType {
|
||||
// NameServerGroup group of nameservers and with group ids
|
||||
type NameServerGroup struct {
|
||||
// ID identifier of group
|
||||
ID string `gorm:"primaryKey"`
|
||||
// AccountID is a reference to Account that this object belongs
|
||||
AccountID string `gorm:"index"`
|
||||
ID string
|
||||
// Name group name
|
||||
Name string
|
||||
// Description group description
|
||||
Description string
|
||||
// NameServers list of nameservers
|
||||
NameServers []NameServer `gorm:"serializer:json"`
|
||||
NameServers []NameServer
|
||||
// Groups list of peer group IDs to distribute the nameservers information
|
||||
Groups []string `gorm:"serializer:json"`
|
||||
Groups []string
|
||||
// Primary indicates that the nameserver group is the primary resolver for any dns query
|
||||
Primary bool
|
||||
// Domains indicate the dns query domains to use with this nameserver group
|
||||
Domains []string `gorm:"serializer:json"`
|
||||
Domains []string
|
||||
// Enabled group status
|
||||
Enabled bool
|
||||
// SearchDomainsEnabled indicates whether to add match domains to search domains list or not
|
||||
SearchDomainsEnabled bool
|
||||
}
|
||||
|
||||
// NameServer represents a DNS nameserver
|
||||
@@ -135,15 +131,14 @@ func ParseNameServerURL(nsURL string) (NameServer, error) {
|
||||
// Copy copies a nameserver group object
|
||||
func (g *NameServerGroup) Copy() *NameServerGroup {
|
||||
nsGroup := &NameServerGroup{
|
||||
ID: g.ID,
|
||||
Name: g.Name,
|
||||
Description: g.Description,
|
||||
NameServers: make([]NameServer, len(g.NameServers)),
|
||||
Groups: make([]string, len(g.Groups)),
|
||||
Enabled: g.Enabled,
|
||||
Primary: g.Primary,
|
||||
Domains: make([]string, len(g.Domains)),
|
||||
SearchDomainsEnabled: g.SearchDomainsEnabled,
|
||||
ID: g.ID,
|
||||
Name: g.Name,
|
||||
Description: g.Description,
|
||||
NameServers: make([]NameServer, len(g.NameServers)),
|
||||
Groups: make([]string, len(g.Groups)),
|
||||
Enabled: g.Enabled,
|
||||
Primary: g.Primary,
|
||||
Domains: make([]string, len(g.Domains)),
|
||||
}
|
||||
|
||||
copy(nsGroup.NameServers, g.NameServers)
|
||||
@@ -159,7 +154,6 @@ func (g *NameServerGroup) IsEqual(other *NameServerGroup) bool {
|
||||
other.Name == g.Name &&
|
||||
other.Description == g.Description &&
|
||||
other.Primary == g.Primary &&
|
||||
other.SearchDomainsEnabled == g.SearchDomainsEnabled &&
|
||||
compareNameServerList(g.NameServers, other.NameServers) &&
|
||||
compareGroupsList(g.Groups, other.Groups) &&
|
||||
compareGroupsList(g.Domains, other.Domains)
|
||||
|
||||
@@ -30,7 +30,7 @@ func Decrypt(encryptedMsg []byte, peerPublicKey wgtypes.Key, privateKey wgtypes.
|
||||
return nil, err
|
||||
}
|
||||
if len(encryptedMsg) < nonceSize {
|
||||
return nil, fmt.Errorf("invalid encrypted message length")
|
||||
return nil, fmt.Errorf("invalid encrypted message lenght")
|
||||
}
|
||||
copy(nonce[:], encryptedMsg[:nonceSize])
|
||||
opened, ok := box.Open(nil, encryptedMsg[nonceSize:], nonce, toByte32(peerPublicKey), toByte32(privateKey))
|
||||
|
||||
17
go.mod
@@ -17,8 +17,8 @@ require (
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/vishvananda/netlink v1.1.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/sys v0.13.0
|
||||
golang.org/x/crypto v0.9.0
|
||||
golang.org/x/sys v0.8.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de
|
||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||
@@ -46,12 +46,11 @@ require (
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/libp2p/go-netroute v0.2.0
|
||||
github.com/magiconair/properties v1.8.5
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/mdlayher/socket v0.4.0
|
||||
github.com/miekg/dns v1.1.43
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
github.com/nadoo/ipset v0.5.0
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20231027143200-a966bce7db88
|
||||
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pion/logging v0.2.2
|
||||
@@ -69,14 +68,12 @@ require (
|
||||
goauthentik.io/api/v3 v3.2023051.3
|
||||
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028
|
||||
golang.org/x/net v0.17.0
|
||||
golang.org/x/net v0.10.0
|
||||
golang.org/x/oauth2 v0.8.0
|
||||
golang.org/x/sync v0.2.0
|
||||
golang.org/x/term v0.13.0
|
||||
golang.org/x/term v0.8.0
|
||||
google.golang.org/api v0.126.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/sqlite v1.5.3
|
||||
gorm.io/gorm v1.25.4
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -113,8 +110,6 @@ require (
|
||||
github.com/googleapis/gax-go/v2 v2.10.0 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/native v1.0.0 // indirect
|
||||
github.com/kelseyhightower/envconfig v1.4.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
@@ -142,7 +137,7 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.11.1 // indirect
|
||||
golang.org/x/image v0.5.0 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||
|
||||
30
go.sum
@@ -383,10 +383,6 @@ github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
|
||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
@@ -445,8 +441,8 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
@@ -495,8 +491,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/nadoo/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
|
||||
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20231027143200-a966bce7db88 h1:zhe8qseauBuYOS910jpl5sv8Tb+36zxQPXrwYXqll0g=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20231027143200-a966bce7db88/go.mod h1:KSqjzHcqlodTWiuap5lRXxt5KT3vtYRoksL0KIrTK40=
|
||||
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0 h1:hirFRfx3grVA/9eEyjME5/z3nxdJlN9kfQpvWWPk32g=
|
||||
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||
github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c h1:wK/s4nyZj/GF/kFJQjX6nqNfE0G3gcqd6hhnPCyp4sw=
|
||||
@@ -730,8 +724,8 @@ golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -838,9 +832,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -964,17 +957,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.1-0.20230222185716-a3b23cc77e89/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -988,9 +979,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1199,10 +1189,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/sqlite v1.5.3 h1:7/0dUgX28KAcopdfbRWWl68Rflh6osa4rDh+m51KL2g=
|
||||
gorm.io/driver/sqlite v1.5.3/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
|
||||
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
|
||||
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLREcpj61RxrWYzD8uwveOY=
|
||||
|
||||
@@ -282,7 +282,7 @@ func (a *xorMapped) closeWaiters() {
|
||||
// just exit
|
||||
break
|
||||
default:
|
||||
// notify that twe have a new addr
|
||||
// notify tha twe have a new addr
|
||||
close(a.waitAddrReceived)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func TestDeviceWrapperRead(t *testing.T) {
|
||||
|
||||
n, err := wrapped.Read(bufs, sizes, offset)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.Errorf("unexpeted error: %v", err)
|
||||
return
|
||||
}
|
||||
if n != 1 {
|
||||
@@ -105,7 +105,7 @@ func TestDeviceWrapperRead(t *testing.T) {
|
||||
|
||||
n, err := wrapped.Write(bufs, 0)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.Errorf("unexpeted error: %v", err)
|
||||
return
|
||||
}
|
||||
if n != 1 {
|
||||
@@ -154,7 +154,7 @@ func TestDeviceWrapperRead(t *testing.T) {
|
||||
|
||||
n, err := wrapped.Write(bufs, 0)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.Errorf("unexpeted error: %v", err)
|
||||
return
|
||||
}
|
||||
if n != 0 {
|
||||
@@ -211,7 +211,7 @@ func TestDeviceWrapperRead(t *testing.T) {
|
||||
|
||||
n, err := wrapped.Read(bufs, sizes, offset)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
t.Errorf("unexpeted error: %v", err)
|
||||
return
|
||||
}
|
||||
if n != 0 {
|
||||
|
||||
@@ -28,14 +28,20 @@ func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
// CreateOnMobile 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.
|
||||
func (w *WGIface) CreateOnMobile(mIFaceArgs MobileIFaceArguments) error {
|
||||
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
return w.tun.Create(mIFaceArgs)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return fmt.Errorf("this function has not implemented on mobile")
|
||||
}
|
||||
|
||||
// Create this function make sense on mobile only
|
||||
func (w *WGIface) Create() error {
|
||||
return fmt.Errorf("this function has not implemented on mobile")
|
||||
|
||||
51
iface/iface_ios.go
Normal file
@@ -0,0 +1,51 @@
|
||||
//go:build ios
|
||||
// +build 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
|
||||
}
|
||||
|
||||
tun := newTunDevice(wgAddress, mtu, tunAdapter, transportNet)
|
||||
wgIFace.tun = tun
|
||||
|
||||
wgIFace.configurer = newWGConfigurer(tun)
|
||||
|
||||
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
|
||||
|
||||
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.
|
||||
// Will reuse an existing one.
|
||||
func (w *WGIface) CreateOnAndroid(mIFaceArgs MobileIFaceArguments) error {
|
||||
return fmt.Errorf("this function has not implemented on mobile")
|
||||
}
|
||||
|
||||
// Create this function make sense on mobile only
|
||||
func (w *WGIface) Create() error {
|
||||
return fmt.Errorf("this function has not implemented on mobile")
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
//go:build !android
|
||||
//go:build !android && !ios
|
||||
// +build !android,!ios
|
||||
|
||||
package iface
|
||||
|
||||
@@ -27,8 +28,13 @@ func NewWGIFace(iFaceName string, address string, mtu int, tunAdapter TunAdapter
|
||||
return wgIFace, nil
|
||||
}
|
||||
|
||||
// CreateOnMobile this function make sense on mobile only
|
||||
func (w *WGIface) CreateOnMobile(mIFaceArgs MobileIFaceArguments) error {
|
||||
// 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")
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
// keep darwin compatibility
|
||||
// keep darwin compability
|
||||
const (
|
||||
WgIntNumber = 2000
|
||||
)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build android
|
||||
// +build android
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
|
||||
63
iface/ipc_parser_ios.go
Normal file
@@ -0,0 +1,63 @@
|
||||
//go:build ios
|
||||
// +build 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()
|
||||
}
|
||||
@@ -110,7 +110,7 @@ func canCreateFakeWireGuardInterface() bool {
|
||||
// We willingly try to create a device with an invalid
|
||||
// MTU here as the validation of the MTU will be performed after
|
||||
// the validation of the link kind and hence allows us to check
|
||||
// for the existence of the wireguard module without actually
|
||||
// for the existance of the wireguard module without actually
|
||||
// creating a link.
|
||||
//
|
||||
// As a side-effect, this will also let the kernel lazy-load
|
||||
@@ -271,12 +271,12 @@ func moduleStatus(name string) (status, error) {
|
||||
func loadModuleWithDependencies(name, path string) error {
|
||||
deps, err := getModuleDependencies(name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't load list of module %s dependencies", name)
|
||||
return fmt.Errorf("couldn't load list of module %s dependecies", name)
|
||||
}
|
||||
for _, dep := range deps {
|
||||
err = loadModule(dep.name, dep.path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't load dependency module %s for %s", dep.name, name)
|
||||
return fmt.Errorf("couldn't load dependecy module %s for %s", dep.name, name)
|
||||
}
|
||||
}
|
||||
return loadModule(name, path)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package iface
|
||||
|
||||
type MobileIFaceArguments struct {
|
||||
Routes []string
|
||||
Dns string
|
||||
SearchDomains []string
|
||||
Routes []string
|
||||
Dns string
|
||||
}
|
||||
|
||||
// NetInterface represents a generic network tunnel interface
|
||||
|
||||
@@ -2,6 +2,6 @@ package iface
|
||||
|
||||
// TunAdapter is an interface for create tun device from externel service
|
||||
type TunAdapter interface {
|
||||
ConfigureInterface(address string, mtu int, dns string, searchDomains string, routes string) (int, error)
|
||||
ConfigureInterface(address string, mtu int, dns string, routes string) (int, error)
|
||||
UpdateAddr(address string) error
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build android
|
||||
// +build android
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
@@ -37,8 +40,7 @@ func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
|
||||
log.Info("create tun interface")
|
||||
var err error
|
||||
routesString := t.routesToString(mIFaceArgs.Routes)
|
||||
searchDomainsToString := t.searchDomainsToString(mIFaceArgs.SearchDomains)
|
||||
t.fd, err = t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, mIFaceArgs.Dns, searchDomainsToString, routesString)
|
||||
t.fd, err = t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, mIFaceArgs.Dns, routesString)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create Android interface: %s", err)
|
||||
return err
|
||||
@@ -56,7 +58,7 @@ func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
|
||||
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.
|
||||
// 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()
|
||||
if err != nil {
|
||||
@@ -95,7 +97,3 @@ func (t *tunDevice) Close() (err error) {
|
||||
func (t *tunDevice) routesToString(routes []string) string {
|
||||
return strings.Join(routes, ";")
|
||||
}
|
||||
|
||||
func (t *tunDevice) searchDomainsToString(searchDomains []string) string {
|
||||
return strings.Join(searchDomains, ";")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build !ios
|
||||
// +build !ios
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
@@ -20,7 +23,7 @@ func (c *tunDevice) Create() error {
|
||||
func (c *tunDevice) assignAddr() error {
|
||||
cmd := exec.Command("ifconfig", c.name, "inet", c.address.IP.String(), c.address.IP.String())
|
||||
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 addreess command "%v" failed with output %s and error: `, cmd.String(), out)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
105
iface/tun_ios.go
Normal file
@@ -0,0 +1,105 @@
|
||||
//go:build ios
|
||||
// +build ios
|
||||
|
||||
package iface
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/transport/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.zx2c4.com/wireguard/device"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
|
||||
"github.com/netbirdio/netbird/iface/bind"
|
||||
)
|
||||
|
||||
type tunDevice struct {
|
||||
address WGAddress
|
||||
mtu int
|
||||
tunAdapter TunAdapter
|
||||
iceBind *bind.ICEBind
|
||||
|
||||
fd int
|
||||
name string
|
||||
device *device.Device
|
||||
wrapper *DeviceWrapper
|
||||
}
|
||||
|
||||
func newTunDevice(address WGAddress, mtu int, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
|
||||
return &tunDevice{
|
||||
address: address,
|
||||
mtu: mtu,
|
||||
tunAdapter: tunAdapter,
|
||||
iceBind: bind.NewICEBind(transportNet),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *tunDevice) Create(tunFd int32) error {
|
||||
log.Infof("create tun interface")
|
||||
|
||||
dupTunFd, err := unix.Dup(int(tunFd))
|
||||
if err != nil {
|
||||
log.Errorf("Unable to dup tun fd: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = unix.SetNonblock(dupTunFd, true)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to set tun fd as non blocking: %v", err)
|
||||
unix.Close(dupTunFd)
|
||||
return err
|
||||
}
|
||||
tun, err := tun.CreateTUNFromFile(os.NewFile(uintptr(dupTunFd), "/dev/tun"), 0)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to create new tun device from fd: %v", err)
|
||||
unix.Close(dupTunFd)
|
||||
return err
|
||||
}
|
||||
|
||||
t.wrapper = newDeviceWrapper(tun)
|
||||
log.Debug("Attaching to interface")
|
||||
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.
|
||||
// this helps with support for the older NetBird clients that had a hardcoded direct mode
|
||||
// t.device.DisableSomeRoamingForBrokenMobileSemantics()
|
||||
|
||||
err = t.device.Up()
|
||||
if err != nil {
|
||||
t.device.Close()
|
||||
return err
|
||||
}
|
||||
log.Debugf("device is ready to use: %s", t.name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) Device() *device.Device {
|
||||
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
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tunDevice) Close() (err error) {
|
||||
if t.device != nil {
|
||||
t.device.Close()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (t *tunDevice) routesToString(routes []string) string {
|
||||
return strings.Join(routes, ";")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build (linux || darwin) && !android
|
||||
//go:build (linux || darwin) && !android && !ios
|
||||
|
||||
package iface
|
||||
|
||||
|
||||
165
iface/wg_configurer_ios.go
Normal file
@@ -0,0 +1,165 @@
|
||||
//go:build ios
|
||||
// +build ios
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build !android
|
||||
//go:build !android && !ios
|
||||
|
||||
package iface
|
||||
|
||||
@@ -44,7 +44,7 @@ func (c *wGConfigurer) configureInterface(privateKey string, port int) error {
|
||||
}
|
||||
|
||||
func (c *wGConfigurer) 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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -6,15 +6,14 @@
|
||||
NETBIRD_MGMT_API_PORT=${NETBIRD_MGMT_API_PORT:-33073}
|
||||
# Management API endpoint address, used by the Dashboard
|
||||
NETBIRD_MGMT_API_ENDPOINT=https://$NETBIRD_DOMAIN:$NETBIRD_MGMT_API_PORT
|
||||
# Management Certificate file path. These are generated by the Dashboard container
|
||||
# Management Certficate file path. These are generated by the Dashboard container
|
||||
NETBIRD_LETSENCRYPT_DOMAIN=$NETBIRD_DOMAIN
|
||||
NETBIRD_MGMT_API_CERT_FILE="/etc/letsencrypt/live/$NETBIRD_LETSENCRYPT_DOMAIN/fullchain.pem"
|
||||
# Management Certificate key file path.
|
||||
# Management Certficate key file path.
|
||||
NETBIRD_MGMT_API_CERT_KEY_FILE="/etc/letsencrypt/live/$NETBIRD_LETSENCRYPT_DOMAIN/privkey.pem"
|
||||
# By default Management single account mode is enabled and domain set to $NETBIRD_DOMAIN, you may want to set this to your user's email domain
|
||||
NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN=$NETBIRD_DOMAIN
|
||||
NETBIRD_MGMT_DNS_DOMAIN=${NETBIRD_MGMT_DNS_DOMAIN:-netbird.selfhosted}
|
||||
NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=${NETBIRD_MGMT_IDP_SIGNKEY_REFRESH:-false}
|
||||
|
||||
# Signal
|
||||
NETBIRD_SIGNAL_PROTOCOL="http"
|
||||
@@ -56,9 +55,6 @@ NETBIRD_AUTH_PKCE_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
||||
NETBIRD_DASH_AUTH_USE_AUDIENCE=${NETBIRD_DASH_AUTH_USE_AUDIENCE:-true}
|
||||
NETBIRD_DASH_AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
||||
|
||||
# Store config
|
||||
NETBIRD_STORE_CONFIG_ENGINE=${NETBIRD_STORE_CONFIG_ENGINE:-"jsonfile"}
|
||||
|
||||
# exports
|
||||
export NETBIRD_DOMAIN
|
||||
export NETBIRD_AUTH_CLIENT_ID
|
||||
@@ -90,7 +86,6 @@ export LETSENCRYPT_VOLUMESUFFIX
|
||||
export NETBIRD_DISABLE_ANONYMOUS_METRICS
|
||||
export NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN
|
||||
export NETBIRD_MGMT_DNS_DOMAIN
|
||||
export NETBIRD_MGMT_IDP_SIGNKEY_REFRESH
|
||||
export NETBIRD_SIGNAL_PROTOCOL
|
||||
export NETBIRD_SIGNAL_PORT
|
||||
export NETBIRD_AUTH_USER_ID_CLAIM
|
||||
@@ -102,5 +97,4 @@ export NETBIRD_AUTH_PKCE_AUTHORIZATION_ENDPOINT
|
||||
export NETBIRD_AUTH_PKCE_USE_ID_TOKEN
|
||||
export NETBIRD_AUTH_PKCE_AUDIENCE
|
||||
export NETBIRD_DASH_AUTH_USE_AUDIENCE
|
||||
export NETBIRD_DASH_AUTH_AUDIENCE
|
||||
export NETBIRD_STORE_CONFIG_ENGINE
|
||||
export NETBIRD_DASH_AUTH_AUDIENCE
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if ! which curl >/dev/null 2>&1; then
|
||||
echo "This script uses curl fetch OpenID configuration from IDP."
|
||||
@@ -125,7 +124,7 @@ if [[ "$NETBIRD_DISABLE_LETSENCRYPT" == "true" ]]; then
|
||||
echo "- $NETBIRD_SIGNAL_ENDPOINT/signalexchange.SignalExchange/ -grpc-> signal:80"
|
||||
echo "You most likely also have to change NETBIRD_MGMT_API_ENDPOINT in base.setup.env and port-mappings in docker-compose.yml.tmpl and rerun this script."
|
||||
echo " The target of the forwards depends on your setup. Beware of the gRPC protocol instead of http for management and signal!"
|
||||
echo "You are also free to remove any occurrences of the Letsencrypt-volume $LETSENCRYPT_VOLUMENAME"
|
||||
echo "You are also free to remove any occurences of the Letsencrypt-volume $LETSENCRYPT_VOLUMENAME"
|
||||
echo ""
|
||||
|
||||
export NETBIRD_SIGNAL_PROTOCOL="https"
|
||||
@@ -155,8 +154,6 @@ if [ -n "$NETBIRD_MGMT_IDP" ]; then
|
||||
export NETBIRD_IDP_MGMT_CLIENT_ID
|
||||
export NETBIRD_IDP_MGMT_CLIENT_SECRET
|
||||
export NETBIRD_IDP_MGMT_EXTRA_CONFIG=$EXTRA_CONFIG
|
||||
else
|
||||
export NETBIRD_IDP_MGMT_EXTRA_CONFIG={}
|
||||
fi
|
||||
|
||||
IFS=',' read -r -a REDIRECT_URL_PORTS <<< "$NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS"
|
||||
@@ -173,29 +170,8 @@ if [ "$NETBIRD_DASH_AUTH_USE_AUDIENCE" = "false" ]; then
|
||||
export NETBIRD_AUTH_PKCE_AUDIENCE=
|
||||
fi
|
||||
|
||||
# Read the encryption key
|
||||
if test -f 'management.json'; then
|
||||
encKey=$(jq -r ".DataStoreEncryptionKey" management.json)
|
||||
if [[ "$encKey" != "null" ]]; then
|
||||
export NETBIRD_DATASTORE_ENC_KEY=$encKey
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
env | grep NETBIRD
|
||||
|
||||
bkp_postfix="$(date +%s)"
|
||||
if test -f 'docker-compose.yml'; then
|
||||
cp docker-compose.yml "docker-compose.yml.bkp.${bkp_postfix}"
|
||||
fi
|
||||
|
||||
if test -f 'management.json'; then
|
||||
cp management.json "management.json.bkp.${bkp_postfix}"
|
||||
fi
|
||||
|
||||
if test -f 'turnserver.conf'; then
|
||||
cp turnserver.conf "turnserver.conf.bpk.${bkp_postfix}"
|
||||
fi
|
||||
envsubst <docker-compose.yml.tmpl >docker-compose.yml
|
||||
envsubst <management.json.tmpl | jq . >management.json
|
||||
envsubst <turnserver.conf.tmpl >turnserver.conf
|
||||
envsubst <management.json.tmpl >management.json
|
||||
envsubst <turnserver.conf.tmpl >turnserver.conf
|
||||
@@ -27,10 +27,6 @@
|
||||
"Password": null
|
||||
},
|
||||
"Datadir": "",
|
||||
"DataStoreEncryptionKey": "$NETBIRD_DATASTORE_ENC_KEY",
|
||||
"StoreConfig": {
|
||||
"Engine": "$NETBIRD_STORE_CONFIG_ENGINE"
|
||||
},
|
||||
"HttpConfig": {
|
||||
"Address": "0.0.0.0:$NETBIRD_MGMT_API_PORT",
|
||||
"AuthIssuer": "$NETBIRD_AUTH_AUTHORITY",
|
||||
@@ -39,7 +35,6 @@
|
||||
"AuthUserIDClaim": "$NETBIRD_AUTH_USER_ID_CLAIM",
|
||||
"CertFile":"$NETBIRD_MGMT_API_CERT_FILE",
|
||||
"CertKey":"$NETBIRD_MGMT_API_CERT_KEY_FILE",
|
||||
"IdpSignKeyRefreshEnabled": $NETBIRD_MGMT_IDP_SIGNKEY_REFRESH,
|
||||
"OIDCConfigEndpoint":"$NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT"
|
||||
},
|
||||
"IdpManagerConfig": {
|
||||
@@ -51,25 +46,18 @@
|
||||
"ClientSecret": "$NETBIRD_IDP_MGMT_CLIENT_SECRET",
|
||||
"GrantType": "client_credentials"
|
||||
},
|
||||
"ExtraConfig": $NETBIRD_IDP_MGMT_EXTRA_CONFIG,
|
||||
"Auth0ClientCredentials": null,
|
||||
"AzureClientCredentials": null,
|
||||
"KeycloakClientCredentials": null,
|
||||
"ZitadelClientCredentials": null
|
||||
"ExtraConfig": $NETBIRD_IDP_MGMT_EXTRA_CONFIG
|
||||
},
|
||||
"DeviceAuthorizationFlow": {
|
||||
"Provider": "$NETBIRD_AUTH_DEVICE_AUTH_PROVIDER",
|
||||
"ProviderConfig": {
|
||||
"Audience": "$NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE",
|
||||
"AuthorizationEndpoint": "",
|
||||
"Domain": "$NETBIRD_AUTH0_DOMAIN",
|
||||
"ClientID": "$NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID",
|
||||
"ClientSecret": "",
|
||||
"TokenEndpoint": "$NETBIRD_AUTH_TOKEN_ENDPOINT",
|
||||
"DeviceAuthEndpoint": "$NETBIRD_AUTH_DEVICE_AUTH_ENDPOINT",
|
||||
"Scope": "$NETBIRD_AUTH_DEVICE_AUTH_SCOPE",
|
||||
"UseIDToken": $NETBIRD_AUTH_DEVICE_AUTH_USE_ID_TOKEN,
|
||||
"RedirectURLs": null
|
||||
"UseIDToken": $NETBIRD_AUTH_DEVICE_AUTH_USE_ID_TOKEN
|
||||
}
|
||||
},
|
||||
"PKCEAuthorizationFlow": {
|
||||
@@ -77,7 +65,6 @@
|
||||
"Audience": "$NETBIRD_AUTH_PKCE_AUDIENCE",
|
||||
"ClientID": "$NETBIRD_AUTH_CLIENT_ID",
|
||||
"ClientSecret": "$NETBIRD_AUTH_CLIENT_SECRET",
|
||||
"Domain": "",
|
||||
"AuthorizationEndpoint": "$NETBIRD_AUTH_PKCE_AUTHORIZATION_ENDPOINT",
|
||||
"TokenEndpoint": "$NETBIRD_AUTH_TOKEN_ENDPOINT",
|
||||
"Scope": "$NETBIRD_AUTH_SUPPORTED_SCOPES",
|
||||
|
||||