merge main
15
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
FROM golang:1.20-bullseye
|
||||||
|
|
||||||
|
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||||
|
&& apt-get -y install --no-install-recommends\
|
||||||
|
gettext-base=0.21-4 \
|
||||||
|
iptables=1.8.7-1 \
|
||||||
|
libgl1-mesa-dev=20.3.5-1 \
|
||||||
|
xorg-dev=1:7.7+22 \
|
||||||
|
libayatana-appindicator3-dev=0.5.5-2+deb11u2 \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& go install -v golang.org/x/tools/gopls@latest
|
||||||
|
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
20
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "NetBird",
|
||||||
|
"build": {
|
||||||
|
"context": "..",
|
||||||
|
"dockerfile": "Dockerfile"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
|
||||||
|
"ghcr.io/devcontainers/features/go:1": {
|
||||||
|
"version": "1.20"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
"capAdd": [
|
||||||
|
"NET_ADMIN",
|
||||||
|
"SYS_ADMIN",
|
||||||
|
"SYS_RESOURCE"
|
||||||
|
],
|
||||||
|
"privileged": true
|
||||||
|
}
|
||||||
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.go text eol=lf
|
||||||
5
.github/workflows/golang-test-darwin.yml
vendored
@@ -12,6 +12,9 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
store: ['jsonfile', 'sqlite']
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
@@ -33,4 +36,4 @@ jobs:
|
|||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./...
|
run: NETBIRD_STORE_ENGINE=${{ matrix.store }} go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./...
|
||||||
|
|||||||
18
.github/workflows/golang-test-linux.yml
vendored
@@ -15,6 +15,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
arch: ['386','amd64']
|
arch: ['386','amd64']
|
||||||
|
store: ['jsonfile', 'sqlite']
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
@@ -41,17 +42,16 @@ jobs:
|
|||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./...
|
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./...
|
||||||
|
|
||||||
test_client_on_docker:
|
test_client_on_docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20.x"
|
go-version: "1.20.x"
|
||||||
|
|
||||||
|
|
||||||
- name: Cache Go modules
|
- name: Cache Go modules
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
@@ -64,7 +64,7 @@ jobs:
|
|||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
|
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev gcc-multilib
|
||||||
|
|
||||||
- name: Install modules
|
- name: Install modules
|
||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
run: CGO_ENABLED=0 go test -c -o nftablesmanager-testing.bin ./client/firewall/nftables/...
|
run: CGO_ENABLED=0 go test -c -o nftablesmanager-testing.bin ./client/firewall/nftables/...
|
||||||
|
|
||||||
- name: Generate Engine Test bin
|
- name: Generate Engine Test bin
|
||||||
run: CGO_ENABLED=0 go test -c -o engine-testing.bin ./client/internal
|
run: CGO_ENABLED=1 go test -c -o engine-testing.bin ./client/internal
|
||||||
|
|
||||||
- name: Generate Peer Test bin
|
- name: Generate Peer Test bin
|
||||||
run: CGO_ENABLED=0 go test -c -o peer-testing.bin ./client/internal/peer/...
|
run: CGO_ENABLED=0 go test -c -o peer-testing.bin ./client/internal/peer/...
|
||||||
@@ -95,15 +95,17 @@ jobs:
|
|||||||
- name: Run Iface tests in docker
|
- 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
|
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
|
- 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
|
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
|
- 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
|
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
|
- 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 --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/engine-testing.bin -test.timeout 5m -test.parallel 1
|
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 Peer tests in docker
|
- 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
|
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,7 +39,9 @@ jobs:
|
|||||||
|
|
||||||
- run: mv ${{ env.downloadPath }}/wintun/bin/amd64/wintun.dll 'C:\Windows\System32\'
|
- run: mv ${{ env.downloadPath }}/wintun/bin/amd64/wintun.dll 'C:\Windows\System32\'
|
||||||
|
|
||||||
- run: choco install -y sysinternals
|
- run: choco install -y sysinternals --ignore-checksums
|
||||||
|
- run: choco install -y mingw
|
||||||
|
|
||||||
- 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 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
|
- 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
|
||||||
|
|
||||||
|
|||||||
18
.github/workflows/golangci-lint.yml
vendored
@@ -1,12 +1,23 @@
|
|||||||
name: golangci-lint
|
name: golangci-lint
|
||||||
on: [pull_request]
|
on: [pull_request]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }}
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
golangci:
|
golangci:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||||
name: lint
|
name: lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.os }}
|
||||||
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -14,7 +25,12 @@ jobs:
|
|||||||
uses: actions/setup-go@v4
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "1.20.x"
|
go-version: "1.20.x"
|
||||||
|
cache: false
|
||||||
- name: Install dependencies
|
- 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
|
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v3
|
uses: golangci/golangci-lint-action@v3
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
args: --timeout=12m
|
||||||
1
.github/workflows/release.yml
vendored
@@ -17,6 +17,7 @@ on:
|
|||||||
- 'release_files/**'
|
- 'release_files/**'
|
||||||
- '**/Dockerfile'
|
- '**/Dockerfile'
|
||||||
- '**/Dockerfile.*'
|
- '**/Dockerfile.*'
|
||||||
|
- 'client/ui/**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
SIGN_PIPE_VER: "v0.0.9"
|
SIGN_PIPE_VER: "v0.0.9"
|
||||||
|
|||||||
23
.github/workflows/test-infrastructure-files.yml
vendored
@@ -8,6 +8,8 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- 'infrastructure_files/**'
|
- 'infrastructure_files/**'
|
||||||
- '.github/workflows/test-infrastructure-files.yml'
|
- '.github/workflows/test-infrastructure-files.yml'
|
||||||
|
- 'management/cmd/**'
|
||||||
|
- 'signal/cmd/**'
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }}
|
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.actor_id }}
|
||||||
@@ -56,6 +58,8 @@ jobs:
|
|||||||
CI_NETBIRD_IDP_MGMT_CLIENT_ID: testing.client.id
|
CI_NETBIRD_IDP_MGMT_CLIENT_ID: testing.client.id
|
||||||
CI_NETBIRD_IDP_MGMT_CLIENT_SECRET: testing.client.secret
|
CI_NETBIRD_IDP_MGMT_CLIENT_SECRET: testing.client.secret
|
||||||
CI_NETBIRD_AUTH_SUPPORTED_SCOPES: "openid profile email offline_access api email_verified"
|
CI_NETBIRD_AUTH_SUPPORTED_SCOPES: "openid profile email offline_access api email_verified"
|
||||||
|
CI_NETBIRD_STORE_CONFIG_ENGINE: "sqlite"
|
||||||
|
CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false
|
||||||
|
|
||||||
- name: check values
|
- name: check values
|
||||||
working-directory: infrastructure_files
|
working-directory: infrastructure_files
|
||||||
@@ -81,6 +85,8 @@ jobs:
|
|||||||
CI_NETBIRD_IDP_MGMT_CLIENT_ID: testing.client.id
|
CI_NETBIRD_IDP_MGMT_CLIENT_ID: testing.client.id
|
||||||
CI_NETBIRD_IDP_MGMT_CLIENT_SECRET: testing.client.secret
|
CI_NETBIRD_IDP_MGMT_CLIENT_SECRET: testing.client.secret
|
||||||
CI_NETBIRD_SIGNAL_PORT: 12345
|
CI_NETBIRD_SIGNAL_PORT: 12345
|
||||||
|
CI_NETBIRD_STORE_CONFIG_ENGINE: "sqlite"
|
||||||
|
CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH: false
|
||||||
|
|
||||||
run: |
|
run: |
|
||||||
grep AUTH_CLIENT_ID docker-compose.yml | grep $CI_NETBIRD_AUTH_CLIENT_ID
|
grep AUTH_CLIENT_ID docker-compose.yml | grep $CI_NETBIRD_AUTH_CLIENT_ID
|
||||||
@@ -97,7 +103,9 @@ jobs:
|
|||||||
grep NETBIRD_TOKEN_SOURCE docker-compose.yml | grep $CI_NETBIRD_TOKEN_SOURCE
|
grep NETBIRD_TOKEN_SOURCE docker-compose.yml | grep $CI_NETBIRD_TOKEN_SOURCE
|
||||||
grep AuthUserIDClaim management.json | grep $CI_NETBIRD_AUTH_USER_ID_CLAIM
|
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 -A 8 DeviceAuthorizationFlow management.json | grep -A 6 ProviderConfig | grep Scope | grep "$CI_NETBIRD_AUTH_DEVICE_AUTH_SCOPE"
|
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 UseIDToken management.json | grep false
|
grep UseIDToken management.json | grep false
|
||||||
grep -A 1 IdpManagerConfig management.json | grep ManagerType | grep $CI_NETBIRD_MGMT_IDP
|
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
|
grep -A 3 IdpManagerConfig management.json | grep -A 1 ClientConfig | grep Issuer | grep $CI_NETBIRD_AUTH_AUTHORITY
|
||||||
@@ -105,12 +113,13 @@ jobs:
|
|||||||
grep -A 5 IdpManagerConfig management.json | grep -A 3 ClientConfig | grep ClientID | grep $CI_NETBIRD_IDP_MGMT_CLIENT_ID
|
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 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 7 IdpManagerConfig management.json | grep -A 5 ClientConfig | grep GrantType | grep client_credentials
|
||||||
grep -A 2 PKCEAuthorizationFlow management.json | grep -A 1 ProviderConfig | grep Audience | grep $CI_NETBIRD_AUTH_AUDIENCE
|
grep -A 10 PKCEAuthorizationFlow management.json | grep -A 10 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 10 PKCEAuthorizationFlow management.json | grep -A 10 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 10 PKCEAuthorizationFlow management.json | grep -A 10 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 10 PKCEAuthorizationFlow management.json | grep -A 10 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 10 PKCEAuthorizationFlow management.json | grep -A 10 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"
|
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"
|
||||||
|
|
||||||
- name: Install modules
|
- name: Install modules
|
||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
|
|||||||
1
.gitignore
vendored
@@ -20,3 +20,4 @@ infrastructure_files/setup.env
|
|||||||
infrastructure_files/setup-*.env
|
infrastructure_files/setup-*.env
|
||||||
.vscode
|
.vscode
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
*.db
|
||||||
@@ -54,7 +54,7 @@ nfpms:
|
|||||||
contents:
|
contents:
|
||||||
- src: client/ui/netbird.desktop
|
- src: client/ui/netbird.desktop
|
||||||
dst: /usr/share/applications/netbird.desktop
|
dst: /usr/share/applications/netbird.desktop
|
||||||
- src: client/ui/disconnected.png
|
- src: client/ui/netbird-systemtray-default.png
|
||||||
dst: /usr/share/pixmaps/netbird.png
|
dst: /usr/share/pixmaps/netbird.png
|
||||||
dependencies:
|
dependencies:
|
||||||
- netbird
|
- netbird
|
||||||
@@ -71,7 +71,7 @@ nfpms:
|
|||||||
contents:
|
contents:
|
||||||
- src: client/ui/netbird.desktop
|
- src: client/ui/netbird.desktop
|
||||||
dst: /usr/share/applications/netbird.desktop
|
dst: /usr/share/applications/netbird.desktop
|
||||||
- src: client/ui/disconnected.png
|
- src: client/ui/netbird-systemtray-default.png
|
||||||
dst: /usr/share/pixmaps/netbird.png
|
dst: /usr/share/pixmaps/netbird.png
|
||||||
dependencies:
|
dependencies:
|
||||||
- netbird
|
- netbird
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ If you haven't already, join our slack workspace [here](https://join.slack.com/t
|
|||||||
- [Test suite](#test-suite)
|
- [Test suite](#test-suite)
|
||||||
- [Checklist before submitting a PR](#checklist-before-submitting-a-pr)
|
- [Checklist before submitting a PR](#checklist-before-submitting-a-pr)
|
||||||
- [Other project repositories](#other-project-repositories)
|
- [Other project repositories](#other-project-repositories)
|
||||||
- [Checklist before submitting a new node](#checklist-before-submitting-a-new-node)
|
|
||||||
- [Contributor License Agreement](#contributor-license-agreement)
|
- [Contributor License Agreement](#contributor-license-agreement)
|
||||||
|
|
||||||
## Code of conduct
|
## Code of conduct
|
||||||
@@ -70,7 +69,7 @@ dependencies are installed. Here is a short guide on how that can be done.
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
#### Go 1.19
|
#### Go 1.21
|
||||||
|
|
||||||
Follow the installation guide from https://go.dev/
|
Follow the installation guide from https://go.dev/
|
||||||
|
|
||||||
@@ -139,15 +138,14 @@ checked out and set up:
|
|||||||
### Build and start
|
### Build and start
|
||||||
#### Client
|
#### 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:
|
To start NetBird, execute:
|
||||||
```
|
```
|
||||||
cd client
|
cd client
|
||||||
# bash wireguard_nt.sh # if windows
|
CGO_ENABLED=0 go build .
|
||||||
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:
|
To start NetBird the client in the foreground:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal"
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
"github.com/netbirdio/netbird/client/internal/dns"
|
"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/peer"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
|
||||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
"github.com/netbirdio/netbird/client/system"
|
"github.com/netbirdio/netbird/client/system"
|
||||||
"github.com/netbirdio/netbird/formatter"
|
"github.com/netbirdio/netbird/formatter"
|
||||||
@@ -31,9 +31,9 @@ type IFaceDiscover interface {
|
|||||||
stdnet.ExternalIFaceDiscover
|
stdnet.ExternalIFaceDiscover
|
||||||
}
|
}
|
||||||
|
|
||||||
// RouteListener export internal RouteListener for mobile
|
// NetworkChangeListener export internal NetworkChangeListener for mobile
|
||||||
type RouteListener interface {
|
type NetworkChangeListener interface {
|
||||||
routemanager.RouteListener
|
listener.NetworkChangeListener
|
||||||
}
|
}
|
||||||
|
|
||||||
// DnsReadyListener export internal dns ReadyListener for mobile
|
// DnsReadyListener export internal dns ReadyListener for mobile
|
||||||
@@ -47,26 +47,26 @@ func init() {
|
|||||||
|
|
||||||
// Client struct manage the life circle of background service
|
// Client struct manage the life circle of background service
|
||||||
type Client struct {
|
type Client struct {
|
||||||
cfgFile string
|
cfgFile string
|
||||||
tunAdapter iface.TunAdapter
|
tunAdapter iface.TunAdapter
|
||||||
iFaceDiscover IFaceDiscover
|
iFaceDiscover IFaceDiscover
|
||||||
recorder *peer.Status
|
recorder *peer.Status
|
||||||
ctxCancel context.CancelFunc
|
ctxCancel context.CancelFunc
|
||||||
ctxCancelLock *sync.Mutex
|
ctxCancelLock *sync.Mutex
|
||||||
deviceName string
|
deviceName string
|
||||||
routeListener routemanager.RouteListener
|
networkChangeListener listener.NetworkChangeListener
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient instantiate a new Client
|
// NewClient instantiate a new Client
|
||||||
func NewClient(cfgFile, deviceName string, tunAdapter TunAdapter, iFaceDiscover IFaceDiscover, routeListener RouteListener) *Client {
|
func NewClient(cfgFile, deviceName string, tunAdapter TunAdapter, iFaceDiscover IFaceDiscover, networkChangeListener NetworkChangeListener) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
cfgFile: cfgFile,
|
cfgFile: cfgFile,
|
||||||
deviceName: deviceName,
|
deviceName: deviceName,
|
||||||
tunAdapter: tunAdapter,
|
tunAdapter: tunAdapter,
|
||||||
iFaceDiscover: iFaceDiscover,
|
iFaceDiscover: iFaceDiscover,
|
||||||
recorder: peer.NewRecorder(""),
|
recorder: peer.NewRecorder(""),
|
||||||
ctxCancelLock: &sync.Mutex{},
|
ctxCancelLock: &sync.Mutex{},
|
||||||
routeListener: routeListener,
|
networkChangeListener: networkChangeListener,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsRead
|
|||||||
|
|
||||||
// todo do not throw error in case of cancelled context
|
// todo do not throw error in case of cancelled context
|
||||||
ctx = internal.CtxInitState(ctx)
|
ctx = internal.CtxInitState(ctx)
|
||||||
return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.routeListener, dns.items, dnsReadyListener)
|
return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunWithoutLogin we apply this type of run function when the backed has been started without UI (i.e. after reboot).
|
// 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
|
// todo do not throw error in case of cancelled context
|
||||||
ctx = internal.CtxInitState(ctx)
|
ctx = internal.CtxInitState(ctx)
|
||||||
return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.routeListener, dns.items, dnsReadyListener)
|
return internal.RunClientMobile(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.networkChangeListener, dns.items, dnsReadyListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the internal client and free the resources
|
// Stop the internal client and free the resources
|
||||||
|
|||||||
@@ -57,11 +57,11 @@ func TestPreferences_ReadUncommitedValues(t *testing.T) {
|
|||||||
p.SetManagementURL(exampleString)
|
p.SetManagementURL(exampleString)
|
||||||
resp, err = p.GetManagementURL()
|
resp, err = p.GetManagementURL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to read managmenet url: %s", err)
|
t.Fatalf("failed to read management url: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp != exampleString {
|
if resp != exampleString {
|
||||||
t.Errorf("unexpected managemenet url: %s", resp)
|
t.Errorf("unexpected management url: %s", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.SetPreSharedKey(exampleString)
|
p.SetPreSharedKey(exampleString)
|
||||||
@@ -102,11 +102,11 @@ func TestPreferences_Commit(t *testing.T) {
|
|||||||
|
|
||||||
resp, err = p.GetManagementURL()
|
resp, err = p.GetManagementURL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to read managmenet url: %s", err)
|
t.Fatalf("failed to read management url: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp != exampleURL {
|
if resp != exampleURL {
|
||||||
t.Errorf("unexpected managemenet url: %s", resp)
|
t.Errorf("unexpected management url: %s", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = p.GetPreSharedKey()
|
resp, err = p.GetPreSharedKey()
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func startManagement(t *testing.T, config *mgmt.Config) (*grpc.Server, net.Liste
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
s := grpc.NewServer()
|
s := grpc.NewServer()
|
||||||
store, err := mgmt.NewFileStore(config.Datadir, nil)
|
store, err := mgmt.NewStoreFromJson(config.Datadir, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
defer func() {
|
defer func() {
|
||||||
err := conn.Close()
|
err := conn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("failed closing dameon gRPC client connection %v", err)
|
log.Warnf("failed closing daemon gRPC client connection %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -200,11 +200,11 @@ func validateNATExternalIPs(list []string) error {
|
|||||||
|
|
||||||
subElements := strings.Split(element, "/")
|
subElements := strings.Split(element, "/")
|
||||||
if len(subElements) > 2 {
|
if len(subElements) > 2 {
|
||||||
return fmt.Errorf("%s is not a valid input for %s. it should be formated as \"String\" or \"String/String\"", element, externalIPMapFlag)
|
return fmt.Errorf("%s is not a valid input for %s. it should be formatted as \"String\" or \"String/String\"", element, externalIPMapFlag)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(subElements) == 1 && !isValidIP(subElements[0]) {
|
if len(subElements) == 1 && !isValidIP(subElements[0]) {
|
||||||
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)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
last := 0
|
last := 0
|
||||||
@@ -259,7 +259,7 @@ func parseCustomDNSAddress(modified bool) ([]byte, error) {
|
|||||||
var parsed []byte
|
var parsed []byte
|
||||||
if modified {
|
if modified {
|
||||||
if !isValidAddrPort(customDNSAddress) {
|
if !isValidAddrPort(customDNSAddress) {
|
||||||
return nil, fmt.Errorf("%s is invalid, it should be formated as IP:Port string or as an empty string like \"\"", customDNSAddress)
|
return nil, fmt.Errorf("%s is invalid, it should be formatted as IP:Port string or as an empty string like \"\"", customDNSAddress)
|
||||||
}
|
}
|
||||||
if customDNSAddress == "" && logFile != "console" {
|
if customDNSAddress == "" && logFile != "console" {
|
||||||
parsed = []byte("empty")
|
parsed = []byte("empty")
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ func (m *Manager) AddFiltering(
|
|||||||
}
|
}
|
||||||
if ipsetName != "" {
|
if ipsetName != "" {
|
||||||
// ipset name is defined and it means that this rule was created
|
// ipset name is defined and it means that this rule was created
|
||||||
// for it, need to assosiate it with ruleset
|
// for it, need to associate it with ruleset
|
||||||
m.rulesets[ipsetName] = ruleset{
|
m.rulesets[ipsetName] = ruleset{
|
||||||
rule: rule,
|
rule: rule,
|
||||||
ips: map[string]string{rule.ip: ruleID},
|
ips: map[string]string{rule.ip: ruleID},
|
||||||
@@ -236,7 +236,7 @@ func (m *Manager) DeleteRule(rule fw.Rule) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we delete last IP from the set, that means we need to delete
|
// we delete last IP from the set, that means we need to delete
|
||||||
// set itself and assosiated firewall rule too
|
// set itself and associated firewall rule too
|
||||||
delete(m.rulesets, r.ipsetName)
|
delete(m.rulesets, r.ipsetName)
|
||||||
|
|
||||||
if err := ipset.Destroy(r.ipsetName); err != nil {
|
if err := ipset.Destroy(r.ipsetName); err != nil {
|
||||||
|
|||||||
@@ -754,7 +754,7 @@ func (m *Manager) AllowNetbird() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if chain == nil {
|
if chain == nil {
|
||||||
log.Debugf("chain INPUT not found. Skiping add allow netbird rule")
|
log.Debugf("chain INPUT not found. Skipping add allow netbird rule")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ func TestNftablesManager(t *testing.T) {
|
|||||||
// test expectations:
|
// test expectations:
|
||||||
// 1) "accept extra routed traffic rule" for the interface
|
// 1) "accept extra routed traffic rule" for the interface
|
||||||
// 2) "drop all rule" for the interface
|
// 2) "drop all rule" for the interface
|
||||||
require.Len(t, rules, 2, "expected 2 rules after deleteion")
|
require.Len(t, rules, 2, "expected 2 rules after deletion")
|
||||||
|
|
||||||
err = manager.Reset()
|
err = manager.Reset()
|
||||||
require.NoError(t, err, "failed to reset")
|
require.NoError(t, err, "failed to reset")
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
package uspfilter
|
package uspfilter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type action string
|
type action string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
addRule action = "add"
|
addRule action = "add"
|
||||||
deleteRule action = "delete"
|
deleteRule action = "delete"
|
||||||
|
firewallRuleName = "Netbird"
|
||||||
firewallRuleName = "Netbird"
|
|
||||||
noRulesMatchCriteria = "No rules match the specified criteria"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reset firewall to the default state
|
// Reset firewall to the default state
|
||||||
@@ -26,6 +24,14 @@ func (m *Manager) Reset() error {
|
|||||||
m.outgoingRules = make(map[string]RuleSet)
|
m.outgoingRules = make(map[string]RuleSet)
|
||||||
m.incomingRules = 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 {
|
if err := manageFirewallRule(firewallRuleName, deleteRule); err != nil {
|
||||||
return fmt.Errorf("couldn't remove windows firewall: %w", err)
|
return fmt.Errorf("couldn't remove windows firewall: %w", err)
|
||||||
}
|
}
|
||||||
@@ -35,6 +41,13 @@ func (m *Manager) Reset() error {
|
|||||||
|
|
||||||
// AllowNetbird allows netbird interface traffic
|
// AllowNetbird allows netbird interface traffic
|
||||||
func (m *Manager) AllowNetbird() error {
|
func (m *Manager) AllowNetbird() error {
|
||||||
|
if !isWindowsFirewallReachable() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFirewallRuleActive(firewallRuleName) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return manageFirewallRule(firewallRuleName,
|
return manageFirewallRule(firewallRuleName,
|
||||||
addRule,
|
addRule,
|
||||||
"dir=in",
|
"dir=in",
|
||||||
@@ -45,47 +58,37 @@ func (m *Manager) AllowNetbird() error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func manageFirewallRule(ruleName string, action action, args ...string) error {
|
func manageFirewallRule(ruleName string, action action, extraArgs ...string) error {
|
||||||
active, err := isFirewallRuleActive(ruleName)
|
|
||||||
if err != nil {
|
args := []string{"advfirewall", "firewall", string(action), "rule", "name=" + ruleName}
|
||||||
return err
|
if action == addRule {
|
||||||
|
args = append(args, extraArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action == addRule && !active) || (action == deleteRule && active) {
|
cmd := exec.Command("netsh", args...)
|
||||||
baseArgs := []string{"advfirewall", "firewall", string(action), "rule", "name=" + ruleName}
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
args := append(baseArgs, args...)
|
return cmd.Run()
|
||||||
|
|
||||||
cmd := exec.Command("netsh", args...)
|
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
|
||||||
return cmd.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isFirewallRuleActive(ruleName string) (bool, error) {
|
func isWindowsFirewallReachable() bool {
|
||||||
|
args := []string{"advfirewall", "show", "allprofiles", "state"}
|
||||||
|
cmd := exec.Command("netsh", args...)
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
|
|
||||||
|
_, err := cmd.Output()
|
||||||
|
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 true
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFirewallRuleActive(ruleName string) bool {
|
||||||
args := []string{"advfirewall", "firewall", "show", "rule", "name=" + ruleName}
|
args := []string{"advfirewall", "firewall", "show", "rule", "name=" + ruleName}
|
||||||
|
|
||||||
cmd := exec.Command("netsh", args...)
|
cmd := exec.Command("netsh", args...)
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||||
output, err := cmd.Output()
|
_, err := cmd.Output()
|
||||||
if err != nil {
|
return 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
|
wgNetwork *net.IPNet
|
||||||
decoders sync.Pool
|
decoders sync.Pool
|
||||||
wgIface IFaceMapper
|
wgIface IFaceMapper
|
||||||
resetHook func() error
|
resetHook func() error
|
||||||
|
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ func (m *Manager) DropIncoming(packetData []byte) bool {
|
|||||||
return m.dropFilter(packetData, m.incomingRules, true)
|
return m.dropFilter(packetData, m.incomingRules, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dropFilter imlements same logic for booth direction of the traffic
|
// dropFilter implements same logic for booth direction of the traffic
|
||||||
func (m *Manager) dropFilter(packetData []byte, rules map[string]RuleSet, isIncomingPacket bool) bool {
|
func (m *Manager) dropFilter(packetData []byte, rules map[string]RuleSet, isIncomingPacket bool) bool {
|
||||||
m.mutex.RLock()
|
m.mutex.RLock()
|
||||||
defer m.mutex.RUnlock()
|
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.
|
// ApplyFiltering firewall rules to the local firewall manager processed by ACL policy.
|
||||||
//
|
//
|
||||||
// If allowByDefault is ture it appends allow ALL traffic rules to input and output chains.
|
// If allowByDefault is true it appends allow ALL traffic rules to input and output chains.
|
||||||
func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
|
func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
|
||||||
d.mutex.Lock()
|
d.mutex.Lock()
|
||||||
defer d.mutex.Unlock()
|
defer d.mutex.Unlock()
|
||||||
@@ -366,7 +366,7 @@ func (d *DefaultManager) squashAcceptRules(
|
|||||||
protocols[r.Protocol] = map[string]int{}
|
protocols[r.Protocol] = map[string]int{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// special case, when we recieve this all network IP address
|
// special case, when we receive this all network IP address
|
||||||
// it means that rules for that protocol was already optimized on the
|
// it means that rules for that protocol was already optimized on the
|
||||||
// management side
|
// management side
|
||||||
if r.PeerIP == "0.0.0.0" {
|
if r.PeerIP == "0.0.0.0" {
|
||||||
@@ -393,7 +393,7 @@ func (d *DefaultManager) squashAcceptRules(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// order of squashing by protocol is important
|
// order of squashing by protocol is important
|
||||||
// only for ther first element ALL, it must be done first
|
// only for their first element ALL, it must be done first
|
||||||
protocolOrders := []mgmProto.FirewallRuleProtocol{
|
protocolOrders := []mgmProto.FirewallRuleProtocol{
|
||||||
mgmProto.FirewallRule_ALL,
|
mgmProto.FirewallRule_ALL,
|
||||||
mgmProto.FirewallRule_ICMP,
|
mgmProto.FirewallRule_ICMP,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func Create(iface IFaceMapper) (manager *DefaultManager, err error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := fm.AllowNetbird(); err != nil {
|
if err := fm.AllowNetbird(); err != nil {
|
||||||
log.Errorf("failed to allow netbird interface traffic: %v", err)
|
log.Warnf("failed to allow netbird interface traffic: %v", err)
|
||||||
}
|
}
|
||||||
return newDefaultManager(fm), nil
|
return newDefaultManager(fm), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import (
|
|||||||
gstatus "google.golang.org/grpc/status"
|
gstatus "google.golang.org/grpc/status"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/dns"
|
"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/peer"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
|
||||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
"github.com/netbirdio/netbird/client/ssh"
|
"github.com/netbirdio/netbird/client/ssh"
|
||||||
"github.com/netbirdio/netbird/client/system"
|
"github.com/netbirdio/netbird/client/system"
|
||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
mgm "github.com/netbirdio/netbird/management/client"
|
mgm "github.com/netbirdio/netbird/management/client"
|
||||||
mgmProto "github.com/netbirdio/netbird/management/proto"
|
mgmProto "github.com/netbirdio/netbird/management/proto"
|
||||||
signal "github.com/netbirdio/netbird/signal/client"
|
signal "github.com/netbirdio/netbird/signal/client"
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunClient with main logic.
|
// RunClient with main logic.
|
||||||
@@ -30,14 +31,14 @@ func RunClient(ctx context.Context, config *Config, statusRecorder *peer.Status)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RunClientMobile with main logic on mobile system
|
// RunClientMobile with main logic on mobile system
|
||||||
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 {
|
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 {
|
||||||
// in case of non Android os these variables will be nil
|
// in case of non Android os these variables will be nil
|
||||||
mobileDependency := MobileDependency{
|
mobileDependency := MobileDependency{
|
||||||
TunAdapter: tunAdapter,
|
TunAdapter: tunAdapter,
|
||||||
IFaceDiscover: iFaceDiscover,
|
IFaceDiscover: iFaceDiscover,
|
||||||
RouteListener: routeListener,
|
NetworkChangeListener: networkChangeListener,
|
||||||
HostDNSAddresses: dnsAddresses,
|
HostDNSAddresses: dnsAddresses,
|
||||||
DnsReadyListener: dnsReadyListener,
|
DnsReadyListener: dnsReadyListener,
|
||||||
}
|
}
|
||||||
return runClient(ctx, config, statusRecorder, mobileDependency)
|
return runClient(ctx, config, statusRecorder, mobileDependency)
|
||||||
}
|
}
|
||||||
@@ -53,6 +54,8 @@ func RunClientiOS(ctx context.Context, config *Config, statusRecorder *peer.Stat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runClient(ctx context.Context, config *Config, statusRecorder *peer.Status, mobileDependency MobileDependency) error {
|
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{
|
backOff := &backoff.ExponentialBackOff{
|
||||||
InitialInterval: time.Second,
|
InitialInterval: time.Second,
|
||||||
RandomizationFactor: 1,
|
RandomizationFactor: 1,
|
||||||
@@ -106,7 +109,7 @@ func runClient(ctx context.Context, config *Config, statusRecorder *peer.Status,
|
|||||||
cancel()
|
cancel()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.Debugf("conecting to the Management service %s", config.ManagementURL.Host)
|
log.Debugf("connecting to the Management service %s", config.ManagementURL.Host)
|
||||||
mgmClient, err := mgm.NewClient(engineCtx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
|
mgmClient, err := mgm.NewClient(engineCtx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wrapErr(gstatus.Errorf(codes.FailedPrecondition, "failed connecting to Management Service : %s", err))
|
return wrapErr(gstatus.Errorf(codes.FailedPrecondition, "failed connecting to Management Service : %s", err))
|
||||||
|
|||||||
@@ -3,29 +3,25 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
fileGeneratedResolvConfContentHeader = "# Generated by NetBird"
|
fileGeneratedResolvConfContentHeader = "# Generated by NetBird"
|
||||||
fileGeneratedResolvConfSearchBeginContent = "search "
|
fileGeneratedResolvConfContentHeaderNextLine = fileGeneratedResolvConfContentHeader + `
|
||||||
fileGeneratedResolvConfContentFormat = fileGeneratedResolvConfContentHeader +
|
# If needed you can restore the original file by copying back ` + fileDefaultResolvConfBackupLocation + "\n\n"
|
||||||
"\n# If needed you can restore the original file by copying back %s\n\nnameserver %s\n" +
|
|
||||||
fileGeneratedResolvConfSearchBeginContent + "%s\n\n" +
|
|
||||||
"%s\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
fileDefaultResolvConfBackupLocation = defaultResolvConfPath + ".original.netbird"
|
fileDefaultResolvConfBackupLocation = defaultResolvConfPath + ".original.netbird"
|
||||||
fileMaxLineCharsLimit = 256
|
|
||||||
fileMaxNumberOfSearchDomains = 6
|
|
||||||
)
|
|
||||||
|
|
||||||
var fileSearchLineBeginCharCount = len(fileGeneratedResolvConfSearchBeginContent)
|
fileMaxLineCharsLimit = 256
|
||||||
|
fileMaxNumberOfSearchDomains = 6
|
||||||
|
)
|
||||||
|
|
||||||
type fileConfigurator struct {
|
type fileConfigurator struct {
|
||||||
originalPerms os.FileMode
|
originalPerms os.FileMode
|
||||||
@@ -55,58 +51,39 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
}
|
}
|
||||||
return fmt.Errorf("unable to configure DNS for this peer using file manager without a nameserver group with all domains configured")
|
return fmt.Errorf("unable to configure DNS for this peer using file manager without a nameserver group with all domains configured")
|
||||||
}
|
}
|
||||||
managerType, err := getOSDNSManagerType()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch managerType {
|
|
||||||
case fileManager, netbirdManager:
|
|
||||||
if !backupFileExist {
|
|
||||||
err = f.backup()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to backup the resolv.conf file")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// todo improve this and maybe restart DNS manager from scratch
|
|
||||||
return fmt.Errorf("something happened and file manager is not your prefered host dns configurator, restart the agent")
|
|
||||||
}
|
|
||||||
|
|
||||||
var searchDomains string
|
if !backupFileExist {
|
||||||
appendedDomains := 0
|
err = f.backup()
|
||||||
for _, dConf := range config.domains {
|
|
||||||
if dConf.matchOnly || dConf.disabled {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if appendedDomains >= fileMaxNumberOfSearchDomains {
|
|
||||||
// lets log all skipped domains
|
|
||||||
log.Infof("already appended %d domains to search list. Skipping append of %s domain", fileMaxNumberOfSearchDomains, dConf.domain)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fileSearchLineBeginCharCount+len(searchDomains) > fileMaxLineCharsLimit {
|
|
||||||
// lets log all skipped domains
|
|
||||||
log.Infof("search list line is larger than %d characters. Skipping append of %s domain", fileMaxLineCharsLimit, dConf.domain)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
searchDomains += " " + dConf.domain
|
|
||||||
appendedDomains++
|
|
||||||
}
|
|
||||||
|
|
||||||
originalContent, err := os.ReadFile(fileDefaultResolvConfBackupLocation)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Could not read existing resolv.conf")
|
|
||||||
}
|
|
||||||
content := fmt.Sprintf(fileGeneratedResolvConfContentFormat, fileDefaultResolvConfBackupLocation, config.serverIP, searchDomains, string(originalContent))
|
|
||||||
err = writeDNSConfig(content, defaultResolvConfPath, f.originalPerms)
|
|
||||||
if err != nil {
|
|
||||||
err = f.restore()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to backup the resolv.conf file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
searchDomainList := searchDomains(config)
|
||||||
|
|
||||||
|
originalSearchDomains, nameServers, others, err := originalDNSConfigs(fileDefaultResolvConfBackupLocation)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
searchDomainList = mergeSearchDomains(searchDomainList, originalSearchDomains)
|
||||||
|
|
||||||
|
buf := prepareResolvConfContent(
|
||||||
|
searchDomainList,
|
||||||
|
append([]string{config.serverIP}, nameServers...),
|
||||||
|
others)
|
||||||
|
|
||||||
|
log.Debugf("creating managed file %s", defaultResolvConfPath)
|
||||||
|
err = os.WriteFile(defaultResolvConfPath, buf.Bytes(), f.originalPerms)
|
||||||
|
if err != nil {
|
||||||
|
restoreErr := f.restore()
|
||||||
|
if restoreErr != nil {
|
||||||
log.Errorf("attempt to restore default file failed with error: %s", err)
|
log.Errorf("attempt to restore default file failed with error: %s", err)
|
||||||
}
|
}
|
||||||
return err
|
return fmt.Errorf("got an creating resolver file %s. Error: %s", defaultResolvConfPath, err)
|
||||||
}
|
}
|
||||||
log.Infof("created a NetBird managed %s file with your DNS settings. Added %d search domains. Search list: %s", defaultResolvConfPath, appendedDomains, searchDomains)
|
|
||||||
|
log.Infof("created a NetBird managed %s file with your DNS settings. Added %d search domains. Search list: %s", defaultResolvConfPath, len(searchDomainList), searchDomainList)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,15 +115,138 @@ func (f *fileConfigurator) restore() error {
|
|||||||
return os.RemoveAll(fileDefaultResolvConfBackupLocation)
|
return os.RemoveAll(fileDefaultResolvConfBackupLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeDNSConfig(content, fileName string, permissions os.FileMode) error {
|
func prepareResolvConfContent(searchDomains, nameServers, others []string) bytes.Buffer {
|
||||||
log.Debugf("creating managed file %s", fileName)
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteString(content)
|
buf.WriteString(fileGeneratedResolvConfContentHeaderNextLine)
|
||||||
err := os.WriteFile(fileName, buf.Bytes(), permissions)
|
|
||||||
if err != nil {
|
for _, cfgLine := range others {
|
||||||
return fmt.Errorf("got an creating resolver file %s. Error: %s", fileName, err)
|
buf.WriteString(cfgLine)
|
||||||
|
buf.WriteString("\n")
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
if len(searchDomains) > 0 {
|
||||||
|
buf.WriteString("search ")
|
||||||
|
buf.WriteString(strings.Join(searchDomains, " "))
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ns := range nameServers {
|
||||||
|
buf.WriteString("nameserver ")
|
||||||
|
buf.WriteString(ns)
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchDomains(config hostDNSConfig) []string {
|
||||||
|
listOfDomains := make([]string, 0)
|
||||||
|
for _, dConf := range config.domains {
|
||||||
|
if dConf.matchOnly || dConf.disabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
listOfDomains = append(listOfDomains, dConf.domain)
|
||||||
|
}
|
||||||
|
return listOfDomains
|
||||||
|
}
|
||||||
|
|
||||||
|
func originalDNSConfigs(resolvconfFile string) (searchDomains, nameServers, others []string, err error) {
|
||||||
|
file, err := os.Open(resolvconfFile)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf(`could not read existing resolv.conf`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
reader := bufio.NewReader(file)
|
||||||
|
|
||||||
|
for {
|
||||||
|
lineBytes, isPrefix, readErr := reader.ReadLine()
|
||||||
|
if readErr != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if isPrefix {
|
||||||
|
err = fmt.Errorf(`resolv.conf line too long`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
line := strings.TrimSpace(string(lineBytes))
|
||||||
|
|
||||||
|
if strings.HasPrefix(line, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(line, "domain") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(line, "options") && strings.Contains(line, "rotate") {
|
||||||
|
line = strings.ReplaceAll(line, "rotate", "")
|
||||||
|
splitLines := strings.Fields(line)
|
||||||
|
if len(splitLines) == 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line = strings.Join(splitLines, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(line, "search") {
|
||||||
|
splitLines := strings.Fields(line)
|
||||||
|
if len(splitLines) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
searchDomains = splitLines[1:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(line, "nameserver") {
|
||||||
|
splitLines := strings.Fields(line)
|
||||||
|
if len(splitLines) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
nameServers = append(nameServers, splitLines[1])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
others = append(others, line)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge search domains lists and cut off the list if it is too long
|
||||||
|
func mergeSearchDomains(searchDomains []string, originalSearchDomains []string) []string {
|
||||||
|
lineSize := len("search")
|
||||||
|
searchDomainsList := make([]string, 0, len(searchDomains)+len(originalSearchDomains))
|
||||||
|
|
||||||
|
lineSize = validateAndFillSearchDomains(lineSize, &searchDomainsList, searchDomains)
|
||||||
|
_ = validateAndFillSearchDomains(lineSize, &searchDomainsList, originalSearchDomains)
|
||||||
|
|
||||||
|
return searchDomainsList
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateAndFillSearchDomains checks if the search domains list is not too long and if the line is not too long
|
||||||
|
// extend s slice with vs elements
|
||||||
|
// return with the number of characters in the searchDomains line
|
||||||
|
func validateAndFillSearchDomains(initialLineChars int, s *[]string, vs []string) int {
|
||||||
|
for _, sd := range vs {
|
||||||
|
tmpCharsNumber := initialLineChars + 1 + len(sd)
|
||||||
|
if tmpCharsNumber > fileMaxLineCharsLimit {
|
||||||
|
// lets log all skipped domains
|
||||||
|
log.Infof("search list line is larger than %d characters. Skipping append of %s domain", fileMaxLineCharsLimit, sd)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
initialLineChars = tmpCharsNumber
|
||||||
|
|
||||||
|
if len(*s) >= fileMaxNumberOfSearchDomains {
|
||||||
|
// lets log all skipped domains
|
||||||
|
log.Infof("already appended %d domains to search list. Skipping append of %s domain", fileMaxNumberOfSearchDomains, sd)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
*s = append(*s, sd)
|
||||||
|
}
|
||||||
|
return initialLineChars
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyFile(src, dest string) error {
|
func copyFile(src, dest string) error {
|
||||||
|
|||||||
62
client/internal/dns/file_linux_test.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_mergeSearchDomains(t *testing.T) {
|
||||||
|
searchDomains := []string{"a", "b"}
|
||||||
|
originDomains := []string{"a", "b"}
|
||||||
|
mergedDomains := mergeSearchDomains(searchDomains, originDomains)
|
||||||
|
if len(mergedDomains) != 4 {
|
||||||
|
t.Errorf("invalid len of result domains: %d, want: %d", len(mergedDomains), 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_mergeSearchTooMuchDomains(t *testing.T) {
|
||||||
|
searchDomains := []string{"a", "b", "c", "d", "e", "f", "g"}
|
||||||
|
originDomains := []string{"h", "i"}
|
||||||
|
mergedDomains := mergeSearchDomains(searchDomains, originDomains)
|
||||||
|
if len(mergedDomains) != 6 {
|
||||||
|
t.Errorf("invalid len of result domains: %d, want: %d", len(mergedDomains), 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_mergeSearchTooMuchDomainsInOrigin(t *testing.T) {
|
||||||
|
searchDomains := []string{"a", "b"}
|
||||||
|
originDomains := []string{"c", "d", "e", "f", "g"}
|
||||||
|
mergedDomains := mergeSearchDomains(searchDomains, originDomains)
|
||||||
|
if len(mergedDomains) != 6 {
|
||||||
|
t.Errorf("invalid len of result domains: %d, want: %d", len(mergedDomains), 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_mergeSearchTooLongDomain(t *testing.T) {
|
||||||
|
searchDomains := []string{getLongLine()}
|
||||||
|
originDomains := []string{"b"}
|
||||||
|
mergedDomains := mergeSearchDomains(searchDomains, originDomains)
|
||||||
|
if len(mergedDomains) != 1 {
|
||||||
|
t.Errorf("invalid len of result domains: %d, want: %d", len(mergedDomains), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
searchDomains = []string{"b"}
|
||||||
|
originDomains = []string{getLongLine()}
|
||||||
|
|
||||||
|
mergedDomains = mergeSearchDomains(searchDomains, originDomains)
|
||||||
|
if len(mergedDomains) != 1 {
|
||||||
|
t.Errorf("invalid len of result domains: %d, want: %d", len(mergedDomains), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLongLine() string {
|
||||||
|
x := "search "
|
||||||
|
for {
|
||||||
|
for i := 0; i <= 9; i++ {
|
||||||
|
if len(x) > fileMaxLineCharsLimit {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
x = fmt.Sprintf("%s%d", x, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,7 +78,7 @@ func dnsConfigToHostDNSConfig(dnsConfig nbdns.Config, ip string, port int) HostD
|
|||||||
for _, domain := range nsConfig.Domains {
|
for _, domain := range nsConfig.Domains {
|
||||||
config.domains = append(config.domains, domainConfig{
|
config.domains = append(config.domains, domainConfig{
|
||||||
domain: strings.TrimSuffix(domain, "."),
|
domain: strings.TrimSuffix(domain, "."),
|
||||||
matchOnly: true,
|
matchOnly: !nsConfig.SearchDomainsEnabled,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,11 @@ const (
|
|||||||
interfaceConfigPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"
|
interfaceConfigPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"
|
||||||
interfaceConfigNameServerKey = "NameServer"
|
interfaceConfigNameServerKey = "NameServer"
|
||||||
interfaceConfigSearchListKey = "SearchList"
|
interfaceConfigSearchListKey = "SearchList"
|
||||||
tcpipParametersPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type registryConfigurator struct {
|
type registryConfigurator struct {
|
||||||
guid string
|
guid string
|
||||||
routingAll bool
|
routingAll bool
|
||||||
existingSearchDomains []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHostManager(wgInterface WGIface) (hostManager, error) {
|
func newHostManager(wgInterface WGIface) (hostManager, error) {
|
||||||
@@ -148,30 +146,11 @@ func (r *registryConfigurator) restoreHostDNS() error {
|
|||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.updateSearchDomains([]string{})
|
return r.deleteInterfaceRegistryKeyProperty(interfaceConfigSearchListKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *registryConfigurator) updateSearchDomains(domains []string) error {
|
func (r *registryConfigurator) updateSearchDomains(domains []string) error {
|
||||||
value, err := getLocalMachineRegistryKeyStringValue(tcpipParametersPath, interfaceConfigSearchListKey)
|
err := r.setInterfaceRegistryKeyStringValue(interfaceConfigSearchListKey, strings.Join(domains, ","))
|
||||||
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("adding search domain failed with error: %s", err)
|
return fmt.Errorf("adding search domain failed with error: %s", err)
|
||||||
}
|
}
|
||||||
@@ -235,33 +214,3 @@ func removeRegistryKeyFromDNSPolicyConfig(regKeyPath string) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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,6 +2,7 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,3 +44,7 @@ func (m *MockServer) UpdateDNSServer(serial uint64, update nbdns.Config) error {
|
|||||||
}
|
}
|
||||||
return fmt.Errorf("method UpdateDNSServer is not implemented")
|
return fmt.Errorf("method UpdateDNSServer is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MockServer) SearchDomains() []string {
|
||||||
|
return make([]string, 0)
|
||||||
|
}
|
||||||
|
|||||||
57
client/internal/dns/notifier.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@@ -3,10 +3,9 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -15,11 +14,24 @@ const resolvconfCommand = "resolvconf"
|
|||||||
|
|
||||||
type resolvconf struct {
|
type resolvconf struct {
|
||||||
ifaceName string
|
ifaceName string
|
||||||
|
|
||||||
|
originalSearchDomains []string
|
||||||
|
originalNameServers []string
|
||||||
|
othersConfigs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// supported "openresolv" only
|
||||||
func newResolvConfConfigurator(wgInterface WGIface) (hostManager, error) {
|
func newResolvConfConfigurator(wgInterface WGIface) (hostManager, error) {
|
||||||
|
originalSearchDomains, nameServers, others, err := originalDNSConfigs("/etc/resolv.conf")
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
return &resolvconf{
|
return &resolvconf{
|
||||||
ifaceName: wgInterface.Name(),
|
ifaceName: wgInterface.Name(),
|
||||||
|
originalSearchDomains: originalSearchDomains,
|
||||||
|
originalNameServers: nameServers,
|
||||||
|
othersConfigs: others,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,41 +49,20 @@ func (r *resolvconf) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
return fmt.Errorf("unable to configure DNS for this peer using resolvconf manager without a nameserver group with all domains configured")
|
return fmt.Errorf("unable to configure DNS for this peer using resolvconf manager without a nameserver group with all domains configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
var searchDomains string
|
searchDomainList := searchDomains(config)
|
||||||
appendedDomains := 0
|
searchDomainList = mergeSearchDomains(searchDomainList, r.originalSearchDomains)
|
||||||
for _, dConf := range config.domains {
|
|
||||||
if dConf.matchOnly || dConf.disabled {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if appendedDomains >= fileMaxNumberOfSearchDomains {
|
buf := prepareResolvConfContent(
|
||||||
// lets log all skipped domains
|
searchDomainList,
|
||||||
log.Infof("already appended %d domains to search list. Skipping append of %s domain", fileMaxNumberOfSearchDomains, dConf.domain)
|
append([]string{config.serverIP}, r.originalNameServers...),
|
||||||
continue
|
r.othersConfigs)
|
||||||
}
|
|
||||||
|
|
||||||
if fileSearchLineBeginCharCount+len(searchDomains) > fileMaxLineCharsLimit {
|
err = r.applyConfig(buf)
|
||||||
// lets log all skipped domains
|
|
||||||
log.Infof("search list line is larger than %d characters. Skipping append of %s domain", fileMaxLineCharsLimit, dConf.domain)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
searchDomains += " " + dConf.domain
|
|
||||||
appendedDomains++
|
|
||||||
}
|
|
||||||
|
|
||||||
originalContent, err := os.ReadFile(fileDefaultResolvConfBackupLocation)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Could not read existing resolv.conf")
|
|
||||||
}
|
|
||||||
content := fmt.Sprintf(fileGeneratedResolvConfContentFormat, fileDefaultResolvConfBackupLocation, config.serverIP, searchDomains, string(originalContent))
|
|
||||||
|
|
||||||
err = r.applyConfig(content)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("added %d search domains. Search list: %s", appendedDomains, searchDomains)
|
log.Infof("added %d search domains. Search list: %s", len(searchDomainList), searchDomainList)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,12 +75,12 @@ func (r *resolvconf) restoreHostDNS() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolvconf) applyConfig(content string) error {
|
func (r *resolvconf) applyConfig(content bytes.Buffer) error {
|
||||||
cmd := exec.Command(resolvconfCommand, "-x", "-a", r.ifaceName)
|
cmd := exec.Command(resolvconfCommand, "-x", "-a", r.ifaceName)
|
||||||
cmd.Stdin = strings.NewReader(content)
|
cmd.Stdin = &content
|
||||||
_, err := cmd.Output()
|
_, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("got an error while appying resolvconf configuration for %s interface, error: %s", r.ifaceName, err)
|
return fmt.Errorf("got an error while applying resolvconf configuration for %s interface, error: %s", r.ifaceName, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"runtime"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/mitchellh/hashstructure/v2"
|
"github.com/mitchellh/hashstructure/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/listener"
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@ type Server interface {
|
|||||||
DnsIP() string
|
DnsIP() string
|
||||||
UpdateDNSServer(serial uint64, update nbdns.Config) error
|
UpdateDNSServer(serial uint64, update nbdns.Config) error
|
||||||
OnUpdatedHostDNSServer(strings []string)
|
OnUpdatedHostDNSServer(strings []string)
|
||||||
|
SearchDomains() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type registeredHandlerMap map[string]handlerWithStop
|
type registeredHandlerMap map[string]handlerWithStop
|
||||||
@@ -56,6 +57,9 @@ type DefaultServer struct {
|
|||||||
|
|
||||||
interfaceName string
|
interfaceName string
|
||||||
wgAddr string
|
wgAddr string
|
||||||
|
|
||||||
|
// make sense on mobile only
|
||||||
|
searchDomainNotifier *notifier
|
||||||
}
|
}
|
||||||
|
|
||||||
type handlerWithStop interface {
|
type handlerWithStop interface {
|
||||||
@@ -90,12 +94,15 @@ func NewDefaultServer(ctx context.Context, wgInterface WGIface, customAddress st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultServerPermanentUpstream returns a new dns server. It optimized for mobile systems
|
// NewDefaultServerPermanentUpstream returns a new dns server. It optimized for mobile systems
|
||||||
func NewDefaultServerPermanentUpstream(ctx context.Context, wgInterface WGIface, hostsDnsList []string) *DefaultServer {
|
func NewDefaultServerPermanentUpstream(ctx context.Context, wgInterface WGIface, hostsDnsList []string, config nbdns.Config, listener listener.NetworkChangeListener) *DefaultServer {
|
||||||
log.Debugf("host dns address list is: %v", hostsDnsList)
|
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.permanent = true
|
||||||
ds.hostsDnsList = hostsDnsList
|
ds.hostsDnsList = hostsDnsList
|
||||||
ds.addHostRootZone()
|
ds.addHostRootZone()
|
||||||
|
ds.currentConfig = dnsConfigToHostDNSConfig(config, ds.service.RuntimeIP(), ds.service.RuntimePort())
|
||||||
|
ds.searchDomainNotifier = newNotifier(ds.SearchDomains())
|
||||||
|
ds.searchDomainNotifier.setListener(listener)
|
||||||
setServerDns(ds)
|
setServerDns(ds)
|
||||||
return ds
|
return ds
|
||||||
}
|
}
|
||||||
@@ -227,6 +234,21 @@ 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 {
|
func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
|
||||||
// is the service should be disabled, we stop the listener or fake resolver
|
// 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
|
// and proceed with a regular update to clean up the handlers and records
|
||||||
@@ -261,6 +283,10 @@ func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
|
|||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.searchDomainNotifier != nil {
|
||||||
|
s.searchDomainNotifier.onNewSearchDomains(s.SearchDomains())
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +329,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
|
|||||||
handler := newUpstreamResolver(s.ctx, s.interfaceName, s.wgAddr)
|
handler := newUpstreamResolver(s.ctx, s.interfaceName, s.wgAddr)
|
||||||
for _, ns := range nsGroup.NameServers {
|
for _, ns := range nsGroup.NameServers {
|
||||||
if ns.NSType != nbdns.UDPNameServerType {
|
if ns.NSType != nbdns.UDPNameServerType {
|
||||||
log.Warnf("skiping nameserver %s with type %s, this peer supports only %s",
|
log.Warnf("skipping nameserver %s with type %s, this peer supports only %s",
|
||||||
ns.IP.String(), ns.NSType.String(), nbdns.UDPNameServerType.String())
|
ns.IP.String(), ns.NSType.String(), nbdns.UDPNameServerType.String())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -321,7 +347,7 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam
|
|||||||
// reapply DNS settings, but it not touch the original configuration and serial number
|
// reapply DNS settings, but it not touch the original configuration and serial number
|
||||||
// because it is temporal deactivation until next try
|
// because it is temporal deactivation until next try
|
||||||
//
|
//
|
||||||
// after some period defined by upstream it trys to reactivate self by calling this hook
|
// after some period defined by upstream it tries to reactivate self by calling this hook
|
||||||
// everything we need here is just to re-apply current configuration because it already
|
// everything we need here is just to re-apply current configuration because it already
|
||||||
// contains this upstream settings (temporal deactivation not removed it)
|
// contains this upstream settings (temporal deactivation not removed it)
|
||||||
handler.deactivate, handler.reactivate = s.upstreamCallbacks(nsGroup, handler)
|
handler.deactivate, handler.reactivate = s.upstreamCallbacks(nsGroup, handler)
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ func TestGetServerDns(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if srvB != srv {
|
if srvB != srv {
|
||||||
t.Errorf("missmatch dns instances")
|
t.Errorf("mismatch dns instances")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -593,7 +593,8 @@ func TestDNSPermanent_updateHostDNS_emptyUpstream(t *testing.T) {
|
|||||||
defer wgIFace.Close()
|
defer wgIFace.Close()
|
||||||
|
|
||||||
var dnsList []string
|
var dnsList []string
|
||||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, dnsList)
|
dnsConfig := nbdns.Config{}
|
||||||
|
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, dnsList, dnsConfig, nil)
|
||||||
err = dnsServer.Initialize()
|
err = dnsServer.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to initialize DNS server: %v", err)
|
t.Errorf("failed to initialize DNS server: %v", err)
|
||||||
@@ -616,8 +617,8 @@ func TestDNSPermanent_updateUpstream(t *testing.T) {
|
|||||||
t.Fatal("failed to initialize wg interface")
|
t.Fatal("failed to initialize wg interface")
|
||||||
}
|
}
|
||||||
defer wgIFace.Close()
|
defer wgIFace.Close()
|
||||||
|
dnsConfig := nbdns.Config{}
|
||||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"})
|
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
|
||||||
err = dnsServer.Initialize()
|
err = dnsServer.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to initialize DNS server: %v", err)
|
t.Errorf("failed to initialize DNS server: %v", err)
|
||||||
@@ -708,8 +709,8 @@ func TestDNSPermanent_matchOnly(t *testing.T) {
|
|||||||
t.Fatal("failed to initialize wg interface")
|
t.Fatal("failed to initialize wg interface")
|
||||||
}
|
}
|
||||||
defer wgIFace.Close()
|
defer wgIFace.Close()
|
||||||
|
dnsConfig := nbdns.Config{}
|
||||||
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"})
|
dnsServer := NewDefaultServerPermanentUpstream(context.Background(), wgIFace, []string{"8.8.8.8"}, dnsConfig, nil)
|
||||||
err = dnsServer.Initialize()
|
err = dnsServer.Initialize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("failed to initialize DNS server: %v", err)
|
t.Errorf("failed to initialize DNS server: %v", err)
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ func (u *upstreamResolver) getClientPrivate() *dns.Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *upstreamResolver) stop() {
|
func (u *upstreamResolver) stop() {
|
||||||
log.Debugf("stoping serving DNS for upstreams %s", u.upstreamServers)
|
log.Debugf("stopping serving DNS for upstreams %s", u.upstreamServers)
|
||||||
u.cancel()
|
u.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -195,12 +195,13 @@ func (e *Engine) Start() error {
|
|||||||
var routes []*route.Route
|
var routes []*route.Route
|
||||||
|
|
||||||
if runtime.GOOS == "android" {
|
if runtime.GOOS == "android" {
|
||||||
routes, err = e.readInitialSettings()
|
var dnsConfig *nbdns.Config
|
||||||
|
routes, dnsConfig, err = e.readInitialSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if e.dnsServer == nil {
|
if e.dnsServer == nil {
|
||||||
e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses)
|
e.dnsServer = dns.NewDefaultServerPermanentUpstream(e.ctx, e.wgInterface, e.mobileDep.HostDNSAddresses, *dnsConfig, e.mobileDep.NetworkChangeListener)
|
||||||
go e.mobileDep.DnsReadyListener.OnReady()
|
go e.mobileDep.DnsReadyListener.OnReady()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -214,17 +215,16 @@ 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 = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, routes)
|
||||||
e.mobileDep.RouteListener.SetInterfaceIP(wgAddr)
|
e.mobileDep.NetworkChangeListener.SetInterfaceIP(wgAddr)
|
||||||
|
e.routeManager.SetRouteChangeListener(e.mobileDep.NetworkChangeListener)
|
||||||
e.routeManager.SetRouteChangeListener(e.mobileDep.RouteListener)
|
|
||||||
|
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "android":
|
case "android":
|
||||||
err = e.wgInterface.CreateOnAndroid(iface.MobileIFaceArguments{
|
err = e.wgInterface.CreateOnAndroid(iface.MobileIFaceArguments{
|
||||||
Routes: e.routeManager.InitialRouteRange(),
|
Routes: e.routeManager.InitialRouteRange(),
|
||||||
Dns: e.dnsServer.DnsIP(),
|
Dns: e.dnsServer.DnsIP(),
|
||||||
|
SearchDomains: e.dnsServer.SearchDomains(),
|
||||||
})
|
})
|
||||||
case "ios":
|
case "ios":
|
||||||
err = e.wgInterface.CreateOniOS(e.mobileDep.FileDescriptor)
|
err = e.wgInterface.CreateOniOS(e.mobileDep.FileDescriptor)
|
||||||
@@ -724,8 +724,9 @@ func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig) nbdns.Config {
|
|||||||
|
|
||||||
for _, nsGroup := range protoDNSConfig.GetNameServerGroups() {
|
for _, nsGroup := range protoDNSConfig.GetNameServerGroups() {
|
||||||
dnsNSGroup := &nbdns.NameServerGroup{
|
dnsNSGroup := &nbdns.NameServerGroup{
|
||||||
Primary: nsGroup.GetPrimary(),
|
Primary: nsGroup.GetPrimary(),
|
||||||
Domains: nsGroup.GetDomains(),
|
Domains: nsGroup.GetDomains(),
|
||||||
|
SearchDomainsEnabled: nsGroup.GetSearchDomainsEnabled(),
|
||||||
}
|
}
|
||||||
for _, ns := range nsGroup.GetNameServers() {
|
for _, ns := range nsGroup.GetNameServers() {
|
||||||
dnsNS := nbdns.NameServer{
|
dnsNS := nbdns.NameServer{
|
||||||
@@ -1060,13 +1061,14 @@ func (e *Engine) close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) readInitialSettings() ([]*route.Route, error) {
|
func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
|
||||||
netMap, err := e.mgmClient.GetNetworkMap()
|
netMap, err := e.mgmClient.GetNetworkMap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
routes := toRoutes(netMap.GetRoutes())
|
routes := toRoutes(netMap.GetRoutes())
|
||||||
return routes, nil
|
dnsCfg := toDNSConfig(netMap.GetDNSConfig())
|
||||||
|
return routes, &dnsCfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findIPFromInterfaceName(ifaceName string) (net.IP, error) {
|
func findIPFromInterfaceName(ifaceName string) (net.IP, error) {
|
||||||
|
|||||||
@@ -1039,10 +1039,11 @@ func startManagement(dataDir string) (*grpc.Server, string, error) {
|
|||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
|
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
|
||||||
store, err := server.NewFileStore(config.Datadir, nil)
|
store, err := server.NewStoreFromJson(config.Datadir, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
peersUpdateManager := server.NewPeersUpdateManager()
|
peersUpdateManager := server.NewPeersUpdateManager()
|
||||||
eventStore := &activity.InMemoryEventStore{}
|
eventStore := &activity.InMemoryEventStore{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
7
client/internal/listener/network_change.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package listener
|
||||||
|
|
||||||
|
// NetworkChangeListener is a callback interface for mobile system
|
||||||
|
type NetworkChangeListener interface {
|
||||||
|
// OnNetworkChanged invoke when network settings has been changed
|
||||||
|
OnNetworkChanged()
|
||||||
|
}
|
||||||
@@ -2,19 +2,19 @@ package internal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/netbirdio/netbird/client/internal/dns"
|
"github.com/netbirdio/netbird/client/internal/dns"
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
"github.com/netbirdio/netbird/client/internal/listener"
|
||||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MobileDependency collect all dependencies for mobile platform
|
// MobileDependency collect all dependencies for mobile platform
|
||||||
type MobileDependency struct {
|
type MobileDependency struct {
|
||||||
TunAdapter iface.TunAdapter
|
TunAdapter iface.TunAdapter
|
||||||
IFaceDiscover stdnet.ExternalIFaceDiscover
|
IFaceDiscover stdnet.ExternalIFaceDiscover
|
||||||
RouteListener routemanager.RouteListener
|
NetworkChangeListener listener.NetworkChangeListener
|
||||||
HostDNSAddresses []string
|
HostDNSAddresses []string
|
||||||
DnsReadyListener dns.ReadyListener
|
DnsReadyListener dns.ReadyListener
|
||||||
DnsManager dns.IosDnsManager
|
DnsManager dns.IosDnsManager
|
||||||
FileDescriptor int32
|
FileDescriptor int32
|
||||||
InterfaceName string
|
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)
|
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 {
|
} else if chosen != currID {
|
||||||
log.Infof("new chosen route is %s with peer %s with score %d", chosen, c.routes[chosen].Peer, chosenScore)
|
log.Infof("new chosen route is %s with peer %s with score %d for network %s", chosen, c.routes[chosen].Peer, chosenScore, c.network)
|
||||||
}
|
}
|
||||||
|
|
||||||
return chosen
|
return chosen
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/listener"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
@@ -16,7 +17,7 @@ import (
|
|||||||
// Manager is a route manager interface
|
// Manager is a route manager interface
|
||||||
type Manager interface {
|
type Manager interface {
|
||||||
UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error
|
UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error
|
||||||
SetRouteChangeListener(listener RouteListener)
|
SetRouteChangeListener(listener listener.NetworkChangeListener)
|
||||||
InitialRouteRange() []string
|
InitialRouteRange() []string
|
||||||
Stop()
|
Stop()
|
||||||
}
|
}
|
||||||
@@ -96,7 +97,7 @@ func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Ro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetRouteChangeListener set RouteListener for route change notifier
|
// SetRouteChangeListener set RouteListener for route change notifier
|
||||||
func (m *DefaultManager) SetRouteChangeListener(listener RouteListener) {
|
func (m *DefaultManager) SetRouteChangeListener(listener listener.NetworkChangeListener) {
|
||||||
m.notifier.setListener(listener)
|
m.notifier.setListener(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +156,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
|
// if prefix is too small, lets assume is a possible default route which is not yet supported
|
||||||
// we skip this route management
|
// we skip this route management
|
||||||
if newRoute.Network.Bits() < 7 {
|
if newRoute.Network.Bits() < 7 {
|
||||||
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skiping this route",
|
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skipping this route",
|
||||||
version.NetbirdVersion(), newRoute.Network)
|
version.NetbirdVersion(), newRoute.Network)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/listener"
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
)
|
)
|
||||||
@@ -32,7 +33,7 @@ func (m *MockManager) Start(ctx context.Context, iface *iface.WGIface) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetRouteChangeListener mock implementation of SetRouteChangeListener from Manager interface
|
// SetRouteChangeListener mock implementation of SetRouteChangeListener from Manager interface
|
||||||
func (m *MockManager) SetRouteChangeListener(listener RouteListener) {
|
func (m *MockManager) SetRouteChangeListener(listener listener.NetworkChangeListener) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,8 @@ func (n *nftablesManager) RestoreOrCreateContainers() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
if table.Name == "filter" {
|
if table.Name == "filter" && table.Family == nftables.TableFamilyIPv4 {
|
||||||
|
log.Debugf("nftables: found filter table for ipv4")
|
||||||
n.filterTable = table
|
n.filterTable = table
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -486,7 +487,7 @@ func (n *nftablesManager) RemoveRoutingRules(pair routerPair) error {
|
|||||||
if len(n.rules) == 2 && n.defaultForwardRules[0] != nil {
|
if len(n.rules) == 2 && n.defaultForwardRules[0] != nil {
|
||||||
err := n.eraseDefaultForwardRule()
|
err := n.eraseDefaultForwardRule()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to delte default fwd rule: %s", err)
|
log.Errorf("failed to delete default fwd rule: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,37 +2,34 @@ package routemanager
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RouteListener is a callback interface for mobile system
|
// RouteListener is a callback interface for mobile system
|
||||||
type RouteListener interface {
|
// type RouteListener interface {
|
||||||
// OnNewRouteSetting invoke when new route setting has been arrived
|
// // OnNewRouteSetting invoke when new route setting has been arrived
|
||||||
OnNewRouteSetting(string)
|
// OnNewRouteSetting(string)
|
||||||
SetInterfaceIP(string)
|
// SetInterfaceIP(string)
|
||||||
}
|
// }
|
||||||
|
|
||||||
type notifier struct {
|
type notifier struct {
|
||||||
initialRouteRangers []string
|
initialRouteRangers []string
|
||||||
routeRangers []string
|
routeRangers []string
|
||||||
|
|
||||||
routeListener RouteListener
|
listener listener.NetworkChangeListener
|
||||||
routeListenerMux sync.Mutex
|
listenerMux sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNotifier() *notifier {
|
func newNotifier() *notifier {
|
||||||
return ¬ifier{}
|
return ¬ifier{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *notifier) setListener(listener RouteListener) {
|
func (n *notifier) setListener(listener listener.NetworkChangeListener) {
|
||||||
n.routeListenerMux.Lock()
|
n.listenerMux.Lock()
|
||||||
defer n.routeListenerMux.Unlock()
|
defer n.listenerMux.Unlock()
|
||||||
n.routeListener = listener
|
n.listener = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *notifier) setInitialClientRoutes(clientRoutes []*route.Route) {
|
func (n *notifier) setInitialClientRoutes(clientRoutes []*route.Route) {
|
||||||
@@ -63,16 +60,16 @@ func (n *notifier) onNewRoutes(idMap map[string][]*route.Route) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *notifier) notify() {
|
func (n *notifier) notify() {
|
||||||
n.routeListenerMux.Lock()
|
n.listenerMux.Lock()
|
||||||
defer n.routeListenerMux.Unlock()
|
defer n.listenerMux.Unlock()
|
||||||
if n.routeListener == nil {
|
if n.listener == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go func(l RouteListener) {
|
go func(l listener.NetworkChangeListener) {
|
||||||
log.Debugf("notifying route listener with route ranges: %s", strings.Join(n.routeRangers, ","))
|
log.Debugf("notifying route listener with route ranges: %s", strings.Join(n.routeRangers, ","))
|
||||||
l.OnNewRouteSetting(strings.Join(n.routeRangers, ","))
|
l.OnNetworkChanged(strings.Join(n.routeRangers, ","))
|
||||||
}(n.routeListener)
|
}(n.listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *notifier) hasDiff(a []string, b []string) bool {
|
func (n *notifier) hasDiff(a []string, b []string) bool {
|
||||||
|
|||||||
5
client/ui/build-ui-linux.sh
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt remove gir1.2-appindicator3-0.1
|
||||||
|
sudo apt install -y libayatana-appindicator3-dev
|
||||||
|
go build
|
||||||
@@ -15,8 +15,10 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/app"
|
"fyne.io/fyne/v2/app"
|
||||||
@@ -74,18 +76,30 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed connected.ico
|
//go:embed netbird-systemtray-connected.ico
|
||||||
var iconConnectedICO []byte
|
var iconConnectedICO []byte
|
||||||
|
|
||||||
//go:embed connected.png
|
//go:embed netbird-systemtray-connected.png
|
||||||
var iconConnectedPNG []byte
|
var iconConnectedPNG []byte
|
||||||
|
|
||||||
//go:embed disconnected.ico
|
//go:embed netbird-systemtray-default.ico
|
||||||
var iconDisconnectedICO []byte
|
var iconDisconnectedICO []byte
|
||||||
|
|
||||||
//go:embed disconnected.png
|
//go:embed netbird-systemtray-default.png
|
||||||
var iconDisconnectedPNG []byte
|
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 {
|
type serviceClient struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
addr string
|
addr string
|
||||||
@@ -93,14 +107,20 @@ type serviceClient struct {
|
|||||||
|
|
||||||
icConnected []byte
|
icConnected []byte
|
||||||
icDisconnected []byte
|
icDisconnected []byte
|
||||||
|
icUpdate []byte
|
||||||
|
icUpdateCloud []byte
|
||||||
|
|
||||||
// systray menu items
|
// systray menu items
|
||||||
mStatus *systray.MenuItem
|
mStatus *systray.MenuItem
|
||||||
mUp *systray.MenuItem
|
mUp *systray.MenuItem
|
||||||
mDown *systray.MenuItem
|
mDown *systray.MenuItem
|
||||||
mAdminPanel *systray.MenuItem
|
mAdminPanel *systray.MenuItem
|
||||||
mSettings *systray.MenuItem
|
mSettings *systray.MenuItem
|
||||||
mQuit *systray.MenuItem
|
mAbout *systray.MenuItem
|
||||||
|
mVersionUI *systray.MenuItem
|
||||||
|
mVersionDaemon *systray.MenuItem
|
||||||
|
mUpdate *systray.MenuItem
|
||||||
|
mQuit *systray.MenuItem
|
||||||
|
|
||||||
// application with main windows.
|
// application with main windows.
|
||||||
app fyne.App
|
app fyne.App
|
||||||
@@ -118,6 +138,11 @@ type serviceClient struct {
|
|||||||
managementURL string
|
managementURL string
|
||||||
preSharedKey string
|
preSharedKey string
|
||||||
adminURL string
|
adminURL string
|
||||||
|
|
||||||
|
update *version.Update
|
||||||
|
daemonVersion string
|
||||||
|
updateIndicationLock sync.Mutex
|
||||||
|
isUpdateIconActive bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// newServiceClient instance constructor
|
// newServiceClient instance constructor
|
||||||
@@ -130,14 +155,20 @@ func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient
|
|||||||
app: a,
|
app: a,
|
||||||
|
|
||||||
showSettings: showSettings,
|
showSettings: showSettings,
|
||||||
|
update: version.NewUpdate(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
s.icConnected = iconConnectedICO
|
s.icConnected = iconConnectedICO
|
||||||
s.icDisconnected = iconDisconnectedICO
|
s.icDisconnected = iconDisconnectedICO
|
||||||
|
s.icUpdate = iconUpdateICO
|
||||||
|
s.icUpdateCloud = iconUpdateCloudICO
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
s.icConnected = iconConnectedPNG
|
s.icConnected = iconConnectedPNG
|
||||||
s.icDisconnected = iconDisconnectedPNG
|
s.icDisconnected = iconDisconnectedPNG
|
||||||
|
s.icUpdate = iconUpdatePNG
|
||||||
|
s.icUpdateCloud = iconUpdateCloudPNG
|
||||||
}
|
}
|
||||||
|
|
||||||
if showSettings {
|
if showSettings {
|
||||||
@@ -202,9 +233,10 @@ func (s *serviceClient) getSettingsForm() *widget.Form {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = client.Login(s.ctx, &proto.LoginRequest{
|
_, err = client.Login(s.ctx, &proto.LoginRequest{
|
||||||
ManagementUrl: s.iMngURL.Text,
|
ManagementUrl: s.iMngURL.Text,
|
||||||
AdminURL: s.iAdminURL.Text,
|
AdminURL: s.iAdminURL.Text,
|
||||||
PreSharedKey: s.iPreSharedKey.Text,
|
PreSharedKey: s.iPreSharedKey.Text,
|
||||||
|
IsLinuxDesktopClient: runtime.GOOS == "linux",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("login to management URL: %v", err)
|
log.Errorf("login to management URL: %v", err)
|
||||||
@@ -233,7 +265,9 @@ func (s *serviceClient) login() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
loginResp, err := conn.Login(s.ctx, &proto.LoginRequest{})
|
loginResp, err := conn.Login(s.ctx, &proto.LoginRequest{
|
||||||
|
IsLinuxDesktopClient: runtime.GOOS == "linux",
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("login to management URL with: %v", err)
|
log.Errorf("login to management URL with: %v", err)
|
||||||
return err
|
return err
|
||||||
@@ -325,19 +359,53 @@ func (s *serviceClient) updateStatus() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.updateIndicationLock.Lock()
|
||||||
|
defer s.updateIndicationLock.Unlock()
|
||||||
|
|
||||||
|
var systrayIconState bool
|
||||||
if status.Status == string(internal.StatusConnected) && !s.mUp.Disabled() {
|
if status.Status == string(internal.StatusConnected) && !s.mUp.Disabled() {
|
||||||
systray.SetIcon(s.icConnected)
|
if !s.isUpdateIconActive {
|
||||||
|
systray.SetIcon(s.icConnected)
|
||||||
|
}
|
||||||
systray.SetTooltip("NetBird (Connected)")
|
systray.SetTooltip("NetBird (Connected)")
|
||||||
s.mStatus.SetTitle("Connected")
|
s.mStatus.SetTitle("Connected")
|
||||||
s.mUp.Disable()
|
s.mUp.Disable()
|
||||||
s.mDown.Enable()
|
s.mDown.Enable()
|
||||||
|
systrayIconState = true
|
||||||
} else if status.Status != string(internal.StatusConnected) && s.mUp.Disabled() {
|
} else if status.Status != string(internal.StatusConnected) && s.mUp.Disabled() {
|
||||||
systray.SetIcon(s.icDisconnected)
|
if !s.isUpdateIconActive {
|
||||||
|
systray.SetIcon(s.icDisconnected)
|
||||||
|
}
|
||||||
systray.SetTooltip("NetBird (Disconnected)")
|
systray.SetTooltip("NetBird (Disconnected)")
|
||||||
s.mStatus.SetTitle("Disconnected")
|
s.mStatus.SetTitle("Disconnected")
|
||||||
s.mDown.Disable()
|
s.mDown.Disable()
|
||||||
s.mUp.Enable()
|
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
|
return nil
|
||||||
}, &backoff.ExponentialBackOff{
|
}, &backoff.ExponentialBackOff{
|
||||||
InitialInterval: time.Second,
|
InitialInterval: time.Second,
|
||||||
@@ -371,11 +439,24 @@ func (s *serviceClient) onTrayReady() {
|
|||||||
systray.AddSeparator()
|
systray.AddSeparator()
|
||||||
s.mSettings = systray.AddMenuItem("Settings", "Settings of the application")
|
s.mSettings = systray.AddMenuItem("Settings", "Settings of the application")
|
||||||
systray.AddSeparator()
|
systray.AddSeparator()
|
||||||
v := systray.AddMenuItem("v"+version.NetbirdVersion(), "Client Version: "+version.NetbirdVersion())
|
|
||||||
v.Disable()
|
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()
|
||||||
|
|
||||||
systray.AddSeparator()
|
systray.AddSeparator()
|
||||||
s.mQuit = systray.AddMenuItem("Quit", "Quit the client app")
|
s.mQuit = systray.AddMenuItem("Quit", "Quit the client app")
|
||||||
|
|
||||||
|
s.update.SetOnUpdateListener(s.onUpdateAvailable)
|
||||||
go func() {
|
go func() {
|
||||||
s.getSrvConfig()
|
s.getSrvConfig()
|
||||||
for {
|
for {
|
||||||
@@ -433,6 +514,11 @@ func (s *serviceClient) onTrayReady() {
|
|||||||
case <-s.mQuit.ClickedCh:
|
case <-s.mQuit.ClickedCh:
|
||||||
systray.Quit()
|
systray.Quit()
|
||||||
return
|
return
|
||||||
|
case <-s.mUpdate.ClickedCh:
|
||||||
|
err := openURL(version.DownloadUrl())
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("%s", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("process connection: %v", err)
|
log.Errorf("process connection: %v", err)
|
||||||
@@ -441,6 +527,14 @@ 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() {}
|
func (s *serviceClient) onTrayExit() {}
|
||||||
|
|
||||||
// getSrvClient connection to the service.
|
// getSrvClient connection to the service.
|
||||||
@@ -501,6 +595,32 @@ 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.
|
// checkPIDFile exists and return error, or write new.
|
||||||
func checkPIDFile() error {
|
func checkPIDFile() error {
|
||||||
pidFile := path.Join(os.TempDir(), "wiretrustee-ui.pid")
|
pidFile := path.Join(os.TempDir(), "wiretrustee-ui.pid")
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 6.3 KiB |
BIN
client/ui/netbird-systemtray-connected.ico
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
client/ui/netbird-systemtray-connected.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
client/ui/netbird-systemtray-default.ico
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
client/ui/netbird-systemtray-default.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
client/ui/netbird-systemtray-update-cloud.ico
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
client/ui/netbird-systemtray-update-cloud.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
client/ui/netbird-systemtray-update.ico
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
client/ui/netbird-systemtray-update.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
@@ -50,21 +50,25 @@ func ToNameServerType(typeString string) NameServerType {
|
|||||||
// NameServerGroup group of nameservers and with group ids
|
// NameServerGroup group of nameservers and with group ids
|
||||||
type NameServerGroup struct {
|
type NameServerGroup struct {
|
||||||
// ID identifier of group
|
// ID identifier of group
|
||||||
ID string
|
ID string `gorm:"primaryKey"`
|
||||||
|
// AccountID is a reference to Account that this object belongs
|
||||||
|
AccountID string `gorm:"index"`
|
||||||
// Name group name
|
// Name group name
|
||||||
Name string
|
Name string
|
||||||
// Description group description
|
// Description group description
|
||||||
Description string
|
Description string
|
||||||
// NameServers list of nameservers
|
// NameServers list of nameservers
|
||||||
NameServers []NameServer
|
NameServers []NameServer `gorm:"serializer:json"`
|
||||||
// Groups list of peer group IDs to distribute the nameservers information
|
// Groups list of peer group IDs to distribute the nameservers information
|
||||||
Groups []string
|
Groups []string `gorm:"serializer:json"`
|
||||||
// Primary indicates that the nameserver group is the primary resolver for any dns query
|
// Primary indicates that the nameserver group is the primary resolver for any dns query
|
||||||
Primary bool
|
Primary bool
|
||||||
// Domains indicate the dns query domains to use with this nameserver group
|
// Domains indicate the dns query domains to use with this nameserver group
|
||||||
Domains []string
|
Domains []string `gorm:"serializer:json"`
|
||||||
// Enabled group status
|
// Enabled group status
|
||||||
Enabled bool
|
Enabled bool
|
||||||
|
// SearchDomainsEnabled indicates whether to add match domains to search domains list or not
|
||||||
|
SearchDomainsEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NameServer represents a DNS nameserver
|
// NameServer represents a DNS nameserver
|
||||||
@@ -131,14 +135,15 @@ func ParseNameServerURL(nsURL string) (NameServer, error) {
|
|||||||
// Copy copies a nameserver group object
|
// Copy copies a nameserver group object
|
||||||
func (g *NameServerGroup) Copy() *NameServerGroup {
|
func (g *NameServerGroup) Copy() *NameServerGroup {
|
||||||
nsGroup := &NameServerGroup{
|
nsGroup := &NameServerGroup{
|
||||||
ID: g.ID,
|
ID: g.ID,
|
||||||
Name: g.Name,
|
Name: g.Name,
|
||||||
Description: g.Description,
|
Description: g.Description,
|
||||||
NameServers: make([]NameServer, len(g.NameServers)),
|
NameServers: make([]NameServer, len(g.NameServers)),
|
||||||
Groups: make([]string, len(g.Groups)),
|
Groups: make([]string, len(g.Groups)),
|
||||||
Enabled: g.Enabled,
|
Enabled: g.Enabled,
|
||||||
Primary: g.Primary,
|
Primary: g.Primary,
|
||||||
Domains: make([]string, len(g.Domains)),
|
Domains: make([]string, len(g.Domains)),
|
||||||
|
SearchDomainsEnabled: g.SearchDomainsEnabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(nsGroup.NameServers, g.NameServers)
|
copy(nsGroup.NameServers, g.NameServers)
|
||||||
@@ -154,6 +159,7 @@ func (g *NameServerGroup) IsEqual(other *NameServerGroup) bool {
|
|||||||
other.Name == g.Name &&
|
other.Name == g.Name &&
|
||||||
other.Description == g.Description &&
|
other.Description == g.Description &&
|
||||||
other.Primary == g.Primary &&
|
other.Primary == g.Primary &&
|
||||||
|
other.SearchDomainsEnabled == g.SearchDomainsEnabled &&
|
||||||
compareNameServerList(g.NameServers, other.NameServers) &&
|
compareNameServerList(g.NameServers, other.NameServers) &&
|
||||||
compareGroupsList(g.Groups, other.Groups) &&
|
compareGroupsList(g.Groups, other.Groups) &&
|
||||||
compareGroupsList(g.Domains, other.Domains)
|
compareGroupsList(g.Domains, other.Domains)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func Decrypt(encryptedMsg []byte, peerPublicKey wgtypes.Key, privateKey wgtypes.
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(encryptedMsg) < nonceSize {
|
if len(encryptedMsg) < nonceSize {
|
||||||
return nil, fmt.Errorf("invalid encrypted message lenght")
|
return nil, fmt.Errorf("invalid encrypted message length")
|
||||||
}
|
}
|
||||||
copy(nonce[:], encryptedMsg[:nonceSize])
|
copy(nonce[:], encryptedMsg[:nonceSize])
|
||||||
opened, ok := box.Open(nil, encryptedMsg[nonceSize:], nonce, toByte32(peerPublicKey), toByte32(privateKey))
|
opened, ok := box.Open(nil, encryptedMsg[nonceSize:], nonce, toByte32(peerPublicKey), toByte32(privateKey))
|
||||||
|
|||||||
23
go.mod
@@ -17,12 +17,12 @@ require (
|
|||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/vishvananda/netlink v1.1.0
|
github.com/vishvananda/netlink v1.1.0
|
||||||
golang.org/x/crypto v0.9.0
|
golang.org/x/crypto v0.14.0
|
||||||
golang.org/x/sys v0.8.0
|
golang.org/x/sys v0.13.0
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675
|
golang.zx2c4.com/wireguard v0.0.0-20230223181233-21636207a675
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||||
google.golang.org/grpc v1.55.0
|
google.golang.org/grpc v1.56.3
|
||||||
google.golang.org/protobuf v1.30.0
|
google.golang.org/protobuf v1.30.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
)
|
)
|
||||||
@@ -46,11 +46,12 @@ require (
|
|||||||
github.com/hashicorp/go-version v1.6.0
|
github.com/hashicorp/go-version v1.6.0
|
||||||
github.com/libp2p/go-netroute v0.2.0
|
github.com/libp2p/go-netroute v0.2.0
|
||||||
github.com/magiconair/properties v1.8.5
|
github.com/magiconair/properties v1.8.5
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.17
|
||||||
github.com/mdlayher/socket v0.4.0
|
github.com/mdlayher/socket v0.4.0
|
||||||
github.com/miekg/dns v1.1.43
|
github.com/miekg/dns v1.1.43
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/nadoo/ipset v0.5.0
|
github.com/nadoo/ipset v0.5.0
|
||||||
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20231027143200-a966bce7db88
|
||||||
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pion/logging v0.2.2
|
github.com/pion/logging v0.2.2
|
||||||
@@ -68,12 +69,14 @@ require (
|
|||||||
goauthentik.io/api/v3 v3.2023051.3
|
goauthentik.io/api/v3 v3.2023051.3
|
||||||
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf
|
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028
|
||||||
golang.org/x/net v0.10.0
|
golang.org/x/net v0.17.0
|
||||||
golang.org/x/oauth2 v0.8.0
|
golang.org/x/oauth2 v0.8.0
|
||||||
golang.org/x/sync v0.2.0
|
golang.org/x/sync v0.2.0
|
||||||
golang.org/x/term v0.8.0
|
golang.org/x/term v0.13.0
|
||||||
google.golang.org/api v0.126.0
|
google.golang.org/api v0.126.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
gorm.io/driver/sqlite v1.5.3
|
||||||
|
gorm.io/gorm v1.25.4
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -110,6 +113,8 @@ require (
|
|||||||
github.com/googleapis/gax-go/v2 v2.10.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.10.0 // indirect
|
||||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/josharian/native v1.0.0 // indirect
|
github.com/josharian/native v1.0.0 // indirect
|
||||||
github.com/kelseyhightower/envconfig v1.4.0 // indirect
|
github.com/kelseyhightower/envconfig v1.4.0 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
@@ -135,9 +140,9 @@ require (
|
|||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.11.1 // indirect
|
go.opentelemetry.io/otel/sdk v1.11.1 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.11.1 // indirect
|
go.opentelemetry.io/otel/trace v1.11.1 // indirect
|
||||||
golang.org/x/image v0.5.0 // indirect
|
golang.org/x/image v0.10.0 // indirect
|
||||||
golang.org/x/mod v0.8.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/text v0.9.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||||
golang.org/x/tools v0.6.0 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
@@ -154,6 +159,6 @@ require (
|
|||||||
|
|
||||||
replace github.com/kardianos/service => github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0
|
replace github.com/kardianos/service => github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0
|
||||||
|
|
||||||
replace github.com/getlantern/systray => github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c
|
replace github.com/getlantern/systray => github.com/netbirdio/systray v0.0.0-20231030152038-ef1ed2a27949
|
||||||
|
|
||||||
replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f
|
replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f
|
||||||
|
|||||||
43
go.sum
@@ -383,6 +383,10 @@ 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 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
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/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/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/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=
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
@@ -441,8 +445,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/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/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-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.17/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.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 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
@@ -491,10 +495,12 @@ 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/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 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
|
||||||
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
|
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 h1:hirFRfx3grVA/9eEyjME5/z3nxdJlN9kfQpvWWPk32g=
|
||||||
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
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=
|
github.com/netbirdio/systray v0.0.0-20231030152038-ef1ed2a27949 h1:xbWM9BU6mwZZLHxEjxIX/V8Hv3HurQt4mReIE4mY4DM=
|
||||||
github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM=
|
github.com/netbirdio/systray v0.0.0-20231030152038-ef1ed2a27949/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM=
|
||||||
github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f h1:WQXGYCKPkNs1KusFTLieV73UVTNfZVyez4CFRvlOruM=
|
github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f h1:WQXGYCKPkNs1KusFTLieV73UVTNfZVyez4CFRvlOruM=
|
||||||
github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4=
|
github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
@@ -724,8 +730,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.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.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.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@@ -741,8 +747,8 @@ golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2
|
|||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
golang.org/x/image v0.10.0 h1:gXjUUtwtx5yOE0VKWq1CH4IJAClq4UGgUA3i+rpON9M=
|
||||||
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
golang.org/x/image v0.10.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
@@ -832,8 +838,9 @@ 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.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.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.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.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-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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@@ -957,15 +964,17 @@ 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.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.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.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.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-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.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.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.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.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.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-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.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@@ -979,8 +988,10 @@ 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.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.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.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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
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-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-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@@ -1135,8 +1146,8 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
|
|||||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||||
google.golang.org/grpc v1.51.0-dev/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
google.golang.org/grpc v1.51.0-dev/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||||
google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag=
|
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
|
||||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@@ -1189,6 +1200,10 @@ 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.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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
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 v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||||
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLREcpj61RxrWYzD8uwveOY=
|
gvisor.dev/gvisor v0.0.0-20221203005347-703fd9b7fbc0 h1:Wobr37noukisGxpKo5jAsLREcpj61RxrWYzD8uwveOY=
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ func (a *xorMapped) closeWaiters() {
|
|||||||
// just exit
|
// just exit
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
// notify tha twe have a new addr
|
// notify that twe have a new addr
|
||||||
close(a.waitAddrReceived)
|
close(a.waitAddrReceived)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func TestDeviceWrapperRead(t *testing.T) {
|
|||||||
|
|
||||||
n, err := wrapped.Read(bufs, sizes, offset)
|
n, err := wrapped.Read(bufs, sizes, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpeted error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n != 1 {
|
if n != 1 {
|
||||||
@@ -105,7 +105,7 @@ func TestDeviceWrapperRead(t *testing.T) {
|
|||||||
|
|
||||||
n, err := wrapped.Write(bufs, 0)
|
n, err := wrapped.Write(bufs, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpeted error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n != 1 {
|
if n != 1 {
|
||||||
@@ -154,7 +154,7 @@ func TestDeviceWrapperRead(t *testing.T) {
|
|||||||
|
|
||||||
n, err := wrapped.Write(bufs, 0)
|
n, err := wrapped.Write(bufs, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpeted error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n != 0 {
|
if n != 0 {
|
||||||
@@ -211,7 +211,7 @@ func TestDeviceWrapperRead(t *testing.T) {
|
|||||||
|
|
||||||
n, err := wrapped.Read(bufs, sizes, offset)
|
n, err := wrapped.Read(bufs, sizes, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpeted error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if n != 0 {
|
if n != 0 {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// keep darwin compability
|
// keep darwin compatibility
|
||||||
const (
|
const (
|
||||||
WgIntNumber = 2000
|
WgIntNumber = 2000
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ func canCreateFakeWireGuardInterface() bool {
|
|||||||
// We willingly try to create a device with an invalid
|
// We willingly try to create a device with an invalid
|
||||||
// MTU here as the validation of the MTU will be performed after
|
// MTU here as the validation of the MTU will be performed after
|
||||||
// the validation of the link kind and hence allows us to check
|
// the validation of the link kind and hence allows us to check
|
||||||
// for the existance of the wireguard module without actually
|
// for the existence of the wireguard module without actually
|
||||||
// creating a link.
|
// creating a link.
|
||||||
//
|
//
|
||||||
// As a side-effect, this will also let the kernel lazy-load
|
// 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 {
|
func loadModuleWithDependencies(name, path string) error {
|
||||||
deps, err := getModuleDependencies(name)
|
deps, err := getModuleDependencies(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't load list of module %s dependecies", name)
|
return fmt.Errorf("couldn't load list of module %s dependencies", name)
|
||||||
}
|
}
|
||||||
for _, dep := range deps {
|
for _, dep := range deps {
|
||||||
err = loadModule(dep.name, dep.path)
|
err = loadModule(dep.name, dep.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't load dependecy module %s for %s", dep.name, name)
|
return fmt.Errorf("couldn't load dependency module %s for %s", dep.name, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loadModule(name, path)
|
return loadModule(name, path)
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package iface
|
package iface
|
||||||
|
|
||||||
type MobileIFaceArguments struct {
|
type MobileIFaceArguments struct {
|
||||||
Routes []string
|
Routes []string
|
||||||
Dns string
|
Dns string
|
||||||
|
SearchDomains []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetInterface represents a generic network tunnel interface
|
// NetInterface represents a generic network tunnel interface
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ package iface
|
|||||||
|
|
||||||
// TunAdapter is an interface for create tun device from externel service
|
// TunAdapter is an interface for create tun device from externel service
|
||||||
type TunAdapter interface {
|
type TunAdapter interface {
|
||||||
ConfigureInterface(address string, mtu int, dns string, routes string) (int, error)
|
ConfigureInterface(address string, mtu int, dns string, searchDomains string, routes string) (int, error)
|
||||||
UpdateAddr(address string) error
|
UpdateAddr(address string) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
|
|||||||
log.Info("create tun interface")
|
log.Info("create tun interface")
|
||||||
var err error
|
var err error
|
||||||
routesString := t.routesToString(mIFaceArgs.Routes)
|
routesString := t.routesToString(mIFaceArgs.Routes)
|
||||||
t.fd, err = t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, mIFaceArgs.Dns, routesString)
|
searchDomainsToString := t.searchDomainsToString(mIFaceArgs.SearchDomains)
|
||||||
|
t.fd, err = t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, mIFaceArgs.Dns, searchDomainsToString, routesString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to create Android interface: %s", err)
|
log.Errorf("failed to create Android interface: %s", err)
|
||||||
return err
|
return err
|
||||||
@@ -97,3 +98,7 @@ func (t *tunDevice) Close() (err error) {
|
|||||||
func (t *tunDevice) routesToString(routes []string) string {
|
func (t *tunDevice) routesToString(routes []string) string {
|
||||||
return strings.Join(routes, ";")
|
return strings.Join(routes, ";")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tunDevice) searchDomainsToString(searchDomains []string) string {
|
||||||
|
return strings.Join(searchDomains, ";")
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ func (c *tunDevice) Create() error {
|
|||||||
func (c *tunDevice) assignAddr() error {
|
func (c *tunDevice) assignAddr() error {
|
||||||
cmd := exec.Command("ifconfig", c.name, "inet", c.address.IP.String(), c.address.IP.String())
|
cmd := exec.Command("ifconfig", c.name, "inet", c.address.IP.String(), c.address.IP.String())
|
||||||
if out, err := cmd.CombinedOutput(); err != nil {
|
if out, err := cmd.CombinedOutput(); err != nil {
|
||||||
log.Infof(`adding addreess command "%v" failed with output %s and error: `, cmd.String(), out)
|
log.Infof(`adding address command "%v" failed with output %s and error: `, cmd.String(), out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,15 @@
|
|||||||
NETBIRD_MGMT_API_PORT=${NETBIRD_MGMT_API_PORT:-33073}
|
NETBIRD_MGMT_API_PORT=${NETBIRD_MGMT_API_PORT:-33073}
|
||||||
# Management API endpoint address, used by the Dashboard
|
# Management API endpoint address, used by the Dashboard
|
||||||
NETBIRD_MGMT_API_ENDPOINT=https://$NETBIRD_DOMAIN:$NETBIRD_MGMT_API_PORT
|
NETBIRD_MGMT_API_ENDPOINT=https://$NETBIRD_DOMAIN:$NETBIRD_MGMT_API_PORT
|
||||||
# Management Certficate file path. These are generated by the Dashboard container
|
# Management Certificate file path. These are generated by the Dashboard container
|
||||||
NETBIRD_LETSENCRYPT_DOMAIN=$NETBIRD_DOMAIN
|
NETBIRD_LETSENCRYPT_DOMAIN=$NETBIRD_DOMAIN
|
||||||
NETBIRD_MGMT_API_CERT_FILE="/etc/letsencrypt/live/$NETBIRD_LETSENCRYPT_DOMAIN/fullchain.pem"
|
NETBIRD_MGMT_API_CERT_FILE="/etc/letsencrypt/live/$NETBIRD_LETSENCRYPT_DOMAIN/fullchain.pem"
|
||||||
# Management Certficate key file path.
|
# Management Certificate key file path.
|
||||||
NETBIRD_MGMT_API_CERT_KEY_FILE="/etc/letsencrypt/live/$NETBIRD_LETSENCRYPT_DOMAIN/privkey.pem"
|
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
|
# 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_SINGLE_ACCOUNT_MODE_DOMAIN=$NETBIRD_DOMAIN
|
||||||
NETBIRD_MGMT_DNS_DOMAIN=${NETBIRD_MGMT_DNS_DOMAIN:-netbird.selfhosted}
|
NETBIRD_MGMT_DNS_DOMAIN=${NETBIRD_MGMT_DNS_DOMAIN:-netbird.selfhosted}
|
||||||
|
NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=${NETBIRD_MGMT_IDP_SIGNKEY_REFRESH:-false}
|
||||||
|
|
||||||
# Signal
|
# Signal
|
||||||
NETBIRD_SIGNAL_PROTOCOL="http"
|
NETBIRD_SIGNAL_PROTOCOL="http"
|
||||||
@@ -55,6 +56,9 @@ NETBIRD_AUTH_PKCE_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
|||||||
NETBIRD_DASH_AUTH_USE_AUDIENCE=${NETBIRD_DASH_AUTH_USE_AUDIENCE:-true}
|
NETBIRD_DASH_AUTH_USE_AUDIENCE=${NETBIRD_DASH_AUTH_USE_AUDIENCE:-true}
|
||||||
NETBIRD_DASH_AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
NETBIRD_DASH_AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
||||||
|
|
||||||
|
# Store config
|
||||||
|
NETBIRD_STORE_CONFIG_ENGINE=${NETBIRD_STORE_CONFIG_ENGINE:-"jsonfile"}
|
||||||
|
|
||||||
# exports
|
# exports
|
||||||
export NETBIRD_DOMAIN
|
export NETBIRD_DOMAIN
|
||||||
export NETBIRD_AUTH_CLIENT_ID
|
export NETBIRD_AUTH_CLIENT_ID
|
||||||
@@ -86,6 +90,7 @@ export LETSENCRYPT_VOLUMESUFFIX
|
|||||||
export NETBIRD_DISABLE_ANONYMOUS_METRICS
|
export NETBIRD_DISABLE_ANONYMOUS_METRICS
|
||||||
export NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN
|
export NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN
|
||||||
export NETBIRD_MGMT_DNS_DOMAIN
|
export NETBIRD_MGMT_DNS_DOMAIN
|
||||||
|
export NETBIRD_MGMT_IDP_SIGNKEY_REFRESH
|
||||||
export NETBIRD_SIGNAL_PROTOCOL
|
export NETBIRD_SIGNAL_PROTOCOL
|
||||||
export NETBIRD_SIGNAL_PORT
|
export NETBIRD_SIGNAL_PORT
|
||||||
export NETBIRD_AUTH_USER_ID_CLAIM
|
export NETBIRD_AUTH_USER_ID_CLAIM
|
||||||
@@ -98,3 +103,4 @@ export NETBIRD_AUTH_PKCE_USE_ID_TOKEN
|
|||||||
export NETBIRD_AUTH_PKCE_AUDIENCE
|
export NETBIRD_AUTH_PKCE_AUDIENCE
|
||||||
export NETBIRD_DASH_AUTH_USE_AUDIENCE
|
export NETBIRD_DASH_AUTH_USE_AUDIENCE
|
||||||
export NETBIRD_DASH_AUTH_AUDIENCE
|
export NETBIRD_DASH_AUTH_AUDIENCE
|
||||||
|
export NETBIRD_STORE_CONFIG_ENGINE
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
if ! which curl >/dev/null 2>&1; then
|
if ! which curl >/dev/null 2>&1; then
|
||||||
echo "This script uses curl fetch OpenID configuration from IDP."
|
echo "This script uses curl fetch OpenID configuration from IDP."
|
||||||
@@ -124,7 +125,7 @@ if [[ "$NETBIRD_DISABLE_LETSENCRYPT" == "true" ]]; then
|
|||||||
echo "- $NETBIRD_SIGNAL_ENDPOINT/signalexchange.SignalExchange/ -grpc-> signal:80"
|
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 "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 " 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 occurences of the Letsencrypt-volume $LETSENCRYPT_VOLUMENAME"
|
echo "You are also free to remove any occurrences of the Letsencrypt-volume $LETSENCRYPT_VOLUMENAME"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
export NETBIRD_SIGNAL_PROTOCOL="https"
|
export NETBIRD_SIGNAL_PROTOCOL="https"
|
||||||
@@ -154,6 +155,8 @@ if [ -n "$NETBIRD_MGMT_IDP" ]; then
|
|||||||
export NETBIRD_IDP_MGMT_CLIENT_ID
|
export NETBIRD_IDP_MGMT_CLIENT_ID
|
||||||
export NETBIRD_IDP_MGMT_CLIENT_SECRET
|
export NETBIRD_IDP_MGMT_CLIENT_SECRET
|
||||||
export NETBIRD_IDP_MGMT_EXTRA_CONFIG=$EXTRA_CONFIG
|
export NETBIRD_IDP_MGMT_EXTRA_CONFIG=$EXTRA_CONFIG
|
||||||
|
else
|
||||||
|
export NETBIRD_IDP_MGMT_EXTRA_CONFIG={}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
IFS=',' read -r -a REDIRECT_URL_PORTS <<< "$NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS"
|
IFS=',' read -r -a REDIRECT_URL_PORTS <<< "$NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS"
|
||||||
@@ -170,8 +173,29 @@ if [ "$NETBIRD_DASH_AUTH_USE_AUDIENCE" = "false" ]; then
|
|||||||
export NETBIRD_AUTH_PKCE_AUDIENCE=
|
export NETBIRD_AUTH_PKCE_AUDIENCE=
|
||||||
fi
|
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
|
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 <docker-compose.yml.tmpl >docker-compose.yml
|
||||||
envsubst <management.json.tmpl >management.json
|
envsubst <management.json.tmpl | jq . >management.json
|
||||||
envsubst <turnserver.conf.tmpl >turnserver.conf
|
envsubst <turnserver.conf.tmpl >turnserver.conf
|
||||||
@@ -27,6 +27,10 @@
|
|||||||
"Password": null
|
"Password": null
|
||||||
},
|
},
|
||||||
"Datadir": "",
|
"Datadir": "",
|
||||||
|
"DataStoreEncryptionKey": "$NETBIRD_DATASTORE_ENC_KEY",
|
||||||
|
"StoreConfig": {
|
||||||
|
"Engine": "$NETBIRD_STORE_CONFIG_ENGINE"
|
||||||
|
},
|
||||||
"HttpConfig": {
|
"HttpConfig": {
|
||||||
"Address": "0.0.0.0:$NETBIRD_MGMT_API_PORT",
|
"Address": "0.0.0.0:$NETBIRD_MGMT_API_PORT",
|
||||||
"AuthIssuer": "$NETBIRD_AUTH_AUTHORITY",
|
"AuthIssuer": "$NETBIRD_AUTH_AUTHORITY",
|
||||||
@@ -35,6 +39,7 @@
|
|||||||
"AuthUserIDClaim": "$NETBIRD_AUTH_USER_ID_CLAIM",
|
"AuthUserIDClaim": "$NETBIRD_AUTH_USER_ID_CLAIM",
|
||||||
"CertFile":"$NETBIRD_MGMT_API_CERT_FILE",
|
"CertFile":"$NETBIRD_MGMT_API_CERT_FILE",
|
||||||
"CertKey":"$NETBIRD_MGMT_API_CERT_KEY_FILE",
|
"CertKey":"$NETBIRD_MGMT_API_CERT_KEY_FILE",
|
||||||
|
"IdpSignKeyRefreshEnabled": $NETBIRD_MGMT_IDP_SIGNKEY_REFRESH,
|
||||||
"OIDCConfigEndpoint":"$NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT"
|
"OIDCConfigEndpoint":"$NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT"
|
||||||
},
|
},
|
||||||
"IdpManagerConfig": {
|
"IdpManagerConfig": {
|
||||||
@@ -46,18 +51,25 @@
|
|||||||
"ClientSecret": "$NETBIRD_IDP_MGMT_CLIENT_SECRET",
|
"ClientSecret": "$NETBIRD_IDP_MGMT_CLIENT_SECRET",
|
||||||
"GrantType": "client_credentials"
|
"GrantType": "client_credentials"
|
||||||
},
|
},
|
||||||
"ExtraConfig": $NETBIRD_IDP_MGMT_EXTRA_CONFIG
|
"ExtraConfig": $NETBIRD_IDP_MGMT_EXTRA_CONFIG,
|
||||||
|
"Auth0ClientCredentials": null,
|
||||||
|
"AzureClientCredentials": null,
|
||||||
|
"KeycloakClientCredentials": null,
|
||||||
|
"ZitadelClientCredentials": null
|
||||||
},
|
},
|
||||||
"DeviceAuthorizationFlow": {
|
"DeviceAuthorizationFlow": {
|
||||||
"Provider": "$NETBIRD_AUTH_DEVICE_AUTH_PROVIDER",
|
"Provider": "$NETBIRD_AUTH_DEVICE_AUTH_PROVIDER",
|
||||||
"ProviderConfig": {
|
"ProviderConfig": {
|
||||||
"Audience": "$NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE",
|
"Audience": "$NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE",
|
||||||
|
"AuthorizationEndpoint": "",
|
||||||
"Domain": "$NETBIRD_AUTH0_DOMAIN",
|
"Domain": "$NETBIRD_AUTH0_DOMAIN",
|
||||||
"ClientID": "$NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID",
|
"ClientID": "$NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID",
|
||||||
|
"ClientSecret": "",
|
||||||
"TokenEndpoint": "$NETBIRD_AUTH_TOKEN_ENDPOINT",
|
"TokenEndpoint": "$NETBIRD_AUTH_TOKEN_ENDPOINT",
|
||||||
"DeviceAuthEndpoint": "$NETBIRD_AUTH_DEVICE_AUTH_ENDPOINT",
|
"DeviceAuthEndpoint": "$NETBIRD_AUTH_DEVICE_AUTH_ENDPOINT",
|
||||||
"Scope": "$NETBIRD_AUTH_DEVICE_AUTH_SCOPE",
|
"Scope": "$NETBIRD_AUTH_DEVICE_AUTH_SCOPE",
|
||||||
"UseIDToken": $NETBIRD_AUTH_DEVICE_AUTH_USE_ID_TOKEN
|
"UseIDToken": $NETBIRD_AUTH_DEVICE_AUTH_USE_ID_TOKEN,
|
||||||
|
"RedirectURLs": null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"PKCEAuthorizationFlow": {
|
"PKCEAuthorizationFlow": {
|
||||||
@@ -65,6 +77,7 @@
|
|||||||
"Audience": "$NETBIRD_AUTH_PKCE_AUDIENCE",
|
"Audience": "$NETBIRD_AUTH_PKCE_AUDIENCE",
|
||||||
"ClientID": "$NETBIRD_AUTH_CLIENT_ID",
|
"ClientID": "$NETBIRD_AUTH_CLIENT_ID",
|
||||||
"ClientSecret": "$NETBIRD_AUTH_CLIENT_SECRET",
|
"ClientSecret": "$NETBIRD_AUTH_CLIENT_SECRET",
|
||||||
|
"Domain": "",
|
||||||
"AuthorizationEndpoint": "$NETBIRD_AUTH_PKCE_AUTHORIZATION_ENDPOINT",
|
"AuthorizationEndpoint": "$NETBIRD_AUTH_PKCE_AUTHORIZATION_ENDPOINT",
|
||||||
"TokenEndpoint": "$NETBIRD_AUTH_TOKEN_ENDPOINT",
|
"TokenEndpoint": "$NETBIRD_AUTH_TOKEN_ENDPOINT",
|
||||||
"Scope": "$NETBIRD_AUTH_SUPPORTED_SCOPES",
|
"Scope": "$NETBIRD_AUTH_SUPPORTED_SCOPES",
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ NETBIRD_MGMT_IDP="none"
|
|||||||
# Some IDPs requires different client id and client secret for management api
|
# Some IDPs requires different client id and client secret for management api
|
||||||
NETBIRD_IDP_MGMT_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
NETBIRD_IDP_MGMT_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
||||||
NETBIRD_IDP_MGMT_CLIENT_SECRET=""
|
NETBIRD_IDP_MGMT_CLIENT_SECRET=""
|
||||||
|
# With some IDPs may be needed enabling automatic refresh of signing keys on expire
|
||||||
|
# NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=false
|
||||||
# NETBIRD_IDP_MGMT_EXTRA_ variables. See https://docs.netbird.io/selfhosted/identity-providers for more information about your IDP of choice.
|
# NETBIRD_IDP_MGMT_EXTRA_ variables. See https://docs.netbird.io/selfhosted/identity-providers for more information about your IDP of choice.
|
||||||
# -------------------------------------------
|
# -------------------------------------------
|
||||||
# Letsencrypt
|
# Letsencrypt
|
||||||
|
|||||||
@@ -23,3 +23,5 @@ NETBIRD_MGMT_IDP=$CI_NETBIRD_MGMT_IDP
|
|||||||
NETBIRD_IDP_MGMT_CLIENT_ID=$CI_NETBIRD_IDP_MGMT_CLIENT_ID
|
NETBIRD_IDP_MGMT_CLIENT_ID=$CI_NETBIRD_IDP_MGMT_CLIENT_ID
|
||||||
NETBIRD_IDP_MGMT_CLIENT_SECRET=$CI_NETBIRD_IDP_MGMT_CLIENT_SECRET
|
NETBIRD_IDP_MGMT_CLIENT_SECRET=$CI_NETBIRD_IDP_MGMT_CLIENT_SECRET
|
||||||
NETBIRD_SIGNAL_PORT=12345
|
NETBIRD_SIGNAL_PORT=12345
|
||||||
|
NETBIRD_STORE_CONFIG_ENGINE=$CI_NETBIRD_STORE_CONFIG_ENGINE
|
||||||
|
NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=$CI_NETBIRD_MGMT_IDP_SIGNKEY_REFRESH
|
||||||
@@ -696,7 +696,7 @@ no-cli
|
|||||||
#web-admin-port=8080
|
#web-admin-port=8080
|
||||||
|
|
||||||
# Web-admin server listen on STUN/TURN worker threads
|
# Web-admin server listen on STUN/TURN worker threads
|
||||||
# By default it is disabled for security resons! (Not recommended in any production environment!)
|
# By default it is disabled for security reasons! (Not recommended in any production environment!)
|
||||||
#
|
#
|
||||||
#web-admin-listen-on-workers
|
#web-admin-listen-on-workers
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/encryption"
|
"github.com/netbirdio/netbird/encryption"
|
||||||
"github.com/netbirdio/netbird/management/proto"
|
|
||||||
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
||||||
mgmt "github.com/netbirdio/netbird/management/server"
|
mgmt "github.com/netbirdio/netbird/management/server"
|
||||||
"github.com/netbirdio/netbird/management/server/mock_server"
|
"github.com/netbirdio/netbird/management/server/mock_server"
|
||||||
@@ -53,7 +52,7 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
s := grpc.NewServer()
|
s := grpc.NewServer()
|
||||||
store, err := mgmt.NewFileStore(config.Datadir, nil)
|
store, err := mgmt.NewStoreFromJson(config.Datadir, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -95,8 +94,8 @@ func startMockManagement(t *testing.T) (*grpc.Server, net.Listener, *mock_server
|
|||||||
}
|
}
|
||||||
|
|
||||||
mgmtMockServer := &mock_server.ManagementServiceServerMock{
|
mgmtMockServer := &mock_server.ManagementServiceServerMock{
|
||||||
GetServerKeyFunc: func(context.Context, *proto.Empty) (*proto.ServerKeyResponse, error) {
|
GetServerKeyFunc: func(context.Context, *mgmtProto.Empty) (*mgmtProto.ServerKeyResponse, error) {
|
||||||
response := &proto.ServerKeyResponse{
|
response := &mgmtProto.ServerKeyResponse{
|
||||||
Key: serverKey.PublicKey().String(),
|
Key: serverKey.PublicKey().String(),
|
||||||
}
|
}
|
||||||
return response, nil
|
return response, nil
|
||||||
@@ -300,19 +299,19 @@ func Test_SystemMetaDataFromClient(t *testing.T) {
|
|||||||
log.Fatalf("error while getting server public key from testclient, %v", err)
|
log.Fatalf("error while getting server public key from testclient, %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var actualMeta *proto.PeerSystemMeta
|
var actualMeta *mgmtProto.PeerSystemMeta
|
||||||
var actualValidKey string
|
var actualValidKey string
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
mgmtMockServer.LoginFunc = func(ctx context.Context, msg *proto.EncryptedMessage) (*proto.EncryptedMessage, error) {
|
mgmtMockServer.LoginFunc = func(ctx context.Context, msg *mgmtProto.EncryptedMessage) (*mgmtProto.EncryptedMessage, error) {
|
||||||
peerKey, err := wgtypes.ParseKey(msg.GetWgPubKey())
|
peerKey, err := wgtypes.ParseKey(msg.GetWgPubKey())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("error while parsing peer's Wireguard public key %s on Sync request.", msg.WgPubKey)
|
log.Warnf("error while parsing peer's Wireguard public key %s on Sync request.", msg.WgPubKey)
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "provided wgPubKey %s is invalid", msg.WgPubKey)
|
return nil, status.Errorf(codes.InvalidArgument, "provided wgPubKey %s is invalid", msg.WgPubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
loginReq := &proto.LoginRequest{}
|
loginReq := &mgmtProto.LoginRequest{}
|
||||||
err = encryption.DecryptMessage(peerKey, serverKey, msg.Body, loginReq)
|
err = encryption.DecryptMessage(peerKey, serverKey, msg.Body, loginReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -322,7 +321,7 @@ func Test_SystemMetaDataFromClient(t *testing.T) {
|
|||||||
actualValidKey = loginReq.GetSetupKey()
|
actualValidKey = loginReq.GetSetupKey()
|
||||||
wg.Done()
|
wg.Done()
|
||||||
|
|
||||||
loginResp := &proto.LoginResponse{}
|
loginResp := &mgmtProto.LoginResponse{}
|
||||||
encryptedResp, err := encryption.EncryptMessage(peerKey, serverKey, loginResp)
|
encryptedResp, err := encryption.EncryptMessage(peerKey, serverKey, loginResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -343,7 +342,7 @@ func Test_SystemMetaDataFromClient(t *testing.T) {
|
|||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
expectedMeta := &proto.PeerSystemMeta{
|
expectedMeta := &mgmtProto.PeerSystemMeta{
|
||||||
Hostname: info.Hostname,
|
Hostname: info.Hostname,
|
||||||
GoOS: info.GoOS,
|
GoOS: info.GoOS,
|
||||||
Kernel: info.Kernel,
|
Kernel: info.Kernel,
|
||||||
@@ -374,12 +373,12 @@ func Test_GetDeviceAuthorizationFlow(t *testing.T) {
|
|||||||
log.Fatalf("error while creating testClient: %v", err)
|
log.Fatalf("error while creating testClient: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedFlowInfo := &proto.DeviceAuthorizationFlow{
|
expectedFlowInfo := &mgmtProto.DeviceAuthorizationFlow{
|
||||||
Provider: 0,
|
Provider: 0,
|
||||||
ProviderConfig: &proto.ProviderConfig{ClientID: "client"},
|
ProviderConfig: &mgmtProto.ProviderConfig{ClientID: "client"},
|
||||||
}
|
}
|
||||||
|
|
||||||
mgmtMockServer.GetDeviceAuthorizationFlowFunc = func(ctx context.Context, req *mgmtProto.EncryptedMessage) (*proto.EncryptedMessage, error) {
|
mgmtMockServer.GetDeviceAuthorizationFlowFunc = func(ctx context.Context, req *mgmtProto.EncryptedMessage) (*mgmtProto.EncryptedMessage, error) {
|
||||||
encryptedResp, err := encryption.EncryptMessage(serverKey, client.key, expectedFlowInfo)
|
encryptedResp, err := encryption.EncryptMessage(serverKey, client.key, expectedFlowInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -418,14 +417,14 @@ func Test_GetPKCEAuthorizationFlow(t *testing.T) {
|
|||||||
log.Fatalf("error while creating testClient: %v", err)
|
log.Fatalf("error while creating testClient: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedFlowInfo := &proto.PKCEAuthorizationFlow{
|
expectedFlowInfo := &mgmtProto.PKCEAuthorizationFlow{
|
||||||
ProviderConfig: &proto.ProviderConfig{
|
ProviderConfig: &mgmtProto.ProviderConfig{
|
||||||
ClientID: "client",
|
ClientID: "client",
|
||||||
ClientSecret: "secret",
|
ClientSecret: "secret",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
mgmtMockServer.GetPKCEAuthorizationFlowFunc = func(ctx context.Context, req *mgmtProto.EncryptedMessage) (*proto.EncryptedMessage, error) {
|
mgmtMockServer.GetPKCEAuthorizationFlowFunc = func(ctx context.Context, req *mgmtProto.EncryptedMessage) (*mgmtProto.EncryptedMessage, error) {
|
||||||
encryptedResp, err := encryption.EncryptMessage(serverKey, client.key, expectedFlowInfo)
|
encryptedResp, err := encryption.EncryptMessage(serverKey, client.key, expectedFlowInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsE
|
|||||||
transportOption,
|
transportOption,
|
||||||
grpc.WithBlock(),
|
grpc.WithBlock(),
|
||||||
grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||||
Time: 15 * time.Second,
|
Time: 30 * time.Second,
|
||||||
Timeout: 10 * time.Second,
|
Timeout: 10 * time.Second,
|
||||||
}))
|
}))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ var (
|
|||||||
|
|
||||||
_, valid := dns.IsDomainName(dnsDomain)
|
_, valid := dns.IsDomainName(dnsDomain)
|
||||||
if !valid || len(dnsDomain) > 192 {
|
if !valid || len(dnsDomain) > 192 {
|
||||||
return fmt.Errorf("failed parsing the provided dns-domain. Valid status: %t, Lenght: %d", valid, len(dnsDomain))
|
return fmt.Errorf("failed parsing the provided dns-domain. Valid status: %t, Length: %d", valid, len(dnsDomain))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -126,7 +126,7 @@ var (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
store, err := server.NewFileStore(config.Datadir, appMetrics)
|
store, err := server.NewStore(config.StoreConfig.Engine, config.Datadir, appMetrics)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed creating Store: %s: %v", config.Datadir, err)
|
return fmt.Errorf("failed creating Store: %s: %v", config.Datadir, err)
|
||||||
}
|
}
|
||||||
|
|||||||
66
management/cmd/migration_down.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server"
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var shortDown = "Rollback SQLite store to JSON file store. Please make a backup of the SQLite file before running this command."
|
||||||
|
|
||||||
|
var downCmd = &cobra.Command{
|
||||||
|
Use: "downgrade [--datadir directory] [--log-file console]",
|
||||||
|
Aliases: []string{"down"},
|
||||||
|
Short: shortDown,
|
||||||
|
Long: shortDown +
|
||||||
|
"\n\n" +
|
||||||
|
"This command reads the content of {datadir}/store.db and migrates it to {datadir}/store.json that can be used by File store driver.",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
flag.Parse()
|
||||||
|
err := util.InitLog(logLevel, logFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed initializing log %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sqliteStorePath := path.Join(mgmtDataDir, "store.db")
|
||||||
|
if _, err := os.Stat(sqliteStorePath); errors.Is(err, os.ErrNotExist) {
|
||||||
|
return fmt.Errorf("%s doesn't exist, couldn't continue the operation", sqliteStorePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStorePath := path.Join(mgmtDataDir, "store.json")
|
||||||
|
if _, err := os.Stat(fileStorePath); err == nil {
|
||||||
|
return fmt.Errorf("%s already exists, couldn't continue the operation", fileStorePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlstore, err := server.NewSqliteStore(mgmtDataDir, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed creating file store: %s: %v", mgmtDataDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sqliteStoreAccounts := len(sqlstore.GetAllAccounts())
|
||||||
|
log.Infof("%d account will be migrated from sqlite store %s to file store %s",
|
||||||
|
sqliteStoreAccounts, sqliteStorePath, fileStorePath)
|
||||||
|
|
||||||
|
store, err := server.NewFilestoreFromSqliteStore(sqlstore, mgmtDataDir, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed creating file store: %s: %v", mgmtDataDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fsStoreAccounts := len(store.GetAllAccounts())
|
||||||
|
if fsStoreAccounts != sqliteStoreAccounts {
|
||||||
|
return fmt.Errorf("failed to migrate accounts from sqlite to file[]. Expected accounts: %d, got: %d",
|
||||||
|
sqliteStoreAccounts, fsStoreAccounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Migration finished successfully")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
66
management/cmd/migration_up.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server"
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var shortUp = "Migrate JSON file store to SQLite store. Please make a backup of the JSON file before running this command."
|
||||||
|
|
||||||
|
var upCmd = &cobra.Command{
|
||||||
|
Use: "upgrade [--datadir directory] [--log-file console]",
|
||||||
|
Aliases: []string{"up"},
|
||||||
|
Short: shortUp,
|
||||||
|
Long: shortUp +
|
||||||
|
"\n\n" +
|
||||||
|
"This command reads the content of {datadir}/store.json and migrates it to {datadir}/store.db that can be used by SQLite store driver.",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
flag.Parse()
|
||||||
|
err := util.InitLog(logLevel, logFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed initializing log %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStorePath := path.Join(mgmtDataDir, "store.json")
|
||||||
|
if _, err := os.Stat(fileStorePath); errors.Is(err, os.ErrNotExist) {
|
||||||
|
return fmt.Errorf("%s doesn't exist, couldn't continue the operation", fileStorePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlStorePath := path.Join(mgmtDataDir, "store.db")
|
||||||
|
if _, err := os.Stat(sqlStorePath); err == nil {
|
||||||
|
return fmt.Errorf("%s already exists, couldn't continue the operation", sqlStorePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
fstore, err := server.NewFileStore(mgmtDataDir, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed creating file store: %s: %v", mgmtDataDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fsStoreAccounts := len(fstore.GetAllAccounts())
|
||||||
|
log.Infof("%d account will be migrated from file store %s to sqlite store %s",
|
||||||
|
fsStoreAccounts, fileStorePath, sqlStorePath)
|
||||||
|
|
||||||
|
store, err := server.NewSqliteStoreFromFileStore(fstore, mgmtDataDir, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed creating file store: %s: %v", mgmtDataDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sqliteStoreAccounts := len(store.GetAllAccounts())
|
||||||
|
if fsStoreAccounts != sqliteStoreAccounts {
|
||||||
|
return fmt.Errorf("failed to migrate accounts from file to sqlite. Expected accounts: %d, got: %d",
|
||||||
|
fsStoreAccounts, sqliteStoreAccounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Migration finished successfully")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -34,6 +34,12 @@ var (
|
|||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
migrationCmd = &cobra.Command{
|
||||||
|
Use: "sqlite-migration",
|
||||||
|
Short: "Contains sub-commands to perform JSON file store to SQLite store migration and rollback",
|
||||||
|
Long: "",
|
||||||
|
SilenceUsage: true,
|
||||||
|
}
|
||||||
// Execution control channel for stopCh signal
|
// Execution control channel for stopCh signal
|
||||||
stopCh chan int
|
stopCh chan int
|
||||||
)
|
)
|
||||||
@@ -55,7 +61,7 @@ func init() {
|
|||||||
mgmtCmd.Flags().StringVar(&certFile, "cert-file", "", "Location of your SSL certificate. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect")
|
mgmtCmd.Flags().StringVar(&certFile, "cert-file", "", "Location of your SSL certificate. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect")
|
||||||
mgmtCmd.Flags().StringVar(&certKey, "cert-key", "", "Location of your SSL certificate private key. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect")
|
mgmtCmd.Flags().StringVar(&certKey, "cert-key", "", "Location of your SSL certificate private key. Can be used when you have an existing certificate and don't want a new certificate be generated automatically. If letsencrypt-domain is specified this property has no effect")
|
||||||
mgmtCmd.Flags().BoolVar(&disableMetrics, "disable-anonymous-metrics", false, "disables push of anonymous usage metrics to NetBird")
|
mgmtCmd.Flags().BoolVar(&disableMetrics, "disable-anonymous-metrics", false, "disables push of anonymous usage metrics to NetBird")
|
||||||
mgmtCmd.Flags().StringVar(&dnsDomain, "dns-domain", defaultSingleAccModeDomain, fmt.Sprintf("Domain used for peer resolution. This is appended to the peer's name, e.g. pi-server. %s. Max lenght is 192 characters to allow appending to a peer name with up to 63 characters.", defaultSingleAccModeDomain))
|
mgmtCmd.Flags().StringVar(&dnsDomain, "dns-domain", defaultSingleAccModeDomain, fmt.Sprintf("Domain used for peer resolution. This is appended to the peer's name, e.g. pi-server. %s. Max length is 192 characters to allow appending to a peer name with up to 63 characters.", defaultSingleAccModeDomain))
|
||||||
mgmtCmd.Flags().BoolVar(&idpSignKeyRefreshEnabled, "idp-sign-key-refresh-enabled", false, "Enable cache headers evaluation to determine signing key rotation period. This will refresh the signing key upon expiry.")
|
mgmtCmd.Flags().BoolVar(&idpSignKeyRefreshEnabled, "idp-sign-key-refresh-enabled", false, "Enable cache headers evaluation to determine signing key rotation period. This will refresh the signing key upon expiry.")
|
||||||
mgmtCmd.Flags().BoolVar(&userDeleteFromIDPEnabled, "user-delete-from-idp", false, "Allows to delete user from IDP when user is deleted from account")
|
mgmtCmd.Flags().BoolVar(&userDeleteFromIDPEnabled, "user-delete-from-idp", false, "Allows to delete user from IDP when user is deleted from account")
|
||||||
rootCmd.MarkFlagRequired("config") //nolint
|
rootCmd.MarkFlagRequired("config") //nolint
|
||||||
@@ -63,6 +69,14 @@ func init() {
|
|||||||
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "")
|
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "")
|
||||||
rootCmd.PersistentFlags().StringVar(&logFile, "log-file", defaultLogFile, "sets Netbird log path. If console is specified the the log will be output to stdout")
|
rootCmd.PersistentFlags().StringVar(&logFile, "log-file", defaultLogFile, "sets Netbird log path. If console is specified the the log will be output to stdout")
|
||||||
rootCmd.AddCommand(mgmtCmd)
|
rootCmd.AddCommand(mgmtCmd)
|
||||||
|
|
||||||
|
migrationCmd.PersistentFlags().StringVar(&mgmtDataDir, "datadir", defaultMgmtDataDir, "server data directory location")
|
||||||
|
migrationCmd.MarkFlagRequired("datadir") //nolint
|
||||||
|
|
||||||
|
migrationCmd.AddCommand(upCmd)
|
||||||
|
migrationCmd.AddCommand(downCmd)
|
||||||
|
|
||||||
|
rootCmd.AddCommand(migrationCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupCloseHandler handles SIGTERM signal and exits with success
|
// SetupCloseHandler handles SIGTERM signal and exits with success
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.26.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v3.21.12
|
// protoc v3.21.9
|
||||||
// source: management.proto
|
// source: management.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
@@ -1999,9 +1999,10 @@ type NameServerGroup struct {
|
|||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
NameServers []*NameServer `protobuf:"bytes,1,rep,name=NameServers,proto3" json:"NameServers,omitempty"`
|
NameServers []*NameServer `protobuf:"bytes,1,rep,name=NameServers,proto3" json:"NameServers,omitempty"`
|
||||||
Primary bool `protobuf:"varint,2,opt,name=Primary,proto3" json:"Primary,omitempty"`
|
Primary bool `protobuf:"varint,2,opt,name=Primary,proto3" json:"Primary,omitempty"`
|
||||||
Domains []string `protobuf:"bytes,3,rep,name=Domains,proto3" json:"Domains,omitempty"`
|
Domains []string `protobuf:"bytes,3,rep,name=Domains,proto3" json:"Domains,omitempty"`
|
||||||
|
SearchDomainsEnabled bool `protobuf:"varint,4,opt,name=SearchDomainsEnabled,proto3" json:"SearchDomainsEnabled,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *NameServerGroup) Reset() {
|
func (x *NameServerGroup) Reset() {
|
||||||
@@ -2057,6 +2058,13 @@ func (x *NameServerGroup) GetDomains() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *NameServerGroup) GetSearchDomainsEnabled() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.SearchDomainsEnabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// NameServer represents a dns.NameServer
|
// NameServer represents a dns.NameServer
|
||||||
type NameServer struct {
|
type NameServer struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
@@ -2444,73 +2452,76 @@ var file_management_proto_rawDesc = []byte{
|
|||||||
0x52, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x04,
|
0x52, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x04,
|
||||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x44, 0x61,
|
0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x44, 0x61,
|
||||||
0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x22,
|
0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x22,
|
||||||
0x7f, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f,
|
0xb3, 0x01, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72,
|
||||||
0x75, 0x70, 0x12, 0x38, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
0x6f, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
|
||||||
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
||||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52,
|
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
|
||||||
0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07,
|
0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a,
|
||||||
0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x50,
|
0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
|
||||||
0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||||
0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73,
|
0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
|
||||||
0x22, 0x48, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x0e,
|
0x73, 0x12, 0x32, 0x0a, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69,
|
||||||
0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16,
|
0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06,
|
0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e,
|
||||||
0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03,
|
0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x48, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72,
|
||||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0xf0, 0x02, 0x0a, 0x0c, 0x46,
|
0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x50,
|
0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
|
||||||
0x65, 0x65, 0x72, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x65, 0x65,
|
0x01, 0x28, 0x03, 0x52, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50,
|
||||||
0x72, 0x49, 0x50, 0x12, 0x40, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
0xf0, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65,
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65,
|
0x12, 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x2e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x44, 0x69, 0x72, 0x65,
|
0x52, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x12, 0x40, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65,
|
||||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18,
|
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x6d, 0x61,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c,
|
||||||
|
0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
|
||||||
|
0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x06, 0x41, 0x63,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e,
|
||||||
|
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c,
|
||||||
|
0x52, 0x75, 0x6c, 0x65, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74,
|
||||||
|
0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
|
||||||
|
0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
||||||
0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e,
|
0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e,
|
||||||
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d,
|
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
|
||||||
0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e,
|
0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x32, 0x21, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69,
|
0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
|
||||||
0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f,
|
||||||
0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a,
|
0x55, 0x54, 0x10, 0x01, 0x22, 0x1e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a,
|
||||||
0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x6f, 0x72,
|
0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x52,
|
||||||
0x74, 0x22, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x06,
|
0x4f, 0x50, 0x10, 0x01, 0x22, 0x3c, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
|
||||||
0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x22,
|
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a,
|
||||||
0x1e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x43,
|
0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12,
|
||||||
0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x52, 0x4f, 0x50, 0x10, 0x01, 0x22,
|
0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50,
|
||||||
0x3c, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55,
|
0x10, 0x04, 0x32, 0xd1, 0x03, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
|
||||||
0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10,
|
0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69,
|
||||||
0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44,
|
0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
|
||||||
0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50, 0x10, 0x04, 0x32, 0xd1, 0x03,
|
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a,
|
||||||
0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76,
|
0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63,
|
||||||
0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d,
|
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12,
|
||||||
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70,
|
0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||||
0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e,
|
|
||||||
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65,
|
|
||||||
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79,
|
|
||||||
0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
|
||||||
0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
|
||||||
0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e,
|
|
||||||
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00,
|
|
||||||
0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b,
|
|
||||||
0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
|
||||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
|
||||||
0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70,
|
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c,
|
|
||||||
0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
|
||||||
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
|
||||||
0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47,
|
|
||||||
0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
|
|
||||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
|
|
||||||
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64,
|
|
||||||
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
|
||||||
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65,
|
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65,
|
||||||
0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b,
|
0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
||||||
0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46,
|
0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73,
|
||||||
0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
|
0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65,
|
||||||
0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
|
||||||
0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
|
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e,
|
||||||
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
|
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65,
|
||||||
0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69,
|
||||||
0x74, 0x6f, 0x33,
|
0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
||||||
|
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61,
|
||||||
|
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00,
|
||||||
|
0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74,
|
||||||
|
0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c,
|
||||||
|
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72,
|
||||||
|
0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d,
|
||||||
|
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70,
|
||||||
|
0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18,
|
||||||
|
0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
|
||||||
|
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d,
|
||||||
|
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
||||||
|
0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73,
|
||||||
|
0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||||
|
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -317,6 +317,7 @@ message NameServerGroup {
|
|||||||
repeated NameServer NameServers = 1;
|
repeated NameServer NameServers = 1;
|
||||||
bool Primary = 2;
|
bool Primary = 2;
|
||||||
repeated string Domains = 3;
|
repeated string Domains = 3;
|
||||||
|
bool SearchDomainsEnabled = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NameServer represents a dns.NameServer
|
// NameServer represents a dns.NameServer
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const (
|
|||||||
UnknownCategory = "unknown"
|
UnknownCategory = "unknown"
|
||||||
GroupIssuedAPI = "api"
|
GroupIssuedAPI = "api"
|
||||||
GroupIssuedJWT = "jwt"
|
GroupIssuedJWT = "jwt"
|
||||||
|
GroupIssuedIntegration = "integration"
|
||||||
CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days
|
CacheExpirationMax = 7 * 24 * 3600 * time.Second // 7 days
|
||||||
CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days
|
CacheExpirationMin = 3 * 24 * 3600 * time.Second // 3 days
|
||||||
DefaultPeerLoginExpiration = 24 * time.Hour
|
DefaultPeerLoginExpiration = 24 * time.Hour
|
||||||
@@ -91,7 +92,7 @@ type AccountManager interface {
|
|||||||
DeleteRoute(accountID, routeID, userID string) error
|
DeleteRoute(accountID, routeID, userID string) error
|
||||||
ListRoutes(accountID, userID string) ([]*route.Route, error)
|
ListRoutes(accountID, userID string) ([]*route.Route, error)
|
||||||
GetNameServerGroup(accountID, nsGroupID string) (*nbdns.NameServerGroup, error)
|
GetNameServerGroup(accountID, nsGroupID string) (*nbdns.NameServerGroup, error)
|
||||||
CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool, userID string) (*nbdns.NameServerGroup, error)
|
CreateNameServerGroup(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, primary bool, domains []string, enabled bool, userID string, searchDomainsEnabled bool) (*nbdns.NameServerGroup, error)
|
||||||
SaveNameServerGroup(accountID, userID string, nsGroupToSave *nbdns.NameServerGroup) error
|
SaveNameServerGroup(accountID, userID string, nsGroupToSave *nbdns.NameServerGroup) error
|
||||||
DeleteNameServerGroup(accountID, nsGroupID, userID string) error
|
DeleteNameServerGroup(accountID, nsGroupID, userID string) error
|
||||||
ListNameServerGroups(accountID string) ([]*nbdns.NameServerGroup, error)
|
ListNameServerGroups(accountID string) ([]*nbdns.NameServerGroup, error)
|
||||||
@@ -103,6 +104,7 @@ type AccountManager interface {
|
|||||||
UpdateAccountSettings(accountID, userID string, newSettings *Settings) (*Account, error)
|
UpdateAccountSettings(accountID, userID string, newSettings *Settings) (*Account, error)
|
||||||
LoginPeer(login PeerLogin) (*Peer, *NetworkMap, error) // used by peer gRPC API
|
LoginPeer(login PeerLogin) (*Peer, *NetworkMap, error) // used by peer gRPC API
|
||||||
SyncPeer(sync PeerSync) (*Peer, *NetworkMap, error) // used by peer gRPC API
|
SyncPeer(sync PeerSync) (*Peer, *NetworkMap, error) // used by peer gRPC API
|
||||||
|
GetAllConnectedPeers() (map[string]struct{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultAccountManager struct {
|
type DefaultAccountManager struct {
|
||||||
@@ -164,43 +166,54 @@ func (s *Settings) Copy() *Settings {
|
|||||||
|
|
||||||
// Account represents a unique account of the system
|
// Account represents a unique account of the system
|
||||||
type Account struct {
|
type Account struct {
|
||||||
Id string
|
// we have to name column to aid as it collides with Network.Id when work with associations
|
||||||
|
Id string `gorm:"primaryKey"`
|
||||||
|
|
||||||
// User.Id it was created by
|
// User.Id it was created by
|
||||||
CreatedBy string
|
CreatedBy string
|
||||||
Domain string
|
Domain string `gorm:"index"`
|
||||||
DomainCategory string
|
DomainCategory string
|
||||||
IsDomainPrimaryAccount bool
|
IsDomainPrimaryAccount bool
|
||||||
SetupKeys map[string]*SetupKey
|
SetupKeys map[string]*SetupKey `gorm:"-"`
|
||||||
Network *Network
|
SetupKeysG []SetupKey `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
Peers map[string]*Peer
|
Network *Network `gorm:"embedded;embeddedPrefix:network_"`
|
||||||
Users map[string]*User
|
Peers map[string]*Peer `gorm:"-"`
|
||||||
Groups map[string]*Group
|
PeersG []Peer `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
Rules map[string]*Rule
|
Users map[string]*User `gorm:"-"`
|
||||||
Policies []*Policy
|
UsersG []User `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
Routes map[string]*route.Route
|
Groups map[string]*Group `gorm:"-"`
|
||||||
NameServerGroups map[string]*nbdns.NameServerGroup
|
GroupsG []Group `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
DNSSettings *DNSSettings
|
Rules map[string]*Rule `gorm:"-"`
|
||||||
|
RulesG []Rule `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
|
Policies []*Policy `gorm:"foreignKey:AccountID;references:id"`
|
||||||
|
Routes map[string]*route.Route `gorm:"-"`
|
||||||
|
RoutesG []route.Route `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
|
NameServerGroups map[string]*nbdns.NameServerGroup `gorm:"-"`
|
||||||
|
NameServerGroupsG []nbdns.NameServerGroup `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
|
DNSSettings DNSSettings `gorm:"embedded;embeddedPrefix:dns_settings_"`
|
||||||
// Settings is a dictionary of Account settings
|
// Settings is a dictionary of Account settings
|
||||||
Settings *Settings
|
Settings *Settings `gorm:"embedded;embeddedPrefix:settings_"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
AutoGroups []string `json:"auto_groups"`
|
AutoGroups []string `json:"auto_groups"`
|
||||||
Status string `json:"-"`
|
Status string `json:"-"`
|
||||||
IsServiceUser bool `json:"is_service_user"`
|
IsServiceUser bool `json:"is_service_user"`
|
||||||
IsBlocked bool `json:"is_blocked"`
|
IsBlocked bool `json:"is_blocked"`
|
||||||
LastLogin time.Time `json:"last_login"`
|
LastLogin time.Time `json:"last_login"`
|
||||||
|
Issued string `json:"issued"`
|
||||||
|
IntegrationReference IntegrationReference `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRoutesToSync returns the enabled routes for the peer ID and the routes
|
// getRoutesToSync returns the enabled routes for the peer ID and the routes
|
||||||
// from the ACL peers that have distribution groups associated with the peer ID.
|
// from the ACL peers that have distribution groups associated with the peer ID.
|
||||||
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
|
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
|
||||||
func (a *Account) getRoutesToSync(peerID string, aclPeers []*Peer) []*route.Route {
|
func (a *Account) getRoutesToSync(peerID string, aclPeers []*Peer) []*route.Route {
|
||||||
routes, peerDisabledRoutes := a.getEnabledAndDisabledRoutesByPeer(peerID)
|
routes, peerDisabledRoutes := a.getRoutingPeerRoutes(peerID)
|
||||||
peerRoutesMembership := make(lookupMap)
|
peerRoutesMembership := make(lookupMap)
|
||||||
for _, r := range append(routes, peerDisabledRoutes...) {
|
for _, r := range append(routes, peerDisabledRoutes...) {
|
||||||
peerRoutesMembership[route.GetHAUniqueID(r)] = struct{}{}
|
peerRoutesMembership[route.GetHAUniqueID(r)] = struct{}{}
|
||||||
@@ -208,7 +221,7 @@ func (a *Account) getRoutesToSync(peerID string, aclPeers []*Peer) []*route.Rout
|
|||||||
|
|
||||||
groupListMap := a.getPeerGroups(peerID)
|
groupListMap := a.getPeerGroups(peerID)
|
||||||
for _, peer := range aclPeers {
|
for _, peer := range aclPeers {
|
||||||
activeRoutes, _ := a.getEnabledAndDisabledRoutesByPeer(peer.ID)
|
activeRoutes, _ := a.getRoutingPeerRoutes(peer.ID)
|
||||||
groupFilteredRoutes := a.filterRoutesByGroups(activeRoutes, groupListMap)
|
groupFilteredRoutes := a.filterRoutesByGroups(activeRoutes, groupListMap)
|
||||||
filteredRoutes := a.filterRoutesFromPeersOfSameHAGroup(groupFilteredRoutes, peerRoutesMembership)
|
filteredRoutes := a.filterRoutesFromPeersOfSameHAGroup(groupFilteredRoutes, peerRoutesMembership)
|
||||||
routes = append(routes, filteredRoutes...)
|
routes = append(routes, filteredRoutes...)
|
||||||
@@ -244,20 +257,32 @@ func (a *Account) filterRoutesByGroups(routes []*route.Route, groupListMap looku
|
|||||||
return filteredRoutes
|
return filteredRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
// getEnabledAndDisabledRoutesByPeer returns the enabled and disabled lists of routes that belong to a peer.
|
// getRoutingPeerRoutes returns the enabled and disabled lists of routes that the given routing peer serves
|
||||||
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
|
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
|
||||||
func (a *Account) getEnabledAndDisabledRoutesByPeer(peerID string) ([]*route.Route, []*route.Route) {
|
// If the given is not a routing peer, then the lists are empty.
|
||||||
var enabledRoutes []*route.Route
|
func (a *Account) getRoutingPeerRoutes(peerID string) (enabledRoutes []*route.Route, disabledRoutes []*route.Route) {
|
||||||
var disabledRoutes []*route.Route
|
|
||||||
|
peer := a.GetPeer(peerID)
|
||||||
|
if peer == nil {
|
||||||
|
log.Errorf("peer %s that doesn't exist under account %s", peerID, a.Id)
|
||||||
|
return enabledRoutes, disabledRoutes
|
||||||
|
}
|
||||||
|
|
||||||
|
// currently we support only linux routing peers
|
||||||
|
if peer.Meta.GoOS != "linux" {
|
||||||
|
return enabledRoutes, disabledRoutes
|
||||||
|
}
|
||||||
|
|
||||||
|
seenRoute := make(map[string]struct{})
|
||||||
|
|
||||||
takeRoute := func(r *route.Route, id string) {
|
takeRoute := func(r *route.Route, id string) {
|
||||||
peer := a.GetPeer(peerID)
|
if _, ok := seenRoute[r.ID]; ok {
|
||||||
if peer == nil {
|
|
||||||
log.Errorf("route %s has peer %s that doesn't exist under account %s", r.ID, peerID, a.Id)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
seenRoute[r.ID] = struct{}{}
|
||||||
|
|
||||||
if r.Enabled {
|
if r.Enabled {
|
||||||
|
r.Peer = peer.Key
|
||||||
enabledRoutes = append(enabledRoutes, r)
|
enabledRoutes = append(enabledRoutes, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -265,25 +290,30 @@ func (a *Account) getEnabledAndDisabledRoutesByPeer(peerID string) ([]*route.Rou
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range a.Routes {
|
for _, r := range a.Routes {
|
||||||
if len(r.PeerGroups) != 0 {
|
for _, groupID := range r.PeerGroups {
|
||||||
for _, groupID := range r.PeerGroups {
|
group := a.GetGroup(groupID)
|
||||||
group := a.GetGroup(groupID)
|
if group == nil {
|
||||||
if group == nil {
|
log.Errorf("route %s has peers group %s that doesn't exist under account %s", r.ID, groupID, a.Id)
|
||||||
log.Errorf("route %s has peers group %s that doesn't exist under account %s", r.ID, groupID, a.Id)
|
continue
|
||||||
|
}
|
||||||
|
for _, id := range group.Peers {
|
||||||
|
if id != peerID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, id := range group.Peers {
|
|
||||||
if id == peerID {
|
newPeerRoute := r.Copy()
|
||||||
takeRoute(r, id)
|
newPeerRoute.Peer = id
|
||||||
break
|
newPeerRoute.PeerGroups = nil
|
||||||
}
|
newPeerRoute.ID = r.ID + ":" + id // we have to provide unique route id when distribute network map
|
||||||
}
|
takeRoute(newPeerRoute, id)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if r.Peer == peerID {
|
if r.Peer == peerID {
|
||||||
takeRoute(r, peerID)
|
takeRoute(r.Copy(), peerID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return enabledRoutes, disabledRoutes
|
return enabledRoutes, disabledRoutes
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,50 +349,7 @@ func (a *Account) GetPeerNetworkMap(peerID, dnsDomain string) *NetworkMap {
|
|||||||
peersToConnect = append(peersToConnect, p)
|
peersToConnect = append(peersToConnect, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
routes := a.getRoutesToSync(peerID, peersToConnect)
|
routesUpdate := a.getRoutesToSync(peerID, peersToConnect)
|
||||||
|
|
||||||
takePeer := func(id string) (*Peer, bool) {
|
|
||||||
peer := a.GetPeer(id)
|
|
||||||
if peer == nil || peer.Meta.GoOS != "linux" {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return peer, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to set Peer.Key instead of Peer.ID because this object will be sent to agents as part of a network map.
|
|
||||||
// Ideally we should have a separate field for that, but fine for now.
|
|
||||||
var routesUpdate []*route.Route
|
|
||||||
seenPeers := make(map[string]bool)
|
|
||||||
for _, r := range routes {
|
|
||||||
if r.Peer != "" {
|
|
||||||
peer, valid := takePeer(r.Peer)
|
|
||||||
if !valid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rCopy := r.Copy()
|
|
||||||
rCopy.Peer = peer.Key // client expects the key
|
|
||||||
routesUpdate = append(routesUpdate, rCopy)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, groupID := range r.PeerGroups {
|
|
||||||
if group := a.GetGroup(groupID); group != nil {
|
|
||||||
for _, peerId := range group.Peers {
|
|
||||||
peer, valid := takePeer(peerId)
|
|
||||||
if !valid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := seenPeers[peer.ID]; !ok {
|
|
||||||
rCopy := r.Copy()
|
|
||||||
rCopy.ID = r.ID + ":" + peer.ID // we have to provide unit route id when distribute network map
|
|
||||||
rCopy.Peer = peer.Key // client expects the key
|
|
||||||
routesUpdate = append(routesUpdate, rCopy)
|
|
||||||
}
|
|
||||||
seenPeers[peer.ID] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsManagementStatus := a.getPeerDNSManagementStatus(peerID)
|
dnsManagementStatus := a.getPeerDNSManagementStatus(peerID)
|
||||||
dnsUpdate := nbdns.Config{
|
dnsUpdate := nbdns.Config{
|
||||||
@@ -538,13 +525,11 @@ func (a *Account) getUserGroups(userID string) ([]string, error) {
|
|||||||
func (a *Account) getPeerDNSManagementStatus(peerID string) bool {
|
func (a *Account) getPeerDNSManagementStatus(peerID string) bool {
|
||||||
peerGroups := a.getPeerGroups(peerID)
|
peerGroups := a.getPeerGroups(peerID)
|
||||||
enabled := true
|
enabled := true
|
||||||
if a.DNSSettings != nil {
|
for _, groupID := range a.DNSSettings.DisabledManagementGroups {
|
||||||
for _, groupID := range a.DNSSettings.DisabledManagementGroups {
|
_, found := peerGroups[groupID]
|
||||||
_, found := peerGroups[groupID]
|
if found {
|
||||||
if found {
|
enabled = false
|
||||||
enabled = false
|
break
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return enabled
|
return enabled
|
||||||
@@ -631,10 +616,7 @@ func (a *Account) Copy() *Account {
|
|||||||
nsGroups[id] = nsGroup.Copy()
|
nsGroups[id] = nsGroup.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
var dnsSettings *DNSSettings
|
dnsSettings := a.DNSSettings.Copy()
|
||||||
if a.DNSSettings != nil {
|
|
||||||
dnsSettings = a.DNSSettings.Copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
var settings *Settings
|
var settings *Settings
|
||||||
if a.Settings != nil {
|
if a.Settings != nil {
|
||||||
@@ -972,6 +954,7 @@ func (am *DefaultAccountManager) warmupIDPCache() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
log.Infof("%d entries received from IdP management", len(userData))
|
||||||
|
|
||||||
// If the Identity Provider does not support writing AppMetadata,
|
// If the Identity Provider does not support writing AppMetadata,
|
||||||
// in cases like this, we expect it to return all users in an "unset" field.
|
// in cases like this, we expect it to return all users in an "unset" field.
|
||||||
@@ -1071,6 +1054,7 @@ func (am *DefaultAccountManager) loadAccount(_ context.Context, accountID interf
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
log.Debugf("%d entries received from IdP management", len(userData))
|
||||||
|
|
||||||
dataMap := make(map[string]*idp.UserData, len(userData))
|
dataMap := make(map[string]*idp.UserData, len(userData))
|
||||||
for _, datum := range userData {
|
for _, datum := range userData {
|
||||||
@@ -1582,6 +1566,11 @@ func (am *DefaultAccountManager) getAccountWithAuthorizationClaims(claims jwtcla
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllConnectedPeers returns connected peers based on peersUpdateManager.GetAllConnectedPeers()
|
||||||
|
func (am *DefaultAccountManager) GetAllConnectedPeers() (map[string]struct{}, error) {
|
||||||
|
return am.peersUpdateManager.GetAllConnectedPeers(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func isDomainValid(domain string) bool {
|
func isDomainValid(domain string) bool {
|
||||||
re := regexp.MustCompile(`^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$`)
|
re := regexp.MustCompile(`^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$`)
|
||||||
return re.Match([]byte(domain))
|
return re.Match([]byte(domain))
|
||||||
@@ -1636,7 +1625,7 @@ func newAccountWithId(accountID, userID, domain string) *Account {
|
|||||||
setupKeys := map[string]*SetupKey{}
|
setupKeys := map[string]*SetupKey{}
|
||||||
nameServersGroups := make(map[string]*nbdns.NameServerGroup)
|
nameServersGroups := make(map[string]*nbdns.NameServerGroup)
|
||||||
users[userID] = NewAdminUser(userID)
|
users[userID] = NewAdminUser(userID)
|
||||||
dnsSettings := &DNSSettings{
|
dnsSettings := DNSSettings{
|
||||||
DisabledManagementGroups: make([]string, 0),
|
DisabledManagementGroups: make([]string, 0),
|
||||||
}
|
}
|
||||||
log.Debugf("created new account %s", accountID)
|
log.Debugf("created new account %s", accountID)
|
||||||
|
|||||||
@@ -198,11 +198,11 @@ func TestAccount_GetPeerNetworkMap(t *testing.T) {
|
|||||||
netIP := net.IP{100, 64, 0, 0}
|
netIP := net.IP{100, 64, 0, 0}
|
||||||
netMask := net.IPMask{255, 255, 0, 0}
|
netMask := net.IPMask{255, 255, 0, 0}
|
||||||
network := &Network{
|
network := &Network{
|
||||||
Id: "network",
|
Identifier: "network",
|
||||||
Net: net.IPNet{IP: netIP, Mask: netMask},
|
Net: net.IPNet{IP: netIP, Mask: netMask},
|
||||||
Dns: "netbird.selfhosted",
|
Dns: "netbird.selfhosted",
|
||||||
Serial: 0,
|
Serial: 0,
|
||||||
mu: sync.Mutex{},
|
mu: sync.Mutex{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range tt {
|
for _, testCase := range tt {
|
||||||
@@ -476,7 +476,7 @@ func TestDefaultAccountManager_GetGroupsFromTheToken(t *testing.T) {
|
|||||||
// as initAccount was created without account id we have to take the id after account initialization
|
// as initAccount was created without account id we have to take the id after account initialization
|
||||||
// that happens inside the GetAccountByUserOrAccountID where the id is getting generated
|
// that happens inside the GetAccountByUserOrAccountID where the id is getting generated
|
||||||
// it is important to set the id as it help to avoid creating additional account with empty Id and re-pointing indices to it
|
// it is important to set the id as it help to avoid creating additional account with empty Id and re-pointing indices to it
|
||||||
initAccount.Id = acc.Id
|
initAccount = acc
|
||||||
|
|
||||||
claims := jwtclaims.AuthorizationClaims{
|
claims := jwtclaims.AuthorizationClaims{
|
||||||
AccountId: accountID, // is empty as it is based on accountID right after initialization of initAccount
|
AccountId: accountID, // is empty as it is based on accountID right after initialization of initAccount
|
||||||
@@ -1025,7 +1025,6 @@ func TestAccountManager_NetworkUpdates(t *testing.T) {
|
|||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("delete peer update", func(t *testing.T) {
|
t.Run("delete peer update", func(t *testing.T) {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
@@ -1117,7 +1116,7 @@ func TestAccountManager_DeletePeer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if account.Network.CurrentSerial() != 2 {
|
if account.Network.CurrentSerial() != 2 {
|
||||||
t.Errorf("expecting Network Serial=%d to be incremented and be equal to 2 after adding and deleteing a peer", account.Network.CurrentSerial())
|
t.Errorf("expecting Network Serial=%d to be incremented and be equal to 2 after adding and deleting a peer", account.Network.CurrentSerial())
|
||||||
}
|
}
|
||||||
|
|
||||||
ev := getEvent(t, account.Id, manager, activity.PeerRemovedByUser)
|
ev := getEvent(t, account.Id, manager, activity.PeerRemovedByUser)
|
||||||
@@ -1237,7 +1236,7 @@ func TestAccount_GetRoutesToSync(t *testing.T) {
|
|||||||
}
|
}
|
||||||
account := &Account{
|
account := &Account{
|
||||||
Peers: map[string]*Peer{
|
Peers: map[string]*Peer{
|
||||||
"peer-1": {Key: "peer-1"}, "peer-2": {Key: "peer-2"}, "peer-3": {Key: "peer-1"},
|
"peer-1": {Key: "peer-1", Meta: PeerSystemMeta{GoOS: "linux"}}, "peer-2": {Key: "peer-2", Meta: PeerSystemMeta{GoOS: "linux"}}, "peer-3": {Key: "peer-1", Meta: PeerSystemMeta{GoOS: "linux"}},
|
||||||
},
|
},
|
||||||
Groups: map[string]*Group{"group1": {ID: "group1", Peers: []string{"peer-1", "peer-2"}}},
|
Groups: map[string]*Group{"group1": {ID: "group1", Peers: []string{"peer-1", "peer-2"}}},
|
||||||
Routes: map[string]*route.Route{
|
Routes: map[string]*route.Route{
|
||||||
@@ -1309,7 +1308,7 @@ func TestAccount_Copy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Network: &Network{
|
Network: &Network{
|
||||||
Id: "net1",
|
Identifier: "net1",
|
||||||
},
|
},
|
||||||
Peers: map[string]*Peer{
|
Peers: map[string]*Peer{
|
||||||
"peer1": {
|
"peer1": {
|
||||||
@@ -1374,7 +1373,7 @@ func TestAccount_Copy(t *testing.T) {
|
|||||||
NameServers: []nbdns.NameServer{},
|
NameServers: []nbdns.NameServer{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DNSSettings: &DNSSettings{DisabledManagementGroups: []string{}},
|
DNSSettings: DNSSettings{DisabledManagementGroups: []string{}},
|
||||||
Settings: &Settings{},
|
Settings: &Settings{},
|
||||||
}
|
}
|
||||||
err := hasNilField(account)
|
err := hasNilField(account)
|
||||||
@@ -1400,6 +1399,10 @@ func hasNilField(x interface{}) error {
|
|||||||
rv := reflect.ValueOf(x)
|
rv := reflect.ValueOf(x)
|
||||||
rv = rv.Elem()
|
rv = rv.Elem()
|
||||||
for i := 0; i < rv.NumField(); i++ {
|
for i := 0; i < rv.NumField(); i++ {
|
||||||
|
// skip gorm internal fields
|
||||||
|
if json, ok := rv.Type().Field(i).Tag.Lookup("json"); ok && json == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if f := rv.Field(i); f.IsValid() {
|
if f := rv.Field(i); f.IsValid() {
|
||||||
k := f.Kind()
|
k := f.Kind()
|
||||||
switch k {
|
switch k {
|
||||||
@@ -2045,7 +2048,7 @@ func createManager(t *testing.T) (*DefaultAccountManager, error) {
|
|||||||
|
|
||||||
func createStore(t *testing.T) (Store, error) {
|
func createStore(t *testing.T) (Store, error) {
|
||||||
dataDir := t.TempDir()
|
dataDir := t.TempDir()
|
||||||
store, err := NewFileStore(dataDir, nil)
|
store, err := NewStoreFromJson(dataDir, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,9 @@ const (
|
|||||||
"VALUES(?, ?, ?, ?, ?, ?)"
|
"VALUES(?, ?, ?, ?, ?, ?)"
|
||||||
|
|
||||||
insertDeleteUserQuery = `INSERT INTO deleted_users(id, email, name) VALUES(?, ?, ?)`
|
insertDeleteUserQuery = `INSERT INTO deleted_users(id, email, name) VALUES(?, ?, ?)`
|
||||||
|
|
||||||
|
fallbackName = "unknown"
|
||||||
|
fallbackEmail = "unknown@unknown.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store is the implementation of the activity.Store interface backed by SQLite
|
// Store is the implementation of the activity.Store interface backed by SQLite
|
||||||
@@ -128,6 +131,7 @@ func NewSQLiteStore(dataDir string, encryptionKey string) (*Store, error) {
|
|||||||
|
|
||||||
func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
|
func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
|
||||||
events := make([]*activity.Event, 0)
|
events := make([]*activity.Event, 0)
|
||||||
|
var cryptErr error
|
||||||
for result.Next() {
|
for result.Next() {
|
||||||
var id int64
|
var id int64
|
||||||
var operation activity.Activity
|
var operation activity.Activity
|
||||||
@@ -156,8 +160,8 @@ func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
|
|||||||
if targetUserName != nil {
|
if targetUserName != nil {
|
||||||
name, err := store.fieldEncrypt.Decrypt(*targetUserName)
|
name, err := store.fieldEncrypt.Decrypt(*targetUserName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to decrypt username for target id: %s", target)
|
cryptErr = fmt.Errorf("failed to decrypt username for target id: %s", target)
|
||||||
meta["username"] = ""
|
meta["username"] = fallbackName
|
||||||
} else {
|
} else {
|
||||||
meta["username"] = name
|
meta["username"] = name
|
||||||
}
|
}
|
||||||
@@ -166,8 +170,8 @@ func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
|
|||||||
if targetEmail != nil {
|
if targetEmail != nil {
|
||||||
email, err := store.fieldEncrypt.Decrypt(*targetEmail)
|
email, err := store.fieldEncrypt.Decrypt(*targetEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to decrypt email address for target id: %s", target)
|
cryptErr = fmt.Errorf("failed to decrypt email address for target id: %s", target)
|
||||||
meta["email"] = ""
|
meta["email"] = fallbackEmail
|
||||||
} else {
|
} else {
|
||||||
meta["email"] = email
|
meta["email"] = email
|
||||||
}
|
}
|
||||||
@@ -186,7 +190,8 @@ func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
|
|||||||
if initiatorName != nil {
|
if initiatorName != nil {
|
||||||
name, err := store.fieldEncrypt.Decrypt(*initiatorName)
|
name, err := store.fieldEncrypt.Decrypt(*initiatorName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to decrypt username of initiator: %s", initiator)
|
cryptErr = fmt.Errorf("failed to decrypt username of initiator: %s", initiator)
|
||||||
|
event.InitiatorName = fallbackName
|
||||||
} else {
|
} else {
|
||||||
event.InitiatorName = name
|
event.InitiatorName = name
|
||||||
}
|
}
|
||||||
@@ -195,7 +200,8 @@ func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
|
|||||||
if initiatorEmail != nil {
|
if initiatorEmail != nil {
|
||||||
email, err := store.fieldEncrypt.Decrypt(*initiatorEmail)
|
email, err := store.fieldEncrypt.Decrypt(*initiatorEmail)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to decrypt email address of initiator: %s", initiator)
|
cryptErr = fmt.Errorf("failed to decrypt email address of initiator: %s", initiator)
|
||||||
|
event.InitiatorEmail = fallbackEmail
|
||||||
} else {
|
} else {
|
||||||
event.InitiatorEmail = email
|
event.InitiatorEmail = email
|
||||||
}
|
}
|
||||||
@@ -204,6 +210,10 @@ func (store *Store) processResult(result *sql.Rows) ([]*activity.Event, error) {
|
|||||||
events = append(events, event)
|
events = append(events, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cryptErr != nil {
|
||||||
|
log.Warnf("%s", cryptErr)
|
||||||
|
}
|
||||||
|
|
||||||
return events, nil
|
return events, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ type Config struct {
|
|||||||
DeviceAuthorizationFlow *DeviceAuthorizationFlow
|
DeviceAuthorizationFlow *DeviceAuthorizationFlow
|
||||||
|
|
||||||
PKCEAuthorizationFlow *PKCEAuthorizationFlow
|
PKCEAuthorizationFlow *PKCEAuthorizationFlow
|
||||||
|
|
||||||
|
StoreConfig StoreConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAuthAudiences returns the audience from the http config and device authorization flow config
|
// GetAuthAudiences returns the audience from the http config and device authorization flow config
|
||||||
@@ -136,6 +138,11 @@ type ProviderConfig struct {
|
|||||||
RedirectURLs []string
|
RedirectURLs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StoreConfig contains Store configuration
|
||||||
|
type StoreConfig struct {
|
||||||
|
Engine StoreEngine
|
||||||
|
}
|
||||||
|
|
||||||
// validateURL validates input http url
|
// validateURL validates input http url
|
||||||
func validateURL(httpURL string) bool {
|
func validateURL(httpURL string) bool {
|
||||||
_, err := url.ParseRequestURI(httpURL)
|
_, err := url.ParseRequestURI(httpURL)
|
||||||
|
|||||||
@@ -20,23 +20,15 @@ type lookupMap map[string]struct{}
|
|||||||
// DNSSettings defines dns settings at the account level
|
// DNSSettings defines dns settings at the account level
|
||||||
type DNSSettings struct {
|
type DNSSettings struct {
|
||||||
// DisabledManagementGroups groups whose DNS management is disabled
|
// DisabledManagementGroups groups whose DNS management is disabled
|
||||||
DisabledManagementGroups []string
|
DisabledManagementGroups []string `gorm:"serializer:json"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy returns a copy of the DNS settings
|
// Copy returns a copy of the DNS settings
|
||||||
func (d *DNSSettings) Copy() *DNSSettings {
|
func (d DNSSettings) Copy() DNSSettings {
|
||||||
settings := &DNSSettings{
|
settings := DNSSettings{
|
||||||
DisabledManagementGroups: make([]string, 0),
|
DisabledManagementGroups: make([]string, len(d.DisabledManagementGroups)),
|
||||||
}
|
}
|
||||||
|
copy(settings.DisabledManagementGroups, d.DisabledManagementGroups)
|
||||||
if d == nil {
|
|
||||||
return settings
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.DisabledManagementGroups != nil && len(d.DisabledManagementGroups) > 0 {
|
|
||||||
settings.DisabledManagementGroups = d.DisabledManagementGroups[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return settings
|
return settings
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,12 +50,8 @@ func (am *DefaultAccountManager) GetDNSSettings(accountID string, userID string)
|
|||||||
if !user.IsAdmin() {
|
if !user.IsAdmin() {
|
||||||
return nil, status.Errorf(status.PermissionDenied, "only admins are allowed to view DNS settings")
|
return nil, status.Errorf(status.PermissionDenied, "only admins are allowed to view DNS settings")
|
||||||
}
|
}
|
||||||
|
dnsSettings := account.DNSSettings.Copy()
|
||||||
if account.DNSSettings == nil {
|
return &dnsSettings, nil
|
||||||
return &DNSSettings{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return account.DNSSettings.Copy(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveDNSSettings validates a user role and updates the account's DNS settings
|
// SaveDNSSettings validates a user role and updates the account's DNS settings
|
||||||
@@ -96,11 +84,7 @@ func (am *DefaultAccountManager) SaveDNSSettings(accountID string, userID string
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
oldSettings := &DNSSettings{}
|
oldSettings := account.DNSSettings.Copy()
|
||||||
if account.DNSSettings != nil {
|
|
||||||
oldSettings = account.DNSSettings.Copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
account.DNSSettings = dnsSettingsToSave.Copy()
|
account.DNSSettings = dnsSettingsToSave.Copy()
|
||||||
|
|
||||||
account.Network.IncSerial()
|
account.Network.IncSerial()
|
||||||
@@ -146,8 +130,9 @@ func toProtocolDNSConfig(update nbdns.Config) *proto.DNSConfig {
|
|||||||
|
|
||||||
for _, nsGroup := range update.NameServerGroups {
|
for _, nsGroup := range update.NameServerGroups {
|
||||||
protoGroup := &proto.NameServerGroup{
|
protoGroup := &proto.NameServerGroup{
|
||||||
Primary: nsGroup.Primary,
|
Primary: nsGroup.Primary,
|
||||||
Domains: nsGroup.Domains,
|
Domains: nsGroup.Domains,
|
||||||
|
SearchDomainsEnabled: nsGroup.SearchDomainsEnabled,
|
||||||
}
|
}
|
||||||
for _, ns := range nsGroup.NameServers {
|
for _, ns := range nsGroup.NameServers {
|
||||||
protoNS := &proto.NameServer{
|
protoNS := &proto.NameServer{
|
||||||
@@ -231,7 +216,7 @@ func addPeerLabelsToAccount(account *Account, peerLabels lookupMap) {
|
|||||||
log.Errorf("got an error while generating a peer host label. Peer name %s, error: %v. Trying with the peer's meta hostname", peer.Name, err)
|
log.Errorf("got an error while generating a peer host label. Peer name %s, error: %v. Trying with the peer's meta hostname", peer.Name, err)
|
||||||
label, err = getPeerHostLabel(peer.Meta.Hostname, peerLabels)
|
label, err = getPeerHostLabel(peer.Meta.Hostname, peerLabels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("got another error while generating a peer host label with hostname. Peer hostname %s, error: %v. Skiping", peer.Meta.Hostname, err)
|
log.Errorf("got another error while generating a peer host label with hostname. Peer hostname %s, error: %v. Skipping", peer.Meta.Hostname, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func TestGetDNSSettings(t *testing.T) {
|
|||||||
t.Fatal("DNS settings for new accounts shouldn't return nil")
|
t.Fatal("DNS settings for new accounts shouldn't return nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
account.DNSSettings = &DNSSettings{
|
account.DNSSettings = DNSSettings{
|
||||||
DisabledManagementGroups: []string{group1ID},
|
DisabledManagementGroups: []string{group1ID},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
|
|||||||
|
|
||||||
func createDNSStore(t *testing.T) (Store, error) {
|
func createDNSStore(t *testing.T) (Store, error) {
|
||||||
dataDir := t.TempDir()
|
dataDir := t.TempDir()
|
||||||
store, err := NewFileStore(dataDir, nil)
|
store, err := NewStoreFromJson(dataDir, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,25 @@ func NewFileStore(dataDir string, metrics telemetry.AppMetrics) (*FileStore, err
|
|||||||
return fs, nil
|
return fs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFilestoreFromSqliteStore restores a store from Sqlite and stores to Filestore json in the file located in datadir
|
||||||
|
func NewFilestoreFromSqliteStore(sqlitestore *SqliteStore, dataDir string, metrics telemetry.AppMetrics) (*FileStore, error) {
|
||||||
|
store, err := NewFileStore(dataDir, metrics)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = store.SaveInstallationID(sqlitestore.GetInstallationID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, account := range sqlitestore.GetAllAccounts() {
|
||||||
|
store.Accounts[account.Id] = account
|
||||||
|
}
|
||||||
|
|
||||||
|
return store, store.persist(store.storeFile)
|
||||||
|
}
|
||||||
|
|
||||||
// restore the state of the store from the file.
|
// restore the state of the store from the file.
|
||||||
// Creates a new empty store file if doesn't exist
|
// Creates a new empty store file if doesn't exist
|
||||||
func restore(file string) (*FileStore, error) {
|
func restore(file string) (*FileStore, error) {
|
||||||
@@ -111,13 +130,14 @@ func restore(file string) (*FileStore, error) {
|
|||||||
for _, peer := range account.Peers {
|
for _, peer := range account.Peers {
|
||||||
store.PeerKeyID2AccountID[peer.Key] = accountID
|
store.PeerKeyID2AccountID[peer.Key] = accountID
|
||||||
store.PeerID2AccountID[peer.ID] = accountID
|
store.PeerID2AccountID[peer.ID] = accountID
|
||||||
// reset all peers to status = Disconnected
|
|
||||||
if peer.Status != nil && peer.Status.Connected {
|
|
||||||
peer.Status.Connected = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for _, user := range account.Users {
|
for _, user := range account.Users {
|
||||||
store.UserID2AccountID[user.Id] = accountID
|
store.UserID2AccountID[user.Id] = accountID
|
||||||
|
if user.Issued == "" {
|
||||||
|
user.Issued = UserIssuedAPI
|
||||||
|
account.Users[user.Id] = user
|
||||||
|
}
|
||||||
|
|
||||||
for _, pat := range user.PATs {
|
for _, pat := range user.PATs {
|
||||||
store.TokenID2UserID[pat.ID] = user.Id
|
store.TokenID2UserID[pat.ID] = user.Id
|
||||||
store.HashedPAT2TokenID[pat.HashedToken] = pat.ID
|
store.HashedPAT2TokenID[pat.HashedToken] = pat.ID
|
||||||
@@ -599,3 +619,8 @@ func (s *FileStore) Close() error {
|
|||||||
|
|
||||||
return s.persist(s.storeFile)
|
return s.persist(s.storeFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStoreEngine returns FileStoreEngine
|
||||||
|
func (s *FileStore) GetStoreEngine() StoreEngine {
|
||||||
|
return FileStoreEngine
|
||||||
|
}
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ func TestFileStore_GetAccount(t *testing.T) {
|
|||||||
assert.Equal(t, expected.DomainCategory, account.DomainCategory)
|
assert.Equal(t, expected.DomainCategory, account.DomainCategory)
|
||||||
assert.Equal(t, expected.Domain, account.Domain)
|
assert.Equal(t, expected.Domain, account.Domain)
|
||||||
assert.Equal(t, expected.CreatedBy, account.CreatedBy)
|
assert.Equal(t, expected.CreatedBy, account.CreatedBy)
|
||||||
assert.Equal(t, expected.Network.Id, account.Network.Id)
|
assert.Equal(t, expected.Network.Identifier, account.Network.Identifier)
|
||||||
assert.Len(t, account.Peers, len(expected.Peers))
|
assert.Len(t, account.Peers, len(expected.Peers))
|
||||||
assert.Len(t, account.Users, len(expected.Users))
|
assert.Len(t, account.Users, len(expected.Users))
|
||||||
assert.Len(t, account.SetupKeys, len(expected.SetupKeys))
|
assert.Len(t, account.SetupKeys, len(expected.SetupKeys))
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ type Group struct {
|
|||||||
// ID of the group
|
// ID of the group
|
||||||
ID string
|
ID string
|
||||||
|
|
||||||
|
// AccountID is a reference to Account that this object belongs
|
||||||
|
AccountID string `json:"-" gorm:"index"`
|
||||||
|
|
||||||
// Name visible in the UI
|
// Name visible in the UI
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
@@ -30,7 +33,9 @@ type Group struct {
|
|||||||
Issued string
|
Issued string
|
||||||
|
|
||||||
// Peers list of the group
|
// Peers list of the group
|
||||||
Peers []string
|
Peers []string `gorm:"serializer:json"`
|
||||||
|
|
||||||
|
IntegrationReference IntegrationReference `gorm:"embedded;embeddedPrefix:integration_ref_"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventMeta returns activity event meta related to the group
|
// EventMeta returns activity event meta related to the group
|
||||||
@@ -40,10 +45,11 @@ func (g *Group) EventMeta() map[string]any {
|
|||||||
|
|
||||||
func (g *Group) Copy() *Group {
|
func (g *Group) Copy() *Group {
|
||||||
group := &Group{
|
group := &Group{
|
||||||
ID: g.ID,
|
ID: g.ID,
|
||||||
Name: g.Name,
|
Name: g.Name,
|
||||||
Issued: g.Issued,
|
Issued: g.Issued,
|
||||||
Peers: make([]string, len(g.Peers)),
|
Peers: make([]string, len(g.Peers)),
|
||||||
|
IntegrationReference: g.IntegrationReference,
|
||||||
}
|
}
|
||||||
copy(group.Peers, g.Peers)
|
copy(group.Peers, g.Peers)
|
||||||
return group
|
return group
|
||||||
@@ -157,6 +163,11 @@ func (am *DefaultAccountManager) DeleteGroup(accountId, userId, groupID string)
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check integration link
|
||||||
|
if g.Issued == GroupIssuedIntegration {
|
||||||
|
return &GroupLinkError{GroupIssuedIntegration, g.IntegrationReference.String()}
|
||||||
|
}
|
||||||
|
|
||||||
// check route links
|
// check route links
|
||||||
for _, r := range account.Routes {
|
for _, r := range account.Routes {
|
||||||
for _, g := range r.Groups {
|
for _, g := range r.Groups {
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ func TestDefaultAccountManager_DeleteGroup(t *testing.T) {
|
|||||||
"grp-for-users",
|
"grp-for-users",
|
||||||
"user",
|
"user",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"integration",
|
||||||
|
"grp-for-integration",
|
||||||
|
"integration",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
@@ -79,38 +84,51 @@ func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
|
|||||||
domain := "example.com"
|
domain := "example.com"
|
||||||
|
|
||||||
groupForRoute := &Group{
|
groupForRoute := &Group{
|
||||||
"grp-for-route",
|
ID: "grp-for-route",
|
||||||
"Group for route",
|
AccountID: "account-id",
|
||||||
GroupIssuedAPI,
|
Name: "Group for route",
|
||||||
make([]string, 0),
|
Issued: GroupIssuedAPI,
|
||||||
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForNameServerGroups := &Group{
|
groupForNameServerGroups := &Group{
|
||||||
"grp-for-name-server-grp",
|
ID: "grp-for-name-server-grp",
|
||||||
"Group for name server groups",
|
AccountID: "account-id",
|
||||||
GroupIssuedAPI,
|
Name: "Group for name server groups",
|
||||||
make([]string, 0),
|
Issued: GroupIssuedAPI,
|
||||||
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForPolicies := &Group{
|
groupForPolicies := &Group{
|
||||||
"grp-for-policies",
|
ID: "grp-for-policies",
|
||||||
"Group for policies",
|
AccountID: "account-id",
|
||||||
GroupIssuedAPI,
|
Name: "Group for policies",
|
||||||
make([]string, 0),
|
Issued: GroupIssuedAPI,
|
||||||
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForSetupKeys := &Group{
|
groupForSetupKeys := &Group{
|
||||||
"grp-for-keys",
|
ID: "grp-for-keys",
|
||||||
"Group for setup keys",
|
AccountID: "account-id",
|
||||||
GroupIssuedAPI,
|
Name: "Group for setup keys",
|
||||||
make([]string, 0),
|
Issued: GroupIssuedAPI,
|
||||||
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
groupForUsers := &Group{
|
groupForUsers := &Group{
|
||||||
"grp-for-users",
|
ID: "grp-for-users",
|
||||||
"Group for users",
|
AccountID: "account-id",
|
||||||
GroupIssuedAPI,
|
Name: "Group for users",
|
||||||
make([]string, 0),
|
Issued: GroupIssuedAPI,
|
||||||
|
Peers: make([]string, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
groupForIntegration := &Group{
|
||||||
|
ID: "grp-for-integration",
|
||||||
|
AccountID: "account-id",
|
||||||
|
Name: "Group for users",
|
||||||
|
Issued: GroupIssuedIntegration,
|
||||||
|
Peers: make([]string, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
routeResource := &route.Route{
|
routeResource := &route.Route{
|
||||||
@@ -159,6 +177,7 @@ func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
|
|||||||
_ = am.SaveGroup(accountID, groupAdminUserID, groupForPolicies)
|
_ = am.SaveGroup(accountID, groupAdminUserID, groupForPolicies)
|
||||||
_ = am.SaveGroup(accountID, groupAdminUserID, groupForSetupKeys)
|
_ = am.SaveGroup(accountID, groupAdminUserID, groupForSetupKeys)
|
||||||
_ = am.SaveGroup(accountID, groupAdminUserID, groupForUsers)
|
_ = am.SaveGroup(accountID, groupAdminUserID, groupForUsers)
|
||||||
|
_ = am.SaveGroup(accountID, groupAdminUserID, groupForIntegration)
|
||||||
|
|
||||||
return am.Store.GetAccount(account.Id)
|
return am.Store.GetAccount(account.Id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
|||||||
s.cancelPeerRoutines(peer)
|
s.cancelPeerRoutines(peer)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Debugf("recevied an update for peer %s", peerKey.String())
|
log.Debugf("received an update for peer %s", peerKey.String())
|
||||||
|
|
||||||
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, update.Update)
|
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, update.Update)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
|||||||
expectedID: accountID,
|
expectedID: accountID,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PutAccount OK wiht JWT",
|
name: "PutAccount OK with JWT",
|
||||||
expectedBody: true,
|
expectedBody: true,
|
||||||
requestType: http.MethodPut,
|
requestType: http.MethodPut,
|
||||||
requestPath: "/api/accounts/" + accountID,
|
requestPath: "/api/accounts/" + accountID,
|
||||||
@@ -134,7 +134,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
|
|||||||
expectedID: accountID,
|
expectedID: accountID,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PutAccount OK wiht JWT Propagation",
|
name: "PutAccount OK with JWT Propagation",
|
||||||
expectedBody: true,
|
expectedBody: true,
|
||||||
requestType: http.MethodPut,
|
requestType: http.MethodPut,
|
||||||
requestPath: "/api/accounts/" + accountID,
|
requestPath: "/api/accounts/" + accountID,
|
||||||
|
|||||||
@@ -125,6 +125,10 @@ components:
|
|||||||
description: Is true if this user is blocked. Blocked users can't use the system
|
description: Is true if this user is blocked. Blocked users can't use the system
|
||||||
type: boolean
|
type: boolean
|
||||||
example: false
|
example: false
|
||||||
|
issued:
|
||||||
|
description: How user was issued by API or Integration
|
||||||
|
type: string
|
||||||
|
example: api
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- email
|
- email
|
||||||
@@ -857,13 +861,17 @@ components:
|
|||||||
type: boolean
|
type: boolean
|
||||||
example: true
|
example: true
|
||||||
domains:
|
domains:
|
||||||
description: Nameserver group domain list
|
description: Nameserver group match domain list
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
maxLength: 255
|
maxLength: 255
|
||||||
example: "example.com"
|
example: "example.com"
|
||||||
|
search_domains_enabled:
|
||||||
|
description: Nameserver group search domain status for match domains. It should be true only if domains list is not empty.
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
- description
|
- description
|
||||||
@@ -872,6 +880,7 @@ components:
|
|||||||
- groups
|
- groups
|
||||||
- primary
|
- primary
|
||||||
- domains
|
- domains
|
||||||
|
- search_domains_enabled
|
||||||
NameserverGroup:
|
NameserverGroup:
|
||||||
allOf:
|
allOf:
|
||||||
- type: object
|
- type: object
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Package api provides primitives to interact with the openapi HTTP API.
|
// Package api provides primitives to interact with the openapi HTTP API.
|
||||||
//
|
//
|
||||||
// Code generated by github.com/deepmap/oapi-codegen version v1.15.0 DO NOT EDIT.
|
// Code generated by github.com/deepmap/oapi-codegen version v1.11.1-0.20220912230023-4a1477f6a8ba DO NOT EDIT.
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -248,7 +248,7 @@ type NameserverGroup struct {
|
|||||||
// Description Nameserver group description
|
// Description Nameserver group description
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
|
||||||
// Domains Nameserver group domain list
|
// Domains Nameserver group match domain list
|
||||||
Domains []string `json:"domains"`
|
Domains []string `json:"domains"`
|
||||||
|
|
||||||
// Enabled Nameserver group status
|
// Enabled Nameserver group status
|
||||||
@@ -268,6 +268,9 @@ type NameserverGroup struct {
|
|||||||
|
|
||||||
// Primary Nameserver group primary status
|
// Primary Nameserver group primary status
|
||||||
Primary bool `json:"primary"`
|
Primary bool `json:"primary"`
|
||||||
|
|
||||||
|
// SearchDomainsEnabled Nameserver group search domain status for match domains. It should be true only if domains list is not empty.
|
||||||
|
SearchDomainsEnabled bool `json:"search_domains_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NameserverGroupRequest defines model for NameserverGroupRequest.
|
// NameserverGroupRequest defines model for NameserverGroupRequest.
|
||||||
@@ -275,7 +278,7 @@ type NameserverGroupRequest struct {
|
|||||||
// Description Nameserver group description
|
// Description Nameserver group description
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
|
||||||
// Domains Nameserver group domain list
|
// Domains Nameserver group match domain list
|
||||||
Domains []string `json:"domains"`
|
Domains []string `json:"domains"`
|
||||||
|
|
||||||
// Enabled Nameserver group status
|
// Enabled Nameserver group status
|
||||||
@@ -292,6 +295,9 @@ type NameserverGroupRequest struct {
|
|||||||
|
|
||||||
// Primary Nameserver group primary status
|
// Primary Nameserver group primary status
|
||||||
Primary bool `json:"primary"`
|
Primary bool `json:"primary"`
|
||||||
|
|
||||||
|
// SearchDomainsEnabled Nameserver group search domain status for match domains. It should be true only if domains list is not empty.
|
||||||
|
SearchDomainsEnabled bool `json:"search_domains_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peer defines model for Peer.
|
// Peer defines model for Peer.
|
||||||
@@ -785,6 +791,9 @@ type User struct {
|
|||||||
// IsServiceUser Is true if this user is a service user
|
// IsServiceUser Is true if this user is a service user
|
||||||
IsServiceUser *bool `json:"is_service_user,omitempty"`
|
IsServiceUser *bool `json:"is_service_user,omitempty"`
|
||||||
|
|
||||||
|
// Issued How user was issued by API or Integration
|
||||||
|
Issued *string `json:"issued,omitempty"`
|
||||||
|
|
||||||
// LastLogin Last time this user performed a login to the dashboard
|
// LastLogin Last time this user performed a login to the dashboard
|
||||||
LastLogin *time.Time `json:"last_login,omitempty"`
|
LastLogin *time.Time `json:"last_login,omitempty"`
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const (
|
|||||||
testDNSSettingsUserID = "test_user"
|
testDNSSettingsUserID = "test_user"
|
||||||
)
|
)
|
||||||
|
|
||||||
var baseExistingDNSSettings = &server.DNSSettings{
|
var baseExistingDNSSettings = server.DNSSettings{
|
||||||
DisabledManagementGroups: []string{testDNSSettingsExistingGroup},
|
DisabledManagementGroups: []string{testDNSSettingsExistingGroup},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ func initDNSSettingsTestData() *DNSSettingsHandler {
|
|||||||
return &DNSSettingsHandler{
|
return &DNSSettingsHandler{
|
||||||
accountManager: &mock_server.MockAccountManager{
|
accountManager: &mock_server.MockAccountManager{
|
||||||
GetDNSSettingsFunc: func(accountID string, userID string) (*server.DNSSettings, error) {
|
GetDNSSettingsFunc: func(accountID string, userID string) (*server.DNSSettings, error) {
|
||||||
return testingDNSSettingsAccount.DNSSettings, nil
|
return &testingDNSSettingsAccount.DNSSettings, nil
|
||||||
},
|
},
|
||||||
SaveDNSSettingsFunc: func(accountID string, userID string, dnsSettingsToSave *server.DNSSettings) error {
|
SaveDNSSettingsFunc: func(accountID string, userID string, dnsSettingsToSave *server.DNSSettings) error {
|
||||||
if dnsSettingsToSave != nil {
|
if dnsSettingsToSave != nil {
|
||||||
|
|||||||
@@ -107,10 +107,11 @@ func (h *GroupsHandler) UpdateGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
peers = *req.Peers
|
peers = *req.Peers
|
||||||
}
|
}
|
||||||
group := server.Group{
|
group := server.Group{
|
||||||
ID: groupID,
|
ID: groupID,
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Peers: peers,
|
Peers: peers,
|
||||||
Issued: eg.Issued,
|
Issued: eg.Issued,
|
||||||
|
IntegrationReference: eg.IntegrationReference,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.accountManager.SaveGroup(account.Id, user.Id, &group); err != nil {
|
if err := h.accountManager.SaveGroup(account.Id, user.Id, &group); err != nil {
|
||||||
|
|||||||