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
|
||||||
|
|
||||||
|
|||||||
20
.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
|
||||||
|
|||||||
3
.gitignore
vendored
@@ -19,4 +19,5 @@ client/.distfiles/
|
|||||||
infrastructure_files/setup.env
|
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
|
||||||
@@ -91,4 +91,4 @@ uploads:
|
|||||||
mode: archive
|
mode: archive
|
||||||
target: https://pkgs.wiretrustee.com/yum/{{ .Arch }}{{ if .Arm }}{{ .Arm }}{{ end }}
|
target: https://pkgs.wiretrustee.com/yum/{{ .Arch }}{{ if .Arm }}{{ .Arm }}{{ end }}
|
||||||
username: dev@wiretrustee.com
|
username: dev@wiretrustee.com
|
||||||
method: PUT
|
method: PUT
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -215,4 +213,4 @@ NetBird project is composed of 3 main repositories:
|
|||||||
|
|
||||||
That we do not have any potential problems later it is sadly necessary to sign a [Contributor License Agreement](CONTRIBUTOR_LICENSE_AGREEMENT.md). That can be done literally with the push of a button.
|
That we do not have any potential problems later it is sadly necessary to sign a [Contributor License Agreement](CONTRIBUTOR_LICENSE_AGREEMENT.md). That can be done literally with the push of a button.
|
||||||
|
|
||||||
A bot will automatically comment on the pull request once it got opened asking for the agreement to be signed. Before it did not get signed it is sadly not possible to merge it in.
|
A bot will automatically comment on the pull request once it got opened asking for the agreement to be signed. Before it did not get signed it is sadly not possible to merge it in.
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal"
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
"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
|
||||||
@@ -97,4 +102,5 @@ export NETBIRD_AUTH_PKCE_AUTHORIZATION_ENDPOINT
|
|||||||
export NETBIRD_AUTH_PKCE_USE_ID_TOKEN
|
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
|
||||||
|
|||||||
@@ -22,4 +22,6 @@ NETBIRD_AUTH_DEVICE_AUTH_SCOPE="openid email"
|
|||||||
NETBIRD_MGMT_IDP=$CI_NETBIRD_MGMT_IDP
|
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 {
|
||||||
|
|||||||