mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-30 22:26:42 +00:00
Compare commits
3 Commits
feature/ke
...
separate_p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b43d7e8ef | ||
|
|
dcc83c8741 | ||
|
|
d56669ec2e |
6
.github/workflows/golang-test-linux.yml
vendored
6
.github/workflows/golang-test-linux.yml
vendored
@@ -78,9 +78,6 @@ jobs:
|
|||||||
- name: Generate RouteManager Test bin
|
- name: Generate RouteManager Test bin
|
||||||
run: CGO_ENABLED=0 go test -c -o routemanager-testing.bin ./client/internal/routemanager/...
|
run: CGO_ENABLED=0 go test -c -o routemanager-testing.bin ./client/internal/routemanager/...
|
||||||
|
|
||||||
- name: Generate nftables Manager Test bin
|
|
||||||
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=0 go test -c -o engine-testing.bin ./client/internal
|
||||||
|
|
||||||
@@ -99,9 +96,6 @@ jobs:
|
|||||||
- 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
|
|
||||||
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
|
||||||
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 --entrypoint /busybox/sh gcr.io/distroless/base:debug -c /ci/engine-testing.bin -test.timeout 5m -test.parallel 1
|
||||||
|
|
||||||
|
|||||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -9,7 +9,7 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
SIGN_PIPE_VER: "v0.0.8"
|
SIGN_PIPE_VER: "v0.0.6"
|
||||||
GORELEASER_VER: "v1.14.1"
|
GORELEASER_VER: "v1.14.1"
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
@@ -195,7 +195,7 @@ jobs:
|
|||||||
|
|
||||||
trigger_darwin_signer:
|
trigger_darwin_signer:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [release,release_ui_darwin]
|
needs: release_ui_darwin
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
steps:
|
steps:
|
||||||
- name: Trigger Darwin App binaries sign pipeline
|
- name: Trigger Darwin App binaries sign pipeline
|
||||||
@@ -205,4 +205,4 @@ jobs:
|
|||||||
repo: netbirdio/sign-pipelines
|
repo: netbirdio/sign-pipelines
|
||||||
ref: ${{ env.SIGN_PIPE_VER }}
|
ref: ${{ env.SIGN_PIPE_VER }}
|
||||||
token: ${{ secrets.SIGN_GITHUB_TOKEN }}
|
token: ${{ secrets.SIGN_GITHUB_TOKEN }}
|
||||||
inputs: '{ "tag": "${{ github.ref }}" }'
|
inputs: '{ "tag": "${{ github.ref }}" }'
|
||||||
18
.github/workflows/test-docker-compose-linux.yml
vendored
18
.github/workflows/test-docker-compose-linux.yml
vendored
@@ -6,7 +6,6 @@ on:
|
|||||||
- main
|
- main
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
@@ -46,20 +45,15 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
CI_NETBIRD_DOMAIN: localhost
|
CI_NETBIRD_DOMAIN: localhost
|
||||||
CI_NETBIRD_AUTH_CLIENT_ID: testing.client.id
|
CI_NETBIRD_AUTH_CLIENT_ID: testing.client.id
|
||||||
CI_NETBIRD_AUTH_CLIENT_SECRET: testing.client.secret
|
|
||||||
CI_NETBIRD_AUTH_AUDIENCE: testing.ci
|
CI_NETBIRD_AUTH_AUDIENCE: testing.ci
|
||||||
CI_NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT: https://example.eu.auth0.com/.well-known/openid-configuration
|
CI_NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT: https://example.eu.auth0.com/.well-known/openid-configuration
|
||||||
CI_NETBIRD_USE_AUTH0: true
|
CI_NETBIRD_USE_AUTH0: true
|
||||||
CI_NETBIRD_MGMT_IDP: "none"
|
|
||||||
CI_NETBIRD_IDP_MGMT_CLIENT_ID: testing.client.id
|
|
||||||
CI_NETBIRD_IDP_MGMT_CLIENT_SECRET: testing.client.secret
|
|
||||||
|
|
||||||
- name: check values
|
- name: check values
|
||||||
working-directory: infrastructure_files
|
working-directory: infrastructure_files
|
||||||
env:
|
env:
|
||||||
CI_NETBIRD_DOMAIN: localhost
|
CI_NETBIRD_DOMAIN: localhost
|
||||||
CI_NETBIRD_AUTH_CLIENT_ID: testing.client.id
|
CI_NETBIRD_AUTH_CLIENT_ID: testing.client.id
|
||||||
CI_NETBIRD_AUTH_CLIENT_SECRET: testing.client.secret
|
|
||||||
CI_NETBIRD_AUTH_AUDIENCE: testing.ci
|
CI_NETBIRD_AUTH_AUDIENCE: testing.ci
|
||||||
CI_NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT: https://example.eu.auth0.com/.well-known/openid-configuration
|
CI_NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT: https://example.eu.auth0.com/.well-known/openid-configuration
|
||||||
CI_NETBIRD_USE_AUTH0: true
|
CI_NETBIRD_USE_AUTH0: true
|
||||||
@@ -73,13 +67,9 @@ jobs:
|
|||||||
CI_NETBIRD_AUTH_USER_ID_CLAIM: "email"
|
CI_NETBIRD_AUTH_USER_ID_CLAIM: "email"
|
||||||
CI_NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE: "super"
|
CI_NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE: "super"
|
||||||
CI_NETBIRD_AUTH_DEVICE_AUTH_SCOPE: "openid email"
|
CI_NETBIRD_AUTH_DEVICE_AUTH_SCOPE: "openid email"
|
||||||
CI_NETBIRD_MGMT_IDP: "none"
|
|
||||||
CI_NETBIRD_IDP_MGMT_CLIENT_ID: testing.client.id
|
|
||||||
CI_NETBIRD_IDP_MGMT_CLIENT_SECRET: testing.client.secret
|
|
||||||
|
|
||||||
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
|
||||||
grep AUTH_CLIENT_SECRET docker-compose.yml | grep $CI_NETBIRD_AUTH_CLIENT_SECRET
|
|
||||||
grep AUTH_AUTHORITY docker-compose.yml | grep $CI_NETBIRD_AUTH_AUTHORITY
|
grep AUTH_AUTHORITY docker-compose.yml | grep $CI_NETBIRD_AUTH_AUTHORITY
|
||||||
grep AUTH_AUDIENCE docker-compose.yml | grep $CI_NETBIRD_AUTH_AUDIENCE
|
grep AUTH_AUDIENCE docker-compose.yml | grep $CI_NETBIRD_AUTH_AUDIENCE
|
||||||
grep AUTH_SUPPORTED_SCOPES docker-compose.yml | grep "$CI_NETBIRD_AUTH_SUPPORTED_SCOPES"
|
grep AUTH_SUPPORTED_SCOPES docker-compose.yml | grep "$CI_NETBIRD_AUTH_SUPPORTED_SCOPES"
|
||||||
@@ -93,16 +83,10 @@ jobs:
|
|||||||
grep -A 1 ProviderConfig management.json | grep Audience | grep $CI_NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE
|
grep -A 1 ProviderConfig management.json | grep Audience | grep $CI_NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE
|
||||||
grep Scope management.json | grep "$CI_NETBIRD_AUTH_DEVICE_AUTH_SCOPE"
|
grep Scope management.json | grep "$CI_NETBIRD_AUTH_DEVICE_AUTH_SCOPE"
|
||||||
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 3 IdpManagerConfig management.json | grep -A 1 ClientConfig | grep Issuer | grep $CI_NETBIRD_AUTH_AUTHORITY
|
|
||||||
grep -A 4 IdpManagerConfig management.json | grep -A 2 ClientConfig | grep TokenEndpoint | grep $CI_NETBIRD_AUTH_TOKEN_ENDPOINT
|
|
||||||
grep -A 5 IdpManagerConfig management.json | grep -A 3 ClientConfig | grep ClientID | grep $CI_NETBIRD_IDP_MGMT_CLIENT_ID
|
|
||||||
grep -A 6 IdpManagerConfig management.json | grep -A 4 ClientConfig | grep ClientSecret | grep $CI_NETBIRD_IDP_MGMT_CLIENT_SECRET
|
|
||||||
grep -A 7 IdpManagerConfig management.json | grep -A 5 ClientConfig | grep GrantType | grep client_credentials
|
|
||||||
|
|
||||||
- name: run docker compose up
|
- name: run docker compose up
|
||||||
working-directory: infrastructure_files
|
working-directory: infrastructure_files
|
||||||
run: |
|
run: |
|
||||||
docker-compose up -d
|
docker-compose up -d
|
||||||
sleep 5
|
sleep 5
|
||||||
docker-compose ps
|
docker-compose ps
|
||||||
|
|||||||
22
.github/workflows/update-docs.yml
vendored
22
.github/workflows/update-docs.yml
vendored
@@ -1,22 +0,0 @@
|
|||||||
name: update docs
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
paths:
|
|
||||||
- 'management/server/http/api/openapi.yml'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
trigger_docs_api_update:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
steps:
|
|
||||||
- name: Trigger API pages generation
|
|
||||||
uses: benc-uk/workflow-dispatch@v1
|
|
||||||
with:
|
|
||||||
workflow: generate api pages
|
|
||||||
repo: netbirdio/docs
|
|
||||||
ref: "refs/heads/main"
|
|
||||||
token: ${{ secrets.SIGN_GITHUB_TOKEN }}
|
|
||||||
inputs: '{ "tag": "${{ github.ref }}" }'
|
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -7,15 +7,8 @@ bin/
|
|||||||
conf.json
|
conf.json
|
||||||
http-cmds.sh
|
http-cmds.sh
|
||||||
infrastructure_files/management.json
|
infrastructure_files/management.json
|
||||||
infrastructure_files/management-*.json
|
|
||||||
infrastructure_files/docker-compose.yml
|
infrastructure_files/docker-compose.yml
|
||||||
infrastructure_files/openid-configuration.json
|
|
||||||
infrastructure_files/turnserver.conf
|
|
||||||
management/management
|
|
||||||
client/client
|
|
||||||
client/client.exe
|
|
||||||
*.syso
|
*.syso
|
||||||
client/.distfiles/
|
client/.distfiles/
|
||||||
infrastructure_files/setup.env
|
infrastructure_files/setup.env
|
||||||
infrastructure_files/setup-*.env
|
|
||||||
.vscode
|
.vscode
|
||||||
|
|||||||
@@ -12,7 +12,11 @@ builds:
|
|||||||
- arm
|
- arm
|
||||||
- amd64
|
- amd64
|
||||||
- arm64
|
- arm64
|
||||||
|
- mips
|
||||||
- 386
|
- 386
|
||||||
|
gomips:
|
||||||
|
- hardfloat
|
||||||
|
- softfloat
|
||||||
ignore:
|
ignore:
|
||||||
- goos: windows
|
- goos: windows
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
@@ -26,26 +30,6 @@ builds:
|
|||||||
tags:
|
tags:
|
||||||
- load_wgnt_from_rsrc
|
- load_wgnt_from_rsrc
|
||||||
|
|
||||||
- id: netbird-static
|
|
||||||
dir: client
|
|
||||||
binary: netbird
|
|
||||||
env: [CGO_ENABLED=0]
|
|
||||||
goos:
|
|
||||||
- linux
|
|
||||||
goarch:
|
|
||||||
- mips
|
|
||||||
- mipsle
|
|
||||||
- mips64
|
|
||||||
- mips64le
|
|
||||||
gomips:
|
|
||||||
- hardfloat
|
|
||||||
- softfloat
|
|
||||||
ldflags:
|
|
||||||
- -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
|
||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
|
||||||
tags:
|
|
||||||
- load_wgnt_from_rsrc
|
|
||||||
|
|
||||||
- id: netbird-mgmt
|
- id: netbird-mgmt
|
||||||
dir: management
|
dir: management
|
||||||
env:
|
env:
|
||||||
@@ -83,7 +67,6 @@ builds:
|
|||||||
archives:
|
archives:
|
||||||
- builds:
|
- builds:
|
||||||
- netbird
|
- netbird
|
||||||
- netbird-static
|
|
||||||
|
|
||||||
nfpms:
|
nfpms:
|
||||||
|
|
||||||
@@ -376,4 +359,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
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package base62
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
||||||
base = uint32(len(alphabet))
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encode encodes a uint32 value to a base62 string.
|
|
||||||
func Encode(num uint32) string {
|
|
||||||
if num == 0 {
|
|
||||||
return string(alphabet[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
var encoded strings.Builder
|
|
||||||
remainder := uint32(0)
|
|
||||||
|
|
||||||
for num > 0 {
|
|
||||||
remainder = num % base
|
|
||||||
encoded.WriteByte(alphabet[remainder])
|
|
||||||
num /= base
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse the encoded string
|
|
||||||
encodedString := encoded.String()
|
|
||||||
reversed := reverse(encodedString)
|
|
||||||
return reversed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode decodes a base62 string to a uint32 value.
|
|
||||||
func Decode(encoded string) (uint32, error) {
|
|
||||||
var decoded uint32
|
|
||||||
strLen := len(encoded)
|
|
||||||
|
|
||||||
for i, char := range encoded {
|
|
||||||
index := strings.IndexRune(alphabet, char)
|
|
||||||
if index < 0 {
|
|
||||||
return 0, fmt.Errorf("invalid character: %c", char)
|
|
||||||
}
|
|
||||||
|
|
||||||
decoded += uint32(index) * uint32(math.Pow(float64(base), float64(strLen-i-1)))
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoded, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse a string.
|
|
||||||
func reverse(s string) string {
|
|
||||||
runes := []rune(s)
|
|
||||||
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
runes[i], runes[j] = runes[j], runes[i]
|
|
||||||
}
|
|
||||||
return string(runes)
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package base62
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestEncodeDecode(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
num uint32
|
|
||||||
}{
|
|
||||||
{0},
|
|
||||||
{1},
|
|
||||||
{42},
|
|
||||||
{12345},
|
|
||||||
{99999},
|
|
||||||
{123456789},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
encoded := Encode(tt.num)
|
|
||||||
decoded, err := Decode(encoded)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Decode error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if decoded != tt.num {
|
|
||||||
t.Errorf("Decode(%v) = %v, want %v", encoded, decoded, tt.num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal"
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
"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"
|
||||||
@@ -30,11 +29,6 @@ type IFaceDiscover interface {
|
|||||||
stdnet.ExternalIFaceDiscover
|
stdnet.ExternalIFaceDiscover
|
||||||
}
|
}
|
||||||
|
|
||||||
// RouteListener export internal RouteListener for mobile
|
|
||||||
type RouteListener interface {
|
|
||||||
routemanager.RouteListener
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
formatter.SetLogcatFormatter(log.StandardLogger())
|
formatter.SetLogcatFormatter(log.StandardLogger())
|
||||||
}
|
}
|
||||||
@@ -48,11 +42,13 @@ type Client struct {
|
|||||||
ctxCancel context.CancelFunc
|
ctxCancel context.CancelFunc
|
||||||
ctxCancelLock *sync.Mutex
|
ctxCancelLock *sync.Mutex
|
||||||
deviceName string
|
deviceName string
|
||||||
routeListener routemanager.RouteListener
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) *Client {
|
||||||
|
lvl, _ := log.ParseLevel("trace")
|
||||||
|
log.SetLevel(lvl)
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
cfgFile: cfgFile,
|
cfgFile: cfgFile,
|
||||||
deviceName: deviceName,
|
deviceName: deviceName,
|
||||||
@@ -60,7 +56,6 @@ func NewClient(cfgFile, deviceName string, tunAdapter TunAdapter, iFaceDiscover
|
|||||||
iFaceDiscover: iFaceDiscover,
|
iFaceDiscover: iFaceDiscover,
|
||||||
recorder: peer.NewRecorder(""),
|
recorder: peer.NewRecorder(""),
|
||||||
ctxCancelLock: &sync.Mutex{},
|
ctxCancelLock: &sync.Mutex{},
|
||||||
routeListener: routeListener,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +85,7 @@ func (c *Client) Run(urlOpener URLOpener) error {
|
|||||||
|
|
||||||
// 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.RunClient(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover, c.routeListener)
|
return internal.RunClient(ctx, cfg, c.recorder, c.tunAdapter, c.iFaceDiscover)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the internal client and free the resources
|
// Stop the internal client and free the resources
|
||||||
@@ -104,11 +99,6 @@ func (c *Client) Stop() {
|
|||||||
c.ctxCancel()
|
c.ctxCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTraceLogLevel configure the logger to trace level
|
|
||||||
func (c *Client) SetTraceLogLevel() {
|
|
||||||
log.SetLevel(log.TraceLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PeersList return with the list of the PeerInfos
|
// PeersList return with the list of the PeerInfos
|
||||||
func (c *Client) PeersList() *PeerInfoArray {
|
func (c *Client) PeersList() *PeerInfoArray {
|
||||||
|
|
||||||
@@ -120,9 +110,11 @@ func (c *Client) PeersList() *PeerInfoArray {
|
|||||||
p.IP,
|
p.IP,
|
||||||
p.FQDN,
|
p.FQDN,
|
||||||
p.ConnStatus.String(),
|
p.ConnStatus.String(),
|
||||||
|
p.Direct,
|
||||||
}
|
}
|
||||||
peerInfos[n] = pi
|
peerInfos[n] = pi
|
||||||
}
|
}
|
||||||
|
|
||||||
return &PeerInfoArray{items: peerInfos}
|
return &PeerInfoArray{items: peerInfos}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package android
|
|
||||||
|
|
||||||
import _ "golang.org/x/mobile/bind"
|
|
||||||
|
|
||||||
// to keep our CI/CD that checks go.mod and go.sum files happy, we need to import the package above
|
|
||||||
@@ -5,6 +5,7 @@ type PeerInfo struct {
|
|||||||
IP string
|
IP string
|
||||||
FQDN string
|
FQDN string
|
||||||
ConnStatus string // Todo replace to enum
|
ConnStatus string // Todo replace to enum
|
||||||
|
Direct bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeerInfoCollection made for Java layer to get non default types as collection
|
// PeerInfoCollection made for Java layer to get non default types as collection
|
||||||
|
|||||||
@@ -2,23 +2,21 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
|
|
||||||
clientProto "github.com/netbirdio/netbird/client/proto"
|
clientProto "github.com/netbirdio/netbird/client/proto"
|
||||||
client "github.com/netbirdio/netbird/client/server"
|
client "github.com/netbirdio/netbird/client/server"
|
||||||
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"
|
||||||
sigProto "github.com/netbirdio/netbird/signal/proto"
|
sigProto "github.com/netbirdio/netbird/signal/proto"
|
||||||
sig "github.com/netbirdio/netbird/signal/server"
|
sig "github.com/netbirdio/netbird/signal/server"
|
||||||
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func startTestingServices(t *testing.T) string {
|
func startTestingServices(t *testing.T) string {
|
||||||
@@ -65,7 +63,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.NewFileStore(config.Datadir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
ctx, cancel = context.WithCancel(ctx)
|
ctx, cancel = context.WithCancel(ctx)
|
||||||
SetupCloseHandler(ctx, cancel)
|
SetupCloseHandler(ctx, cancel)
|
||||||
return internal.RunClient(ctx, config, peer.NewRecorder(config.ManagementURL.String()), nil, nil, nil)
|
return internal.RunClient(ctx, config, peer.NewRecorder(config.ManagementURL.String()), nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
||||||
|
|||||||
@@ -13,24 +13,22 @@ type Rule interface {
|
|||||||
GetRuleID() string
|
GetRuleID() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// RuleDirection is the traffic direction which a rule is applied
|
// Direction is the direction of the traffic
|
||||||
type RuleDirection int
|
type Direction int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// RuleDirectionIN applies to filters that handlers incoming traffic
|
// DirectionSrc is the direction of the traffic from the source
|
||||||
RuleDirectionIN RuleDirection = iota
|
DirectionSrc Direction = iota
|
||||||
// RuleDirectionOUT applies to filters that handlers outgoing traffic
|
// DirectionDst is the direction of the traffic from the destination
|
||||||
RuleDirectionOUT
|
DirectionDst
|
||||||
)
|
)
|
||||||
|
|
||||||
// Action is the action to be taken on a rule
|
// Action is the action to be taken on a rule
|
||||||
type Action int
|
type Action int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ActionUnknown is a unknown action
|
|
||||||
ActionUnknown Action = iota
|
|
||||||
// ActionAccept is the action to accept a packet
|
// ActionAccept is the action to accept a packet
|
||||||
ActionAccept
|
ActionAccept Action = iota
|
||||||
// ActionDrop is the action to drop a packet
|
// ActionDrop is the action to drop a packet
|
||||||
ActionDrop
|
ActionDrop
|
||||||
)
|
)
|
||||||
@@ -41,15 +39,10 @@ const (
|
|||||||
// Netbird client for ACL and routing functionality
|
// Netbird client for ACL and routing functionality
|
||||||
type Manager interface {
|
type Manager interface {
|
||||||
// AddFiltering rule to the firewall
|
// AddFiltering rule to the firewall
|
||||||
//
|
|
||||||
// If comment argument is empty firewall manager should set
|
|
||||||
// rule ID as comment for the rule
|
|
||||||
AddFiltering(
|
AddFiltering(
|
||||||
ip net.IP,
|
ip net.IP,
|
||||||
proto Protocol,
|
port *Port,
|
||||||
sPort *Port,
|
direction Direction,
|
||||||
dPort *Port,
|
|
||||||
direction RuleDirection,
|
|
||||||
action Action,
|
action Action,
|
||||||
comment string,
|
comment string,
|
||||||
) (Rule, error)
|
) (Rule, error)
|
||||||
|
|||||||
@@ -8,50 +8,26 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
fw "github.com/netbirdio/netbird/client/firewall"
|
fw "github.com/netbirdio/netbird/client/firewall"
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ChainInputFilterName is the name of the chain that is used for filtering incoming packets
|
// ChainFilterName is the name of the chain that is used for filtering by the Netbird client
|
||||||
ChainInputFilterName = "NETBIRD-ACL-INPUT"
|
ChainFilterName = "NETBIRD-ACL"
|
||||||
|
|
||||||
// ChainOutputFilterName is the name of the chain that is used for filtering outgoing packets
|
|
||||||
ChainOutputFilterName = "NETBIRD-ACL-OUTPUT"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// dropAllDefaultRule in the Netbird chain
|
|
||||||
var dropAllDefaultRule = []string{"-j", "DROP"}
|
|
||||||
|
|
||||||
// Manager of iptables firewall
|
// Manager of iptables firewall
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
|
|
||||||
ipv4Client *iptables.IPTables
|
ipv4Client *iptables.IPTables
|
||||||
ipv6Client *iptables.IPTables
|
ipv6Client *iptables.IPTables
|
||||||
|
|
||||||
inputDefaultRuleSpecs []string
|
|
||||||
outputDefaultRuleSpecs []string
|
|
||||||
wgIface iFaceMapper
|
|
||||||
}
|
|
||||||
|
|
||||||
// iFaceMapper defines subset methods of interface required for manager
|
|
||||||
type iFaceMapper interface {
|
|
||||||
Name() string
|
|
||||||
Address() iface.WGAddress
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create iptables firewall manager
|
// Create iptables firewall manager
|
||||||
func Create(wgIface iFaceMapper) (*Manager, error) {
|
func Create() (*Manager, error) {
|
||||||
m := &Manager{
|
m := &Manager{}
|
||||||
wgIface: wgIface,
|
|
||||||
inputDefaultRuleSpecs: []string{
|
|
||||||
"-i", wgIface.Name(), "-j", ChainInputFilterName, "-s", wgIface.Address().String()},
|
|
||||||
outputDefaultRuleSpecs: []string{
|
|
||||||
"-o", wgIface.Name(), "-j", ChainOutputFilterName, "-d", wgIface.Address().String()},
|
|
||||||
}
|
|
||||||
|
|
||||||
// init clients for booth ipv4 and ipv6
|
// init clients for booth ipv4 and ipv6
|
||||||
ipv4Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
ipv4Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||||
@@ -62,271 +38,118 @@ func Create(wgIface iFaceMapper) (*Manager, error) {
|
|||||||
|
|
||||||
ipv6Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
ipv6Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("ip6tables is not installed in the system or not supported: %v", err)
|
return nil, fmt.Errorf("ip6tables is not installed in the system or not supported")
|
||||||
} else {
|
|
||||||
m.ipv6Client = ipv6Client
|
|
||||||
}
|
}
|
||||||
|
m.ipv6Client = ipv6Client
|
||||||
|
|
||||||
if err := m.Reset(); err != nil {
|
if err := m.Reset(); err != nil {
|
||||||
return nil, fmt.Errorf("failed to reset firewall: %v", err)
|
return nil, fmt.Errorf("failed to reset firewall: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFiltering rule to the firewall
|
// AddFiltering rule to the firewall
|
||||||
//
|
|
||||||
// If comment is empty rule ID is used as comment
|
|
||||||
func (m *Manager) AddFiltering(
|
func (m *Manager) AddFiltering(
|
||||||
ip net.IP,
|
ip net.IP,
|
||||||
protocol fw.Protocol,
|
port *fw.Port,
|
||||||
sPort *fw.Port,
|
direction fw.Direction,
|
||||||
dPort *fw.Port,
|
|
||||||
direction fw.RuleDirection,
|
|
||||||
action fw.Action,
|
action fw.Action,
|
||||||
comment string,
|
comment string,
|
||||||
) (fw.Rule, error) {
|
) (fw.Rule, error) {
|
||||||
m.mutex.Lock()
|
m.mutex.Lock()
|
||||||
defer m.mutex.Unlock()
|
defer m.mutex.Unlock()
|
||||||
|
client := m.client(ip)
|
||||||
client, err := m.client(ip)
|
ok, err := client.ChainExists("filter", ChainFilterName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to check if chain exists: %s", err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
if err := client.NewChain("filter", ChainFilterName); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create chain: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if port == nil || port.Values == nil || (port.IsRange && len(port.Values) != 2) {
|
||||||
|
return nil, fmt.Errorf("invalid port definition")
|
||||||
|
}
|
||||||
|
pv := strconv.Itoa(port.Values[0])
|
||||||
|
if port.IsRange {
|
||||||
|
pv += ":" + strconv.Itoa(port.Values[1])
|
||||||
|
}
|
||||||
|
specs := m.filterRuleSpecs("filter", ChainFilterName, ip, pv, direction, action, comment)
|
||||||
|
if err := client.AppendUnique("filter", ChainFilterName, specs...); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
rule := &Rule{
|
||||||
var dPortVal, sPortVal string
|
id: uuid.New().String(),
|
||||||
if dPort != nil && dPort.Values != nil {
|
|
||||||
// TODO: we support only one port per rule in current implementation of ACLs
|
|
||||||
dPortVal = strconv.Itoa(dPort.Values[0])
|
|
||||||
}
|
|
||||||
if sPort != nil && sPort.Values != nil {
|
|
||||||
sPortVal = strconv.Itoa(sPort.Values[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleID := uuid.New().String()
|
|
||||||
if comment == "" {
|
|
||||||
comment = ruleID
|
|
||||||
}
|
|
||||||
|
|
||||||
specs := m.filterRuleSpecs(
|
|
||||||
"filter",
|
|
||||||
ip,
|
|
||||||
string(protocol),
|
|
||||||
sPortVal,
|
|
||||||
dPortVal,
|
|
||||||
direction,
|
|
||||||
action,
|
|
||||||
comment,
|
|
||||||
)
|
|
||||||
|
|
||||||
if direction == fw.RuleDirectionOUT {
|
|
||||||
ok, err := client.Exists("filter", ChainOutputFilterName, specs...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("check is output rule already exists: %w", err)
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
return nil, fmt.Errorf("input rule already exists")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := client.Insert("filter", ChainOutputFilterName, 1, specs...); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ok, err := client.Exists("filter", ChainInputFilterName, specs...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("check is input rule already exists: %w", err)
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
return nil, fmt.Errorf("input rule already exists")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := client.Insert("filter", ChainInputFilterName, 1, specs...); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Rule{
|
|
||||||
id: ruleID,
|
|
||||||
specs: specs,
|
specs: specs,
|
||||||
dst: direction == fw.RuleDirectionOUT,
|
|
||||||
v6: ip.To4() == nil,
|
v6: ip.To4() == nil,
|
||||||
}, nil
|
}
|
||||||
|
return rule, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRule from the firewall by rule definition
|
// DeleteRule from the firewall by rule definition
|
||||||
func (m *Manager) DeleteRule(rule fw.Rule) error {
|
func (m *Manager) DeleteRule(rule fw.Rule) error {
|
||||||
m.mutex.Lock()
|
m.mutex.Lock()
|
||||||
defer m.mutex.Unlock()
|
defer m.mutex.Unlock()
|
||||||
|
|
||||||
r, ok := rule.(*Rule)
|
r, ok := rule.(*Rule)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("invalid rule type")
|
return fmt.Errorf("invalid rule type")
|
||||||
}
|
}
|
||||||
|
|
||||||
client := m.ipv4Client
|
client := m.ipv4Client
|
||||||
if r.v6 {
|
if r.v6 {
|
||||||
if m.ipv6Client == nil {
|
|
||||||
return fmt.Errorf("ipv6 is not supported")
|
|
||||||
}
|
|
||||||
client = m.ipv6Client
|
client = m.ipv6Client
|
||||||
}
|
}
|
||||||
|
return client.Delete("filter", ChainFilterName, r.specs...)
|
||||||
if r.dst {
|
|
||||||
return client.Delete("filter", ChainOutputFilterName, r.specs...)
|
|
||||||
}
|
|
||||||
return client.Delete("filter", ChainInputFilterName, r.specs...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset firewall to the default state
|
// Reset firewall to the default state
|
||||||
func (m *Manager) Reset() error {
|
func (m *Manager) Reset() error {
|
||||||
m.mutex.Lock()
|
m.mutex.Lock()
|
||||||
defer m.mutex.Unlock()
|
defer m.mutex.Unlock()
|
||||||
|
if err := m.reset(m.ipv4Client, "filter", ChainFilterName); err != nil {
|
||||||
if err := m.reset(m.ipv4Client, "filter"); err != nil {
|
return fmt.Errorf("clean ipv4 firewall ACL chain: %w", err)
|
||||||
return fmt.Errorf("clean ipv4 firewall ACL input chain: %w", err)
|
|
||||||
}
|
}
|
||||||
if m.ipv6Client != nil {
|
if err := m.reset(m.ipv6Client, "filter", ChainFilterName); err != nil {
|
||||||
if err := m.reset(m.ipv6Client, "filter"); err != nil {
|
return fmt.Errorf("clean ipv6 firewall ACL chain: %w", err)
|
||||||
return fmt.Errorf("clean ipv6 firewall ACL input chain: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset firewall chain, clear it and drop it
|
// reset firewall chain, clear it and drop it
|
||||||
func (m *Manager) reset(client *iptables.IPTables, table string) error {
|
func (m *Manager) reset(client *iptables.IPTables, table, chain string) error {
|
||||||
ok, err := client.ChainExists(table, ChainInputFilterName)
|
ok, err := client.ChainExists(table, chain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to check if input chain exists: %w", err)
|
return fmt.Errorf("failed to check if chain exists: %w", err)
|
||||||
}
|
}
|
||||||
if ok {
|
if !ok {
|
||||||
if ok, err := client.Exists("filter", "INPUT", m.inputDefaultRuleSpecs...); err != nil {
|
|
||||||
return err
|
|
||||||
} else if ok {
|
|
||||||
if err := client.Delete("filter", "INPUT", m.inputDefaultRuleSpecs...); err != nil {
|
|
||||||
log.WithError(err).Errorf("failed to delete default input rule: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, err = client.ChainExists(table, ChainOutputFilterName)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to check if output chain exists: %w", err)
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
if ok, err := client.Exists("filter", "OUTPUT", m.outputDefaultRuleSpecs...); err != nil {
|
|
||||||
return err
|
|
||||||
} else if ok {
|
|
||||||
if err := client.Delete("filter", "OUTPUT", m.outputDefaultRuleSpecs...); err != nil {
|
|
||||||
log.WithError(err).Errorf("failed to delete default output rule: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := client.ClearAndDeleteChain(table, ChainInputFilterName); err != nil {
|
|
||||||
log.Errorf("failed to clear and delete input chain: %v", err)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if err := client.ClearChain(table, ChainFilterName); err != nil {
|
||||||
if err := client.ClearAndDeleteChain(table, ChainOutputFilterName); err != nil {
|
return fmt.Errorf("failed to clear chain: %w", err)
|
||||||
log.Errorf("failed to clear and delete input chain: %v", err)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
return client.DeleteChain(table, ChainFilterName)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// filterRuleSpecs returns the specs of a filtering rule
|
// filterRuleSpecs returns the specs of a filtering rule
|
||||||
func (m *Manager) filterRuleSpecs(
|
func (m *Manager) filterRuleSpecs(
|
||||||
table string, ip net.IP, protocol string, sPort, dPort string,
|
table string, chain string, ip net.IP, port string,
|
||||||
direction fw.RuleDirection, action fw.Action, comment string,
|
direction fw.Direction, action fw.Action, comment string,
|
||||||
) (specs []string) {
|
) (specs []string) {
|
||||||
matchByIP := true
|
if direction == fw.DirectionSrc {
|
||||||
// don't use IP matching if IP is ip 0.0.0.0
|
specs = append(specs, "-s", ip.String())
|
||||||
if s := ip.String(); s == "0.0.0.0" || s == "::" {
|
|
||||||
matchByIP = false
|
|
||||||
}
|
|
||||||
switch direction {
|
|
||||||
case fw.RuleDirectionIN:
|
|
||||||
if matchByIP {
|
|
||||||
specs = append(specs, "-s", ip.String())
|
|
||||||
}
|
|
||||||
case fw.RuleDirectionOUT:
|
|
||||||
if matchByIP {
|
|
||||||
specs = append(specs, "-d", ip.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if protocol != "all" {
|
|
||||||
specs = append(specs, "-p", protocol)
|
|
||||||
}
|
|
||||||
if sPort != "" {
|
|
||||||
specs = append(specs, "--sport", sPort)
|
|
||||||
}
|
|
||||||
if dPort != "" {
|
|
||||||
specs = append(specs, "--dport", dPort)
|
|
||||||
}
|
}
|
||||||
|
specs = append(specs, "-p", "tcp", "--dport", port)
|
||||||
specs = append(specs, "-j", m.actionToStr(action))
|
specs = append(specs, "-j", m.actionToStr(action))
|
||||||
return append(specs, "-m", "comment", "--comment", comment)
|
return append(specs, "-m", "comment", "--comment", comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rawClient returns corresponding iptables client for the given ip
|
// client returns corresponding iptables client for the given ip
|
||||||
func (m *Manager) rawClient(ip net.IP) (*iptables.IPTables, error) {
|
func (m *Manager) client(ip net.IP) *iptables.IPTables {
|
||||||
if ip.To4() != nil {
|
if ip.To4() != nil {
|
||||||
return m.ipv4Client, nil
|
return m.ipv4Client
|
||||||
}
|
}
|
||||||
if m.ipv6Client == nil {
|
return m.ipv6Client
|
||||||
return nil, fmt.Errorf("ipv6 is not supported")
|
|
||||||
}
|
|
||||||
return m.ipv6Client, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// client returns client with initialized chain and default rules
|
|
||||||
func (m *Manager) client(ip net.IP) (*iptables.IPTables, error) {
|
|
||||||
client, err := m.rawClient(ip)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, err := client.ChainExists("filter", ChainInputFilterName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to check if chain exists: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
if err := client.NewChain("filter", ChainInputFilterName); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create input chain: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := client.AppendUnique("filter", ChainInputFilterName, dropAllDefaultRule...); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create default drop all in netbird input chain: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := client.AppendUnique("filter", "INPUT", m.inputDefaultRuleSpecs...); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create input chain jump rule: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ok, err = client.ChainExists("filter", ChainOutputFilterName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to check if chain exists: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
if err := client.NewChain("filter", ChainOutputFilterName); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create output chain: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := client.AppendUnique("filter", ChainOutputFilterName, dropAllDefaultRule...); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create default drop all in netbird output chain: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := client.AppendUnique("filter", "OUTPUT", m.outputDefaultRuleSpecs...); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create output chain jump rule: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return client, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) actionToStr(action fw.Action) string {
|
func (m *Manager) actionToStr(action fw.Action) string {
|
||||||
|
|||||||
@@ -1,180 +1,105 @@
|
|||||||
package iptables
|
package iptables
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
fw "github.com/netbirdio/netbird/client/firewall"
|
fw "github.com/netbirdio/netbird/client/firewall"
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// iFaceMapper defines subset methods of interface required for manager
|
func TestNewManager(t *testing.T) {
|
||||||
type iFaceMock struct {
|
|
||||||
NameFunc func() string
|
|
||||||
AddressFunc func() iface.WGAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *iFaceMock) Name() string {
|
|
||||||
if i.NameFunc != nil {
|
|
||||||
return i.NameFunc()
|
|
||||||
}
|
|
||||||
panic("NameFunc is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *iFaceMock) Address() iface.WGAddress {
|
|
||||||
if i.AddressFunc != nil {
|
|
||||||
return i.AddressFunc()
|
|
||||||
}
|
|
||||||
panic("AddressFunc is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIptablesManager(t *testing.T) {
|
|
||||||
ipv4Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
ipv4Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||||
require.NoError(t, err)
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
mock := &iFaceMock{
|
|
||||||
NameFunc: func() string {
|
|
||||||
return "lo"
|
|
||||||
},
|
|
||||||
AddressFunc: func() iface.WGAddress {
|
|
||||||
return iface.WGAddress{
|
|
||||||
IP: net.ParseIP("10.20.0.1"),
|
|
||||||
Network: &net.IPNet{
|
|
||||||
IP: net.ParseIP("10.20.0.0"),
|
|
||||||
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// just check on the local interface
|
manager, err := Create()
|
||||||
manager, err := Create(mock)
|
if err != nil {
|
||||||
require.NoError(t, err)
|
t.Fatal(err)
|
||||||
time.Sleep(time.Second)
|
}
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := manager.Reset(); err != nil {
|
|
||||||
t.Errorf("clear the manager state: %v", err)
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}()
|
|
||||||
|
|
||||||
var rule1 fw.Rule
|
var rule1 fw.Rule
|
||||||
t.Run("add first rule", func(t *testing.T) {
|
t.Run("add first rule", func(t *testing.T) {
|
||||||
ip := net.ParseIP("10.20.0.2")
|
ip := net.ParseIP("10.20.0.2")
|
||||||
port := &fw.Port{Values: []int{8080}}
|
port := &fw.Port{Proto: fw.PortProtocolTCP, Values: []int{8080}}
|
||||||
rule1, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionOUT, fw.ActionAccept, "accept HTTP traffic")
|
rule1, err = manager.AddFiltering(ip, port, fw.DirectionDst, fw.ActionAccept, "accept HTTP traffic")
|
||||||
require.NoError(t, err, "failed to add rule")
|
if err != nil {
|
||||||
|
t.Errorf("failed to add rule: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
checkRuleSpecs(t, ipv4Client, ChainOutputFilterName, true, rule1.(*Rule).specs...)
|
checkRuleSpecs(t, ipv4Client, true, rule1.(*Rule).specs...)
|
||||||
})
|
})
|
||||||
|
|
||||||
var rule2 fw.Rule
|
var rule2 fw.Rule
|
||||||
t.Run("add second rule", func(t *testing.T) {
|
t.Run("add second rule", func(t *testing.T) {
|
||||||
ip := net.ParseIP("10.20.0.3")
|
ip := net.ParseIP("10.20.0.3")
|
||||||
port := &fw.Port{
|
port := &fw.Port{
|
||||||
|
Proto: fw.PortProtocolTCP,
|
||||||
Values: []int{8043: 8046},
|
Values: []int{8043: 8046},
|
||||||
}
|
}
|
||||||
rule2, err = manager.AddFiltering(
|
rule2, err = manager.AddFiltering(
|
||||||
ip, "tcp", port, nil, fw.RuleDirectionIN, fw.ActionAccept, "accept HTTPS traffic from ports range")
|
ip, port, fw.DirectionDst, fw.ActionAccept, "accept HTTPS traffic from ports range")
|
||||||
require.NoError(t, err, "failed to add rule")
|
if err != nil {
|
||||||
|
t.Errorf("failed to add rule: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
checkRuleSpecs(t, ipv4Client, ChainInputFilterName, true, rule2.(*Rule).specs...)
|
checkRuleSpecs(t, ipv4Client, true, rule2.(*Rule).specs...)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("delete first rule", func(t *testing.T) {
|
t.Run("delete first rule", func(t *testing.T) {
|
||||||
if err := manager.DeleteRule(rule1); err != nil {
|
if err := manager.DeleteRule(rule1); err != nil {
|
||||||
require.NoError(t, err, "failed to delete rule")
|
t.Errorf("failed to delete rule: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkRuleSpecs(t, ipv4Client, ChainOutputFilterName, false, rule1.(*Rule).specs...)
|
checkRuleSpecs(t, ipv4Client, false, rule1.(*Rule).specs...)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("delete second rule", func(t *testing.T) {
|
t.Run("delete second rule", func(t *testing.T) {
|
||||||
if err := manager.DeleteRule(rule2); err != nil {
|
if err := manager.DeleteRule(rule2); err != nil {
|
||||||
require.NoError(t, err, "failed to delete rule")
|
t.Errorf("failed to delete rule: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkRuleSpecs(t, ipv4Client, ChainInputFilterName, false, rule2.(*Rule).specs...)
|
checkRuleSpecs(t, ipv4Client, false, rule2.(*Rule).specs...)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("reset check", func(t *testing.T) {
|
t.Run("reset check", func(t *testing.T) {
|
||||||
// add second rule
|
// add second rule
|
||||||
ip := net.ParseIP("10.20.0.3")
|
ip := net.ParseIP("10.20.0.3")
|
||||||
port := &fw.Port{Values: []int{5353}}
|
port := &fw.Port{Proto: fw.PortProtocolUDP, Values: []int{5353}}
|
||||||
_, err = manager.AddFiltering(ip, "udp", nil, port, fw.RuleDirectionOUT, fw.ActionAccept, "accept Fake DNS traffic")
|
_, err = manager.AddFiltering(ip, port, fw.DirectionDst, fw.ActionAccept, "accept Fake DNS traffic")
|
||||||
require.NoError(t, err, "failed to add rule")
|
if err != nil {
|
||||||
|
t.Errorf("failed to add rule: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
err = manager.Reset()
|
if err := manager.Reset(); err != nil {
|
||||||
require.NoError(t, err, "failed to reset")
|
t.Errorf("failed to reset: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
ok, err := ipv4Client.ChainExists("filter", ChainInputFilterName)
|
ok, err := ipv4Client.ChainExists("filter", ChainFilterName)
|
||||||
require.NoError(t, err, "failed check chain exists")
|
if err != nil {
|
||||||
|
t.Errorf("failed to drop chain: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
require.NoErrorf(t, err, "chain '%v' still exists after Reset", ChainInputFilterName)
|
t.Errorf("chain '%v' still exists after Reset", ChainFilterName)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkRuleSpecs(t *testing.T, ipv4Client *iptables.IPTables, chainName string, mustExists bool, rulespec ...string) {
|
func checkRuleSpecs(t *testing.T, ipv4Client *iptables.IPTables, mustExists bool, rulespec ...string) {
|
||||||
exists, err := ipv4Client.Exists("filter", chainName, rulespec...)
|
exists, err := ipv4Client.Exists("filter", ChainFilterName, rulespec...)
|
||||||
require.NoError(t, err, "failed to check rule")
|
if err != nil {
|
||||||
require.Falsef(t, !exists && mustExists, "rule '%v' does not exist", rulespec)
|
t.Errorf("failed to check rule: %v", err)
|
||||||
require.Falsef(t, exists && !mustExists, "rule '%v' exist", rulespec)
|
return
|
||||||
}
|
|
||||||
|
|
||||||
func TestIptablesCreatePerformance(t *testing.T) {
|
|
||||||
mock := &iFaceMock{
|
|
||||||
NameFunc: func() string {
|
|
||||||
return "lo"
|
|
||||||
},
|
|
||||||
AddressFunc: func() iface.WGAddress {
|
|
||||||
return iface.WGAddress{
|
|
||||||
IP: net.ParseIP("10.20.0.1"),
|
|
||||||
Network: &net.IPNet{
|
|
||||||
IP: net.ParseIP("10.20.0.0"),
|
|
||||||
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testMax := range []int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} {
|
if !exists && mustExists {
|
||||||
t.Run(fmt.Sprintf("Testing %d rules", testMax), func(t *testing.T) {
|
t.Errorf("rule '%v' does not exist", rulespec)
|
||||||
// just check on the local interface
|
return
|
||||||
manager, err := Create(mock)
|
}
|
||||||
require.NoError(t, err)
|
if exists && !mustExists {
|
||||||
time.Sleep(time.Second)
|
t.Errorf("rule '%v' exist", rulespec)
|
||||||
|
return
|
||||||
defer func() {
|
|
||||||
if err := manager.Reset(); err != nil {
|
|
||||||
t.Errorf("clear the manager state: %v", err)
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}()
|
|
||||||
|
|
||||||
_, err = manager.client(net.ParseIP("10.20.0.100"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
ip := net.ParseIP("10.20.0.100")
|
|
||||||
start := time.Now()
|
|
||||||
for i := 0; i < testMax; i++ {
|
|
||||||
port := &fw.Port{Values: []int{1000 + i}}
|
|
||||||
if i%2 == 0 {
|
|
||||||
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionOUT, fw.ActionAccept, "accept HTTP traffic")
|
|
||||||
} else {
|
|
||||||
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionIN, fw.ActionAccept, "accept HTTP traffic")
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(t, err, "failed to add rule")
|
|
||||||
}
|
|
||||||
t.Logf("execution avg per rule: %s", time.Since(start)/time.Duration(testMax))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ package iptables
|
|||||||
type Rule struct {
|
type Rule struct {
|
||||||
id string
|
id string
|
||||||
specs []string
|
specs []string
|
||||||
dst bool
|
|
||||||
v6 bool
|
v6 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,511 +0,0 @@
|
|||||||
package nftables
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/google/nftables"
|
|
||||||
"github.com/google/nftables/expr"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
fw "github.com/netbirdio/netbird/client/firewall"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// FilterTableName is the name of the table that is used for filtering by the Netbird client
|
|
||||||
FilterTableName = "netbird-acl"
|
|
||||||
|
|
||||||
// FilterInputChainName is the name of the chain that is used for filtering incoming packets
|
|
||||||
FilterInputChainName = "netbird-acl-input-filter"
|
|
||||||
|
|
||||||
// FilterOutputChainName is the name of the chain that is used for filtering outgoing packets
|
|
||||||
FilterOutputChainName = "netbird-acl-output-filter"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Manager of iptables firewall
|
|
||||||
type Manager struct {
|
|
||||||
mutex sync.Mutex
|
|
||||||
|
|
||||||
conn *nftables.Conn
|
|
||||||
tableIPv4 *nftables.Table
|
|
||||||
tableIPv6 *nftables.Table
|
|
||||||
|
|
||||||
filterInputChainIPv4 *nftables.Chain
|
|
||||||
filterOutputChainIPv4 *nftables.Chain
|
|
||||||
|
|
||||||
filterInputChainIPv6 *nftables.Chain
|
|
||||||
filterOutputChainIPv6 *nftables.Chain
|
|
||||||
|
|
||||||
wgIface iFaceMapper
|
|
||||||
}
|
|
||||||
|
|
||||||
// iFaceMapper defines subset methods of interface required for manager
|
|
||||||
type iFaceMapper interface {
|
|
||||||
Name() string
|
|
||||||
Address() iface.WGAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create nftables firewall manager
|
|
||||||
func Create(wgIface iFaceMapper) (*Manager, error) {
|
|
||||||
m := &Manager{
|
|
||||||
conn: &nftables.Conn{},
|
|
||||||
wgIface: wgIface,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.Reset(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFiltering rule to the firewall
|
|
||||||
//
|
|
||||||
// If comment argument is empty firewall manager should set
|
|
||||||
// rule ID as comment for the rule
|
|
||||||
func (m *Manager) AddFiltering(
|
|
||||||
ip net.IP,
|
|
||||||
proto fw.Protocol,
|
|
||||||
sPort *fw.Port,
|
|
||||||
dPort *fw.Port,
|
|
||||||
direction fw.RuleDirection,
|
|
||||||
action fw.Action,
|
|
||||||
comment string,
|
|
||||||
) (fw.Rule, error) {
|
|
||||||
m.mutex.Lock()
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
table *nftables.Table
|
|
||||||
chain *nftables.Chain
|
|
||||||
)
|
|
||||||
|
|
||||||
if direction == fw.RuleDirectionOUT {
|
|
||||||
table, chain, err = m.chain(
|
|
||||||
ip,
|
|
||||||
FilterOutputChainName,
|
|
||||||
nftables.ChainHookOutput,
|
|
||||||
nftables.ChainPriorityFilter,
|
|
||||||
nftables.ChainTypeFilter)
|
|
||||||
} else {
|
|
||||||
table, chain, err = m.chain(
|
|
||||||
ip,
|
|
||||||
FilterInputChainName,
|
|
||||||
nftables.ChainHookInput,
|
|
||||||
nftables.ChainPriorityFilter,
|
|
||||||
nftables.ChainTypeFilter)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ifaceKey := expr.MetaKeyIIFNAME
|
|
||||||
if direction == fw.RuleDirectionOUT {
|
|
||||||
ifaceKey = expr.MetaKeyOIFNAME
|
|
||||||
}
|
|
||||||
expressions := []expr.Any{
|
|
||||||
&expr.Meta{Key: ifaceKey, Register: 1},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: ifname(m.wgIface.Name()),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if proto != "all" {
|
|
||||||
expressions = append(expressions, &expr.Payload{
|
|
||||||
DestRegister: 1,
|
|
||||||
Base: expr.PayloadBaseNetworkHeader,
|
|
||||||
Offset: uint32(9),
|
|
||||||
Len: uint32(1),
|
|
||||||
})
|
|
||||||
|
|
||||||
var protoData []byte
|
|
||||||
switch proto {
|
|
||||||
case fw.ProtocolTCP:
|
|
||||||
protoData = []byte{unix.IPPROTO_TCP}
|
|
||||||
case fw.ProtocolUDP:
|
|
||||||
protoData = []byte{unix.IPPROTO_UDP}
|
|
||||||
case fw.ProtocolICMP:
|
|
||||||
protoData = []byte{unix.IPPROTO_ICMP}
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported protocol: %s", proto)
|
|
||||||
}
|
|
||||||
expressions = append(expressions, &expr.Cmp{
|
|
||||||
Register: 1,
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Data: protoData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't use IP matching if IP is ip 0.0.0.0
|
|
||||||
if s := ip.String(); s != "0.0.0.0" && s != "::" {
|
|
||||||
// source address position
|
|
||||||
var adrLen, adrOffset uint32
|
|
||||||
if ip.To4() == nil {
|
|
||||||
adrLen = 16
|
|
||||||
adrOffset = 8
|
|
||||||
} else {
|
|
||||||
adrLen = 4
|
|
||||||
adrOffset = 12
|
|
||||||
}
|
|
||||||
|
|
||||||
// change to destination address position if need
|
|
||||||
if direction == fw.RuleDirectionOUT {
|
|
||||||
adrOffset += adrLen
|
|
||||||
}
|
|
||||||
|
|
||||||
ipToAdd, _ := netip.AddrFromSlice(ip)
|
|
||||||
add := ipToAdd.Unmap()
|
|
||||||
|
|
||||||
expressions = append(expressions,
|
|
||||||
&expr.Payload{
|
|
||||||
DestRegister: 1,
|
|
||||||
Base: expr.PayloadBaseNetworkHeader,
|
|
||||||
Offset: adrOffset,
|
|
||||||
Len: adrLen,
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: add.AsSlice(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if sPort != nil && len(sPort.Values) != 0 {
|
|
||||||
expressions = append(expressions,
|
|
||||||
&expr.Payload{
|
|
||||||
DestRegister: 1,
|
|
||||||
Base: expr.PayloadBaseTransportHeader,
|
|
||||||
Offset: 0,
|
|
||||||
Len: 2,
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: encodePort(*sPort),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dPort != nil && len(dPort.Values) != 0 {
|
|
||||||
expressions = append(expressions,
|
|
||||||
&expr.Payload{
|
|
||||||
DestRegister: 1,
|
|
||||||
Base: expr.PayloadBaseTransportHeader,
|
|
||||||
Offset: 2,
|
|
||||||
Len: 2,
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: encodePort(*dPort),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if action == fw.ActionAccept {
|
|
||||||
expressions = append(expressions, &expr.Verdict{Kind: expr.VerdictAccept})
|
|
||||||
} else {
|
|
||||||
expressions = append(expressions, &expr.Verdict{Kind: expr.VerdictDrop})
|
|
||||||
}
|
|
||||||
|
|
||||||
id := uuid.New().String()
|
|
||||||
userData := []byte(strings.Join([]string{id, comment}, " "))
|
|
||||||
|
|
||||||
_ = m.conn.InsertRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chain,
|
|
||||||
Position: 0,
|
|
||||||
Exprs: expressions,
|
|
||||||
UserData: userData,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := m.conn.Flush(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
list, err := m.conn.GetRules(table, chain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the rule to the chain
|
|
||||||
rule := &Rule{id: id}
|
|
||||||
for _, r := range list {
|
|
||||||
if bytes.Equal(r.UserData, userData) {
|
|
||||||
rule.Rule = r
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if rule.Rule == nil {
|
|
||||||
return nil, fmt.Errorf("rule not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
return rule, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// chain returns the chain for the given IP address with specific settings
|
|
||||||
func (m *Manager) chain(
|
|
||||||
ip net.IP,
|
|
||||||
name string,
|
|
||||||
hook nftables.ChainHook,
|
|
||||||
priority nftables.ChainPriority,
|
|
||||||
cType nftables.ChainType,
|
|
||||||
) (*nftables.Table, *nftables.Chain, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
getChain := func(c *nftables.Chain, tf nftables.TableFamily) (*nftables.Chain, error) {
|
|
||||||
if c != nil {
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
return m.createChainIfNotExists(tf, name, hook, priority, cType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip.To4() != nil {
|
|
||||||
if name == FilterInputChainName {
|
|
||||||
m.filterInputChainIPv4, err = getChain(m.filterInputChainIPv4, nftables.TableFamilyIPv4)
|
|
||||||
return m.tableIPv4, m.filterInputChainIPv4, err
|
|
||||||
}
|
|
||||||
m.filterOutputChainIPv4, err = getChain(m.filterOutputChainIPv4, nftables.TableFamilyIPv4)
|
|
||||||
return m.tableIPv4, m.filterOutputChainIPv4, err
|
|
||||||
}
|
|
||||||
if name == FilterInputChainName {
|
|
||||||
m.filterInputChainIPv6, err = getChain(m.filterInputChainIPv6, nftables.TableFamilyIPv6)
|
|
||||||
return m.tableIPv4, m.filterInputChainIPv6, err
|
|
||||||
}
|
|
||||||
m.filterOutputChainIPv6, err = getChain(m.filterOutputChainIPv6, nftables.TableFamilyIPv6)
|
|
||||||
return m.tableIPv4, m.filterOutputChainIPv6, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// table returns the table for the given family of the IP address
|
|
||||||
func (m *Manager) table(family nftables.TableFamily) (*nftables.Table, error) {
|
|
||||||
if family == nftables.TableFamilyIPv4 {
|
|
||||||
if m.tableIPv4 != nil {
|
|
||||||
return m.tableIPv4, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
table, err := m.createTableIfNotExists(nftables.TableFamilyIPv4)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m.tableIPv4 = table
|
|
||||||
return m.tableIPv4, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.tableIPv6 != nil {
|
|
||||||
return m.tableIPv6, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
table, err := m.createTableIfNotExists(nftables.TableFamilyIPv6)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
m.tableIPv6 = table
|
|
||||||
return m.tableIPv6, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) createTableIfNotExists(family nftables.TableFamily) (*nftables.Table, error) {
|
|
||||||
tables, err := m.conn.ListTablesOfFamily(family)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("list of tables: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, t := range tables {
|
|
||||||
if t.Name == FilterTableName {
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.conn.AddTable(&nftables.Table{Name: FilterTableName, Family: nftables.TableFamilyIPv4}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) createChainIfNotExists(
|
|
||||||
family nftables.TableFamily,
|
|
||||||
name string,
|
|
||||||
hooknum nftables.ChainHook,
|
|
||||||
priority nftables.ChainPriority,
|
|
||||||
chainType nftables.ChainType,
|
|
||||||
) (*nftables.Chain, error) {
|
|
||||||
table, err := m.table(family)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
chains, err := m.conn.ListChainsOfTableFamily(family)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("list of chains: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range chains {
|
|
||||||
if c.Name == name && c.Table.Name == table.Name {
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
polAccept := nftables.ChainPolicyAccept
|
|
||||||
chain := &nftables.Chain{
|
|
||||||
Name: name,
|
|
||||||
Table: table,
|
|
||||||
Hooknum: hooknum,
|
|
||||||
Priority: priority,
|
|
||||||
Type: chainType,
|
|
||||||
Policy: &polAccept,
|
|
||||||
}
|
|
||||||
|
|
||||||
chain = m.conn.AddChain(chain)
|
|
||||||
|
|
||||||
ifaceKey := expr.MetaKeyIIFNAME
|
|
||||||
shiftDSTAddr := 0
|
|
||||||
if name == FilterOutputChainName {
|
|
||||||
ifaceKey = expr.MetaKeyOIFNAME
|
|
||||||
shiftDSTAddr = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
expressions := []expr.Any{
|
|
||||||
&expr.Meta{Key: ifaceKey, Register: 1},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: ifname(m.wgIface.Name()),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
mask, _ := netip.AddrFromSlice(m.wgIface.Address().Network.Mask)
|
|
||||||
if m.wgIface.Address().IP.To4() == nil {
|
|
||||||
ip, _ := netip.AddrFromSlice(m.wgIface.Address().Network.IP.To16())
|
|
||||||
expressions = append(expressions,
|
|
||||||
&expr.Payload{
|
|
||||||
DestRegister: 2,
|
|
||||||
Base: expr.PayloadBaseNetworkHeader,
|
|
||||||
Offset: uint32(8 + (16 * shiftDSTAddr)),
|
|
||||||
Len: 16,
|
|
||||||
},
|
|
||||||
&expr.Bitwise{
|
|
||||||
SourceRegister: 2,
|
|
||||||
DestRegister: 2,
|
|
||||||
Len: 16,
|
|
||||||
Xor: []byte{0x0, 0x0, 0x0, 0x0},
|
|
||||||
Mask: mask.Unmap().AsSlice(),
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpNeq,
|
|
||||||
Register: 2,
|
|
||||||
Data: ip.Unmap().AsSlice(),
|
|
||||||
},
|
|
||||||
&expr.Verdict{Kind: expr.VerdictAccept},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
ip, _ := netip.AddrFromSlice(m.wgIface.Address().Network.IP.To4())
|
|
||||||
expressions = append(expressions,
|
|
||||||
&expr.Payload{
|
|
||||||
DestRegister: 2,
|
|
||||||
Base: expr.PayloadBaseNetworkHeader,
|
|
||||||
Offset: uint32(12 + (4 * shiftDSTAddr)),
|
|
||||||
Len: 4,
|
|
||||||
},
|
|
||||||
&expr.Bitwise{
|
|
||||||
SourceRegister: 2,
|
|
||||||
DestRegister: 2,
|
|
||||||
Len: 4,
|
|
||||||
Xor: []byte{0x0, 0x0, 0x0, 0x0},
|
|
||||||
Mask: m.wgIface.Address().Network.Mask,
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpNeq,
|
|
||||||
Register: 2,
|
|
||||||
Data: ip.Unmap().AsSlice(),
|
|
||||||
},
|
|
||||||
&expr.Verdict{Kind: expr.VerdictAccept},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = m.conn.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chain,
|
|
||||||
Exprs: expressions,
|
|
||||||
})
|
|
||||||
|
|
||||||
expressions = []expr.Any{
|
|
||||||
&expr.Meta{Key: ifaceKey, Register: 1},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: ifname(m.wgIface.Name()),
|
|
||||||
},
|
|
||||||
&expr.Verdict{Kind: expr.VerdictDrop},
|
|
||||||
}
|
|
||||||
_ = m.conn.AddRule(&nftables.Rule{
|
|
||||||
Table: table,
|
|
||||||
Chain: chain,
|
|
||||||
Exprs: expressions,
|
|
||||||
})
|
|
||||||
if err := m.conn.Flush(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return chain, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteRule from the firewall by rule definition
|
|
||||||
func (m *Manager) DeleteRule(rule fw.Rule) error {
|
|
||||||
nativeRule, ok := rule.(*Rule)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("invalid rule type")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.conn.DelRule(nativeRule.Rule); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset firewall to the default state
|
|
||||||
func (m *Manager) Reset() error {
|
|
||||||
m.mutex.Lock()
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
|
|
||||||
chains, err := m.conn.ListChains()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("list of chains: %w", err)
|
|
||||||
}
|
|
||||||
for _, c := range chains {
|
|
||||||
if c.Name == FilterInputChainName || c.Name == FilterOutputChainName {
|
|
||||||
m.conn.DelChain(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tables, err := m.conn.ListTables()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("list of tables: %w", err)
|
|
||||||
}
|
|
||||||
for _, t := range tables {
|
|
||||||
if t.Name == FilterTableName {
|
|
||||||
m.conn.DelTable(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodePort(port fw.Port) []byte {
|
|
||||||
bs := make([]byte, 2)
|
|
||||||
binary.BigEndian.PutUint16(bs, uint16(port.Values[0]))
|
|
||||||
return bs
|
|
||||||
}
|
|
||||||
|
|
||||||
func ifname(n string) []byte {
|
|
||||||
b := make([]byte, 16)
|
|
||||||
copy(b, []byte(n+"\x00"))
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
package nftables
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/nftables"
|
|
||||||
"github.com/google/nftables/expr"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
fw "github.com/netbirdio/netbird/client/firewall"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
|
||||||
|
|
||||||
// iFaceMapper defines subset methods of interface required for manager
|
|
||||||
type iFaceMock struct {
|
|
||||||
NameFunc func() string
|
|
||||||
AddressFunc func() iface.WGAddress
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *iFaceMock) Name() string {
|
|
||||||
if i.NameFunc != nil {
|
|
||||||
return i.NameFunc()
|
|
||||||
}
|
|
||||||
panic("NameFunc is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *iFaceMock) Address() iface.WGAddress {
|
|
||||||
if i.AddressFunc != nil {
|
|
||||||
return i.AddressFunc()
|
|
||||||
}
|
|
||||||
panic("AddressFunc is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNftablesManager(t *testing.T) {
|
|
||||||
mock := &iFaceMock{
|
|
||||||
NameFunc: func() string {
|
|
||||||
return "lo"
|
|
||||||
},
|
|
||||||
AddressFunc: func() iface.WGAddress {
|
|
||||||
return iface.WGAddress{
|
|
||||||
IP: net.ParseIP("100.96.0.1"),
|
|
||||||
Network: &net.IPNet{
|
|
||||||
IP: net.ParseIP("100.96.0.0"),
|
|
||||||
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// just check on the local interface
|
|
||||||
manager, err := Create(mock)
|
|
||||||
require.NoError(t, err)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
err = manager.Reset()
|
|
||||||
require.NoError(t, err, "failed to reset")
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}()
|
|
||||||
|
|
||||||
ip := net.ParseIP("100.96.0.1")
|
|
||||||
|
|
||||||
testClient := &nftables.Conn{}
|
|
||||||
|
|
||||||
rule, err := manager.AddFiltering(
|
|
||||||
ip,
|
|
||||||
fw.ProtocolTCP,
|
|
||||||
nil,
|
|
||||||
&fw.Port{Values: []int{53}},
|
|
||||||
fw.RuleDirectionIN,
|
|
||||||
fw.ActionDrop,
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
require.NoError(t, err, "failed to add rule")
|
|
||||||
|
|
||||||
rules, err := testClient.GetRules(manager.tableIPv4, manager.filterInputChainIPv4)
|
|
||||||
require.NoError(t, err, "failed to get rules")
|
|
||||||
// test expectations:
|
|
||||||
// 1) regular rule
|
|
||||||
// 2) "accept extra routed traffic rule" for the interface
|
|
||||||
// 3) "drop all rule" for the interface
|
|
||||||
require.Len(t, rules, 3, "expected 3 rules")
|
|
||||||
|
|
||||||
ipToAdd, _ := netip.AddrFromSlice(ip)
|
|
||||||
add := ipToAdd.Unmap()
|
|
||||||
expectedExprs := []expr.Any{
|
|
||||||
&expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: ifname("lo"),
|
|
||||||
},
|
|
||||||
&expr.Payload{
|
|
||||||
DestRegister: 1,
|
|
||||||
Base: expr.PayloadBaseNetworkHeader,
|
|
||||||
Offset: uint32(9),
|
|
||||||
Len: uint32(1),
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Register: 1,
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Data: []byte{unix.IPPROTO_TCP},
|
|
||||||
},
|
|
||||||
&expr.Payload{
|
|
||||||
DestRegister: 1,
|
|
||||||
Base: expr.PayloadBaseNetworkHeader,
|
|
||||||
Offset: 12,
|
|
||||||
Len: 4,
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: add.AsSlice(),
|
|
||||||
},
|
|
||||||
&expr.Payload{
|
|
||||||
DestRegister: 1,
|
|
||||||
Base: expr.PayloadBaseTransportHeader,
|
|
||||||
Offset: 2,
|
|
||||||
Len: 2,
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: []byte{0, 53},
|
|
||||||
},
|
|
||||||
&expr.Verdict{Kind: expr.VerdictDrop},
|
|
||||||
}
|
|
||||||
require.ElementsMatch(t, rules[0].Exprs, expectedExprs, "expected the same expressions")
|
|
||||||
|
|
||||||
err = manager.DeleteRule(rule)
|
|
||||||
require.NoError(t, err, "failed to delete rule")
|
|
||||||
|
|
||||||
rules, err = testClient.GetRules(manager.tableIPv4, manager.filterInputChainIPv4)
|
|
||||||
require.NoError(t, err, "failed to get rules")
|
|
||||||
// test expectations:
|
|
||||||
// 1) "accept extra routed traffic rule" for the interface
|
|
||||||
// 2) "drop all rule" for the interface
|
|
||||||
require.Len(t, rules, 2, "expected 2 rules after deleteion")
|
|
||||||
|
|
||||||
err = manager.Reset()
|
|
||||||
require.NoError(t, err, "failed to reset")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNFtablesCreatePerformance(t *testing.T) {
|
|
||||||
mock := &iFaceMock{
|
|
||||||
NameFunc: func() string {
|
|
||||||
return "lo"
|
|
||||||
},
|
|
||||||
AddressFunc: func() iface.WGAddress {
|
|
||||||
return iface.WGAddress{
|
|
||||||
IP: net.ParseIP("100.96.0.1"),
|
|
||||||
Network: &net.IPNet{
|
|
||||||
IP: net.ParseIP("100.96.0.0"),
|
|
||||||
Mask: net.IPv4Mask(255, 255, 255, 0),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, testMax := range []int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} {
|
|
||||||
t.Run(fmt.Sprintf("Testing %d rules", testMax), func(t *testing.T) {
|
|
||||||
// just check on the local interface
|
|
||||||
manager, err := Create(mock)
|
|
||||||
require.NoError(t, err)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := manager.Reset(); err != nil {
|
|
||||||
t.Errorf("clear the manager state: %v", err)
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}()
|
|
||||||
|
|
||||||
ip := net.ParseIP("10.20.0.100")
|
|
||||||
start := time.Now()
|
|
||||||
for i := 0; i < testMax; i++ {
|
|
||||||
port := &fw.Port{Values: []int{1000 + i}}
|
|
||||||
if i%2 == 0 {
|
|
||||||
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionOUT, fw.ActionAccept, "accept HTTP traffic")
|
|
||||||
} else {
|
|
||||||
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionIN, fw.ActionAccept, "accept HTTP traffic")
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(t, err, "failed to add rule")
|
|
||||||
}
|
|
||||||
t.Logf("execution avg per rule: %s", time.Since(start)/time.Duration(testMax))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package nftables
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/google/nftables"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Rule to handle management of rules
|
|
||||||
type Rule struct {
|
|
||||||
*nftables.Rule
|
|
||||||
id string
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRuleID returns the rule id
|
|
||||||
func (r *Rule) GetRuleID() string {
|
|
||||||
return r.id
|
|
||||||
}
|
|
||||||
@@ -1,27 +1,14 @@
|
|||||||
package firewall
|
package firewall
|
||||||
|
|
||||||
import (
|
// PortProtocol is the protocol of the port
|
||||||
"strconv"
|
type PortProtocol string
|
||||||
)
|
|
||||||
|
|
||||||
// Protocol is the protocol of the port
|
|
||||||
type Protocol string
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ProtocolTCP is the TCP protocol
|
// PortProtocolTCP is the TCP protocol
|
||||||
ProtocolTCP Protocol = "tcp"
|
PortProtocolTCP PortProtocol = "tcp"
|
||||||
|
|
||||||
// ProtocolUDP is the UDP protocol
|
// PortProtocolUDP is the UDP protocol
|
||||||
ProtocolUDP Protocol = "udp"
|
PortProtocolUDP PortProtocol = "udp"
|
||||||
|
|
||||||
// ProtocolICMP is the ICMP protocol
|
|
||||||
ProtocolICMP Protocol = "icmp"
|
|
||||||
|
|
||||||
// ProtocolALL cover all supported protocols
|
|
||||||
ProtocolALL Protocol = "all"
|
|
||||||
|
|
||||||
// ProtocolUnknown unknown protocol
|
|
||||||
ProtocolUnknown Protocol = "unknown"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Port of the address for firewall rule
|
// Port of the address for firewall rule
|
||||||
@@ -31,16 +18,7 @@ type Port struct {
|
|||||||
|
|
||||||
// Values contains one value for single port, multiple values for the list of ports, or two values for the range of ports
|
// Values contains one value for single port, multiple values for the list of ports, or two values for the range of ports
|
||||||
Values []int
|
Values []int
|
||||||
}
|
|
||||||
|
|
||||||
// String interface implementation
|
// Proto is the protocol of the port
|
||||||
func (p *Port) String() string {
|
Proto PortProtocol
|
||||||
var ports string
|
|
||||||
for _, port := range p.Values {
|
|
||||||
if ports != "" {
|
|
||||||
ports += ","
|
|
||||||
}
|
|
||||||
ports += strconv.Itoa(port)
|
|
||||||
}
|
|
||||||
return ports
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
package uspfilter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
|
|
||||||
fw "github.com/netbirdio/netbird/client/firewall"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Rule to handle management of rules
|
|
||||||
type Rule struct {
|
|
||||||
id string
|
|
||||||
ip net.IP
|
|
||||||
ipLayer gopacket.LayerType
|
|
||||||
matchByIP bool
|
|
||||||
protoLayer gopacket.LayerType
|
|
||||||
direction fw.RuleDirection
|
|
||||||
sPort uint16
|
|
||||||
dPort uint16
|
|
||||||
drop bool
|
|
||||||
comment string
|
|
||||||
|
|
||||||
udpHook func([]byte) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRuleID returns the rule id
|
|
||||||
func (r *Rule) GetRuleID() string {
|
|
||||||
return r.id
|
|
||||||
}
|
|
||||||
@@ -1,359 +0,0 @@
|
|||||||
package uspfilter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
"github.com/google/gopacket/layers"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
fw "github.com/netbirdio/netbird/client/firewall"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
|
||||||
|
|
||||||
const layerTypeAll = 0
|
|
||||||
|
|
||||||
// IFaceMapper defines subset methods of interface required for manager
|
|
||||||
type IFaceMapper interface {
|
|
||||||
SetFilter(iface.PacketFilter) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manager userspace firewall manager
|
|
||||||
type Manager struct {
|
|
||||||
outgoingRules []Rule
|
|
||||||
incomingRules []Rule
|
|
||||||
rulesIndex map[string]int
|
|
||||||
wgNetwork *net.IPNet
|
|
||||||
decoders sync.Pool
|
|
||||||
|
|
||||||
mutex sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// decoder for packages
|
|
||||||
type decoder struct {
|
|
||||||
eth layers.Ethernet
|
|
||||||
ip4 layers.IPv4
|
|
||||||
ip6 layers.IPv6
|
|
||||||
tcp layers.TCP
|
|
||||||
udp layers.UDP
|
|
||||||
icmp4 layers.ICMPv4
|
|
||||||
icmp6 layers.ICMPv6
|
|
||||||
decoded []gopacket.LayerType
|
|
||||||
parser *gopacket.DecodingLayerParser
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create userspace firewall manager constructor
|
|
||||||
func Create(iface IFaceMapper) (*Manager, error) {
|
|
||||||
m := &Manager{
|
|
||||||
rulesIndex: make(map[string]int),
|
|
||||||
decoders: sync.Pool{
|
|
||||||
New: func() any {
|
|
||||||
d := &decoder{
|
|
||||||
decoded: []gopacket.LayerType{},
|
|
||||||
}
|
|
||||||
d.parser = gopacket.NewDecodingLayerParser(
|
|
||||||
layers.LayerTypeIPv4,
|
|
||||||
&d.eth, &d.ip4, &d.ip6, &d.icmp4, &d.icmp6, &d.tcp, &d.udp,
|
|
||||||
)
|
|
||||||
d.parser.IgnoreUnsupported = true
|
|
||||||
return d
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := iface.SetFilter(m); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFiltering rule to the firewall
|
|
||||||
//
|
|
||||||
// If comment argument is empty firewall manager should set
|
|
||||||
// rule ID as comment for the rule
|
|
||||||
func (m *Manager) AddFiltering(
|
|
||||||
ip net.IP,
|
|
||||||
proto fw.Protocol,
|
|
||||||
sPort *fw.Port,
|
|
||||||
dPort *fw.Port,
|
|
||||||
direction fw.RuleDirection,
|
|
||||||
action fw.Action,
|
|
||||||
comment string,
|
|
||||||
) (fw.Rule, error) {
|
|
||||||
r := Rule{
|
|
||||||
id: uuid.New().String(),
|
|
||||||
ip: ip,
|
|
||||||
ipLayer: layers.LayerTypeIPv6,
|
|
||||||
matchByIP: true,
|
|
||||||
direction: direction,
|
|
||||||
drop: action == fw.ActionDrop,
|
|
||||||
comment: comment,
|
|
||||||
}
|
|
||||||
if ipNormalized := ip.To4(); ipNormalized != nil {
|
|
||||||
r.ipLayer = layers.LayerTypeIPv4
|
|
||||||
r.ip = ipNormalized
|
|
||||||
}
|
|
||||||
|
|
||||||
if s := r.ip.String(); s == "0.0.0.0" || s == "::" {
|
|
||||||
r.matchByIP = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if sPort != nil && len(sPort.Values) == 1 {
|
|
||||||
r.sPort = uint16(sPort.Values[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
if dPort != nil && len(dPort.Values) == 1 {
|
|
||||||
r.dPort = uint16(dPort.Values[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
switch proto {
|
|
||||||
case fw.ProtocolTCP:
|
|
||||||
r.protoLayer = layers.LayerTypeTCP
|
|
||||||
case fw.ProtocolUDP:
|
|
||||||
r.protoLayer = layers.LayerTypeUDP
|
|
||||||
case fw.ProtocolICMP:
|
|
||||||
r.protoLayer = layers.LayerTypeICMPv4
|
|
||||||
if r.ipLayer == layers.LayerTypeIPv6 {
|
|
||||||
r.protoLayer = layers.LayerTypeICMPv6
|
|
||||||
}
|
|
||||||
case fw.ProtocolALL:
|
|
||||||
r.protoLayer = layerTypeAll
|
|
||||||
}
|
|
||||||
|
|
||||||
m.mutex.Lock()
|
|
||||||
var p int
|
|
||||||
if direction == fw.RuleDirectionIN {
|
|
||||||
m.incomingRules = append(m.incomingRules, r)
|
|
||||||
p = len(m.incomingRules) - 1
|
|
||||||
} else {
|
|
||||||
m.outgoingRules = append(m.outgoingRules, r)
|
|
||||||
p = len(m.outgoingRules) - 1
|
|
||||||
}
|
|
||||||
m.rulesIndex[r.id] = p
|
|
||||||
m.mutex.Unlock()
|
|
||||||
|
|
||||||
return &r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteRule from the firewall by rule definition
|
|
||||||
func (m *Manager) DeleteRule(rule fw.Rule) error {
|
|
||||||
m.mutex.Lock()
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
|
|
||||||
r, ok := rule.(*Rule)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("delete rule: invalid rule type: %T", rule)
|
|
||||||
}
|
|
||||||
|
|
||||||
p, ok := m.rulesIndex[r.id]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("delete rule: no rule with such id: %v", r.id)
|
|
||||||
}
|
|
||||||
delete(m.rulesIndex, r.id)
|
|
||||||
|
|
||||||
var toUpdate []Rule
|
|
||||||
if r.direction == fw.RuleDirectionIN {
|
|
||||||
m.incomingRules = append(m.incomingRules[:p], m.incomingRules[p+1:]...)
|
|
||||||
toUpdate = m.incomingRules
|
|
||||||
} else {
|
|
||||||
m.outgoingRules = append(m.outgoingRules[:p], m.outgoingRules[p+1:]...)
|
|
||||||
toUpdate = m.outgoingRules
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(toUpdate); i++ {
|
|
||||||
m.rulesIndex[toUpdate[i].id] = i
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset firewall to the default state
|
|
||||||
func (m *Manager) Reset() error {
|
|
||||||
m.mutex.Lock()
|
|
||||||
defer m.mutex.Unlock()
|
|
||||||
|
|
||||||
m.outgoingRules = m.outgoingRules[:0]
|
|
||||||
m.incomingRules = m.incomingRules[:0]
|
|
||||||
m.rulesIndex = make(map[string]int)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropOutgoing filter outgoing packets
|
|
||||||
func (m *Manager) DropOutgoing(packetData []byte) bool {
|
|
||||||
return m.dropFilter(packetData, m.outgoingRules, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropIncoming filter incoming packets
|
|
||||||
func (m *Manager) DropIncoming(packetData []byte) bool {
|
|
||||||
return m.dropFilter(packetData, m.incomingRules, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// dropFilter imlements same logic for booth direction of the traffic
|
|
||||||
func (m *Manager) dropFilter(packetData []byte, rules []Rule, isIncomingPacket bool) bool {
|
|
||||||
m.mutex.RLock()
|
|
||||||
defer m.mutex.RUnlock()
|
|
||||||
|
|
||||||
d := m.decoders.Get().(*decoder)
|
|
||||||
defer m.decoders.Put(d)
|
|
||||||
|
|
||||||
if err := d.parser.DecodeLayers(packetData, &d.decoded); err != nil {
|
|
||||||
log.Tracef("couldn't decode layer, err: %s", err)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(d.decoded) < 2 {
|
|
||||||
log.Tracef("not enough levels in network packet")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ipLayer := d.decoded[0]
|
|
||||||
|
|
||||||
switch ipLayer {
|
|
||||||
case layers.LayerTypeIPv4:
|
|
||||||
if !m.wgNetwork.Contains(d.ip4.SrcIP) || !m.wgNetwork.Contains(d.ip4.DstIP) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case layers.LayerTypeIPv6:
|
|
||||||
if !m.wgNetwork.Contains(d.ip6.SrcIP) || !m.wgNetwork.Contains(d.ip6.DstIP) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Errorf("unknown layer: %v", d.decoded[0])
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
payloadLayer := d.decoded[1]
|
|
||||||
|
|
||||||
// check if IP address match by IP
|
|
||||||
for _, rule := range rules {
|
|
||||||
if rule.matchByIP {
|
|
||||||
switch ipLayer {
|
|
||||||
case layers.LayerTypeIPv4:
|
|
||||||
if isIncomingPacket {
|
|
||||||
if !d.ip4.SrcIP.Equal(rule.ip) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !d.ip4.DstIP.Equal(rule.ip) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case layers.LayerTypeIPv6:
|
|
||||||
if isIncomingPacket {
|
|
||||||
if !d.ip6.SrcIP.Equal(rule.ip) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !d.ip6.DstIP.Equal(rule.ip) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rule.protoLayer == layerTypeAll {
|
|
||||||
return rule.drop
|
|
||||||
}
|
|
||||||
|
|
||||||
if payloadLayer != rule.protoLayer {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch payloadLayer {
|
|
||||||
case layers.LayerTypeTCP:
|
|
||||||
if rule.sPort == 0 && rule.dPort == 0 {
|
|
||||||
return rule.drop
|
|
||||||
}
|
|
||||||
if rule.sPort != 0 && rule.sPort == uint16(d.tcp.SrcPort) {
|
|
||||||
return rule.drop
|
|
||||||
}
|
|
||||||
if rule.dPort != 0 && rule.dPort == uint16(d.tcp.DstPort) {
|
|
||||||
return rule.drop
|
|
||||||
}
|
|
||||||
case layers.LayerTypeUDP:
|
|
||||||
// if rule has UDP hook (and if we are here we match this rule)
|
|
||||||
// we ignore rule.drop and call this hook
|
|
||||||
if rule.udpHook != nil {
|
|
||||||
return rule.udpHook(packetData)
|
|
||||||
}
|
|
||||||
|
|
||||||
if rule.sPort == 0 && rule.dPort == 0 {
|
|
||||||
return rule.drop
|
|
||||||
}
|
|
||||||
if rule.sPort != 0 && rule.sPort == uint16(d.udp.SrcPort) {
|
|
||||||
return rule.drop
|
|
||||||
}
|
|
||||||
if rule.dPort != 0 && rule.dPort == uint16(d.udp.DstPort) {
|
|
||||||
return rule.drop
|
|
||||||
}
|
|
||||||
return rule.drop
|
|
||||||
case layers.LayerTypeICMPv4, layers.LayerTypeICMPv6:
|
|
||||||
return rule.drop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// default policy is DROP ALL
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNetwork of the wireguard interface to which filtering applied
|
|
||||||
func (m *Manager) SetNetwork(network *net.IPNet) {
|
|
||||||
m.wgNetwork = network
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddUDPPacketHook calls hook when UDP packet from given direction matched
|
|
||||||
//
|
|
||||||
// Hook function returns flag which indicates should be the matched package dropped or not
|
|
||||||
func (m *Manager) AddUDPPacketHook(
|
|
||||||
in bool, ip net.IP, dPort uint16, hook func([]byte) bool,
|
|
||||||
) string {
|
|
||||||
r := Rule{
|
|
||||||
id: uuid.New().String(),
|
|
||||||
ip: ip,
|
|
||||||
protoLayer: layers.LayerTypeUDP,
|
|
||||||
dPort: dPort,
|
|
||||||
ipLayer: layers.LayerTypeIPv6,
|
|
||||||
direction: fw.RuleDirectionOUT,
|
|
||||||
comment: fmt.Sprintf("UDP Hook direction: %v, ip:%v, dport:%d", in, ip, dPort),
|
|
||||||
udpHook: hook,
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip.To4() != nil {
|
|
||||||
r.ipLayer = layers.LayerTypeIPv4
|
|
||||||
}
|
|
||||||
|
|
||||||
m.mutex.Lock()
|
|
||||||
var toUpdate []Rule
|
|
||||||
if in {
|
|
||||||
r.direction = fw.RuleDirectionIN
|
|
||||||
m.incomingRules = append([]Rule{r}, m.incomingRules...)
|
|
||||||
toUpdate = m.incomingRules
|
|
||||||
} else {
|
|
||||||
m.outgoingRules = append([]Rule{r}, m.outgoingRules...)
|
|
||||||
toUpdate = m.outgoingRules
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range toUpdate {
|
|
||||||
m.rulesIndex[toUpdate[i].id] = i
|
|
||||||
}
|
|
||||||
m.mutex.Unlock()
|
|
||||||
|
|
||||||
return r.id
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemovePacketHook removes packet hook by given ID
|
|
||||||
func (m *Manager) RemovePacketHook(hookID string) error {
|
|
||||||
for _, r := range m.incomingRules {
|
|
||||||
if r.id == hookID {
|
|
||||||
return m.DeleteRule(&r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, r := range m.outgoingRules {
|
|
||||||
if r.id == hookID {
|
|
||||||
return m.DeleteRule(&r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fmt.Errorf("hook with given id not found")
|
|
||||||
}
|
|
||||||
@@ -1,407 +0,0 @@
|
|||||||
package uspfilter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
"github.com/google/gopacket/layers"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
fw "github.com/netbirdio/netbird/client/firewall"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IFaceMock struct {
|
|
||||||
SetFilterFunc func(iface.PacketFilter) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *IFaceMock) SetFilter(iface iface.PacketFilter) error {
|
|
||||||
if i.SetFilterFunc == nil {
|
|
||||||
return fmt.Errorf("not implemented")
|
|
||||||
}
|
|
||||||
return i.SetFilterFunc(iface)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestManagerCreate(t *testing.T) {
|
|
||||||
ifaceMock := &IFaceMock{
|
|
||||||
SetFilterFunc: func(iface.PacketFilter) error { return nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := Create(ifaceMock)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to create Manager: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if m == nil {
|
|
||||||
t.Error("Manager is nil")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestManagerAddFiltering(t *testing.T) {
|
|
||||||
isSetFilterCalled := false
|
|
||||||
ifaceMock := &IFaceMock{
|
|
||||||
SetFilterFunc: func(iface.PacketFilter) error {
|
|
||||||
isSetFilterCalled = true
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := Create(ifaceMock)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to create Manager: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := net.ParseIP("192.168.1.1")
|
|
||||||
proto := fw.ProtocolTCP
|
|
||||||
port := &fw.Port{Values: []int{80}}
|
|
||||||
direction := fw.RuleDirectionOUT
|
|
||||||
action := fw.ActionDrop
|
|
||||||
comment := "Test rule"
|
|
||||||
|
|
||||||
rule, err := m.AddFiltering(ip, proto, nil, port, direction, action, comment)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to add filtering: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if rule == nil {
|
|
||||||
t.Error("Rule is nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isSetFilterCalled {
|
|
||||||
t.Error("SetFilter was not called")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestManagerDeleteRule(t *testing.T) {
|
|
||||||
ifaceMock := &IFaceMock{
|
|
||||||
SetFilterFunc: func(iface.PacketFilter) error { return nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := Create(ifaceMock)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to create Manager: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := net.ParseIP("192.168.1.1")
|
|
||||||
proto := fw.ProtocolTCP
|
|
||||||
port := &fw.Port{Values: []int{80}}
|
|
||||||
direction := fw.RuleDirectionOUT
|
|
||||||
action := fw.ActionDrop
|
|
||||||
comment := "Test rule"
|
|
||||||
|
|
||||||
rule, err := m.AddFiltering(ip, proto, nil, port, direction, action, comment)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to add filtering: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ip = net.ParseIP("192.168.1.1")
|
|
||||||
proto = fw.ProtocolTCP
|
|
||||||
port = &fw.Port{Values: []int{80}}
|
|
||||||
direction = fw.RuleDirectionIN
|
|
||||||
action = fw.ActionDrop
|
|
||||||
comment = "Test rule 2"
|
|
||||||
|
|
||||||
rule2, err := m.AddFiltering(ip, proto, nil, port, direction, action, comment)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to add filtering: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = m.DeleteRule(rule)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to delete rule: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if idx, ok := m.rulesIndex[rule2.GetRuleID()]; !ok || len(m.incomingRules) != 1 || idx != 0 {
|
|
||||||
t.Errorf("rule2 is not in the rulesIndex")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = m.DeleteRule(rule2)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to delete rule: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(m.rulesIndex) != 0 || len(m.incomingRules) != 0 {
|
|
||||||
t.Errorf("rule1 still in the rulesIndex")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddUDPPacketHook(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
in bool
|
|
||||||
expDir fw.RuleDirection
|
|
||||||
ip net.IP
|
|
||||||
dPort uint16
|
|
||||||
hook func([]byte) bool
|
|
||||||
expectedID string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Test Outgoing UDP Packet Hook",
|
|
||||||
in: false,
|
|
||||||
expDir: fw.RuleDirectionOUT,
|
|
||||||
ip: net.IPv4(10, 168, 0, 1),
|
|
||||||
dPort: 8000,
|
|
||||||
hook: func([]byte) bool { return true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Incoming UDP Packet Hook",
|
|
||||||
in: true,
|
|
||||||
expDir: fw.RuleDirectionIN,
|
|
||||||
ip: net.IPv6loopback,
|
|
||||||
dPort: 9000,
|
|
||||||
hook: func([]byte) bool { return false },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
manager := &Manager{
|
|
||||||
incomingRules: []Rule{},
|
|
||||||
outgoingRules: []Rule{},
|
|
||||||
rulesIndex: make(map[string]int),
|
|
||||||
}
|
|
||||||
|
|
||||||
manager.AddUDPPacketHook(tt.in, tt.ip, tt.dPort, tt.hook)
|
|
||||||
|
|
||||||
var addedRule Rule
|
|
||||||
if tt.in {
|
|
||||||
if len(manager.incomingRules) != 1 {
|
|
||||||
t.Errorf("expected 1 incoming rule, got %d", len(manager.incomingRules))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
addedRule = manager.incomingRules[0]
|
|
||||||
} else {
|
|
||||||
if len(manager.outgoingRules) != 1 {
|
|
||||||
t.Errorf("expected 1 outgoing rule, got %d", len(manager.outgoingRules))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
addedRule = manager.outgoingRules[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tt.ip.Equal(addedRule.ip) {
|
|
||||||
t.Errorf("expected ip %s, got %s", tt.ip, addedRule.ip)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if tt.dPort != addedRule.dPort {
|
|
||||||
t.Errorf("expected dPort %d, got %d", tt.dPort, addedRule.dPort)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if layers.LayerTypeUDP != addedRule.protoLayer {
|
|
||||||
t.Errorf("expected protoLayer %s, got %s", layers.LayerTypeUDP, addedRule.protoLayer)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if tt.expDir != addedRule.direction {
|
|
||||||
t.Errorf("expected direction %d, got %d", tt.expDir, addedRule.direction)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if addedRule.udpHook == nil {
|
|
||||||
t.Errorf("expected udpHook to be set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure rulesIndex is correctly updated
|
|
||||||
index, ok := manager.rulesIndex[addedRule.id]
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("expected rule to be in rulesIndex")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if index != 0 {
|
|
||||||
t.Errorf("expected rule index to be 0, got %d", index)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestManagerReset(t *testing.T) {
|
|
||||||
ifaceMock := &IFaceMock{
|
|
||||||
SetFilterFunc: func(iface.PacketFilter) error { return nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := Create(ifaceMock)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to create Manager: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := net.ParseIP("192.168.1.1")
|
|
||||||
proto := fw.ProtocolTCP
|
|
||||||
port := &fw.Port{Values: []int{80}}
|
|
||||||
direction := fw.RuleDirectionOUT
|
|
||||||
action := fw.ActionDrop
|
|
||||||
comment := "Test rule"
|
|
||||||
|
|
||||||
_, err = m.AddFiltering(ip, proto, nil, port, direction, action, comment)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to add filtering: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = m.Reset()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to reset Manager: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(m.rulesIndex) != 0 || len(m.outgoingRules) != 0 || len(m.incomingRules) != 0 {
|
|
||||||
t.Errorf("rules is not empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNotMatchByIP(t *testing.T) {
|
|
||||||
ifaceMock := &IFaceMock{
|
|
||||||
SetFilterFunc: func(iface.PacketFilter) error { return nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := Create(ifaceMock)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to create Manager: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
m.wgNetwork = &net.IPNet{
|
|
||||||
IP: net.ParseIP("100.10.0.0"),
|
|
||||||
Mask: net.CIDRMask(16, 32),
|
|
||||||
}
|
|
||||||
|
|
||||||
ip := net.ParseIP("0.0.0.0")
|
|
||||||
proto := fw.ProtocolUDP
|
|
||||||
direction := fw.RuleDirectionOUT
|
|
||||||
action := fw.ActionAccept
|
|
||||||
comment := "Test rule"
|
|
||||||
|
|
||||||
_, err = m.AddFiltering(ip, proto, nil, nil, direction, action, comment)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to add filtering: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ipv4 := &layers.IPv4{
|
|
||||||
TTL: 64,
|
|
||||||
Version: 4,
|
|
||||||
SrcIP: net.ParseIP("100.10.0.1"),
|
|
||||||
DstIP: net.ParseIP("100.10.0.100"),
|
|
||||||
Protocol: layers.IPProtocolUDP,
|
|
||||||
}
|
|
||||||
udp := &layers.UDP{
|
|
||||||
SrcPort: 51334,
|
|
||||||
DstPort: 53,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := udp.SetNetworkLayerForChecksum(ipv4); err != nil {
|
|
||||||
t.Errorf("failed to set network layer for checksum: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
payload := gopacket.Payload([]byte("test"))
|
|
||||||
|
|
||||||
buf := gopacket.NewSerializeBuffer()
|
|
||||||
opts := gopacket.SerializeOptions{
|
|
||||||
ComputeChecksums: true,
|
|
||||||
FixLengths: true,
|
|
||||||
}
|
|
||||||
if err = gopacket.SerializeLayers(buf, opts, ipv4, udp, payload); err != nil {
|
|
||||||
t.Errorf("failed to serialize packet: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.dropFilter(buf.Bytes(), m.outgoingRules, false) {
|
|
||||||
t.Errorf("expected packet to be accepted")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = m.Reset(); err != nil {
|
|
||||||
t.Errorf("failed to reset Manager: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestRemovePacketHook tests the functionality of the RemovePacketHook method
|
|
||||||
func TestRemovePacketHook(t *testing.T) {
|
|
||||||
// creating mock iface
|
|
||||||
iface := &IFaceMock{
|
|
||||||
SetFilterFunc: func(iface.PacketFilter) error { return nil },
|
|
||||||
}
|
|
||||||
|
|
||||||
// creating manager instance
|
|
||||||
manager, err := Create(iface)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create Manager: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a UDP packet hook
|
|
||||||
hookFunc := func(data []byte) bool { return true }
|
|
||||||
hookID := manager.AddUDPPacketHook(false, net.IPv4(192, 168, 0, 1), 8080, hookFunc)
|
|
||||||
|
|
||||||
// Assert the hook is added by finding it in the manager's outgoing rules
|
|
||||||
found := false
|
|
||||||
for _, rule := range manager.outgoingRules {
|
|
||||||
if rule.id == hookID {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
t.Fatalf("The hook was not added properly.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now remove the packet hook
|
|
||||||
err = manager.RemovePacketHook(hookID)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to remove hook: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert the hook is removed by checking it in the manager's outgoing rules
|
|
||||||
for _, rule := range manager.outgoingRules {
|
|
||||||
if rule.id == hookID {
|
|
||||||
t.Fatalf("The hook was not removed properly.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUSPFilterCreatePerformance(t *testing.T) {
|
|
||||||
for _, testMax := range []int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} {
|
|
||||||
t.Run(fmt.Sprintf("Testing %d rules", testMax), func(t *testing.T) {
|
|
||||||
// just check on the local interface
|
|
||||||
ifaceMock := &IFaceMock{
|
|
||||||
SetFilterFunc: func(iface.PacketFilter) error { return nil },
|
|
||||||
}
|
|
||||||
manager, err := Create(ifaceMock)
|
|
||||||
require.NoError(t, err)
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := manager.Reset(); err != nil {
|
|
||||||
t.Errorf("clear the manager state: %v", err)
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}()
|
|
||||||
|
|
||||||
ip := net.ParseIP("10.20.0.100")
|
|
||||||
start := time.Now()
|
|
||||||
for i := 0; i < testMax; i++ {
|
|
||||||
port := &fw.Port{Values: []int{1000 + i}}
|
|
||||||
if i%2 == 0 {
|
|
||||||
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionOUT, fw.ActionAccept, "accept HTTP traffic")
|
|
||||||
} else {
|
|
||||||
_, err = manager.AddFiltering(ip, "tcp", nil, port, fw.RuleDirectionIN, fw.ActionAccept, "accept HTTP traffic")
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(t, err, "failed to add rule")
|
|
||||||
}
|
|
||||||
t.Logf("execution avg per rule: %s", time.Since(start)/time.Duration(testMax))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,413 +0,0 @@
|
|||||||
package acl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/firewall"
|
|
||||||
"github.com/netbirdio/netbird/client/ssh"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
mgmProto "github.com/netbirdio/netbird/management/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IFaceMapper defines subset methods of interface required for manager
|
|
||||||
type IFaceMapper interface {
|
|
||||||
Name() string
|
|
||||||
Address() iface.WGAddress
|
|
||||||
IsUserspaceBind() bool
|
|
||||||
SetFilter(iface.PacketFilter) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manager is a ACL rules manager
|
|
||||||
type Manager interface {
|
|
||||||
ApplyFiltering(networkMap *mgmProto.NetworkMap)
|
|
||||||
Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultManager uses firewall manager to handle
|
|
||||||
type DefaultManager struct {
|
|
||||||
manager firewall.Manager
|
|
||||||
rulesPairs map[string][]firewall.Rule
|
|
||||||
mutex sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
|
|
||||||
d.mutex.Lock()
|
|
||||||
defer d.mutex.Unlock()
|
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
defer func() {
|
|
||||||
total := 0
|
|
||||||
for _, pairs := range d.rulesPairs {
|
|
||||||
total += len(pairs)
|
|
||||||
}
|
|
||||||
log.Infof(
|
|
||||||
"ACL rules processed in: %v, total rules count: %d",
|
|
||||||
time.Since(start), total)
|
|
||||||
}()
|
|
||||||
|
|
||||||
if d.manager == nil {
|
|
||||||
log.Debug("firewall manager is not supported, skipping firewall rules")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rules, squashedProtocols := d.squashAcceptRules(networkMap)
|
|
||||||
|
|
||||||
enableSSH := (networkMap.PeerConfig != nil &&
|
|
||||||
networkMap.PeerConfig.SshConfig != nil &&
|
|
||||||
networkMap.PeerConfig.SshConfig.SshEnabled)
|
|
||||||
if _, ok := squashedProtocols[mgmProto.FirewallRule_ALL]; ok {
|
|
||||||
enableSSH = enableSSH && !ok
|
|
||||||
}
|
|
||||||
if _, ok := squashedProtocols[mgmProto.FirewallRule_TCP]; ok {
|
|
||||||
enableSSH = enableSSH && !ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// if TCP protocol rules not squashed and SSH enabled
|
|
||||||
// we add default firewall rule which accepts connection to any peer
|
|
||||||
// in the network by SSH (TCP 22 port).
|
|
||||||
if enableSSH {
|
|
||||||
rules = append(rules, &mgmProto.FirewallRule{
|
|
||||||
PeerIP: "0.0.0.0",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_TCP,
|
|
||||||
Port: strconv.Itoa(ssh.DefaultSSHPort),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we got empty rules list but management not set networkMap.FirewallRulesIsEmpty flag
|
|
||||||
// we have old version of management without rules handling, we should allow all traffic
|
|
||||||
if len(networkMap.FirewallRules) == 0 && !networkMap.FirewallRulesIsEmpty {
|
|
||||||
log.Warn("this peer is connected to a NetBird Management service with an older version. Allowing all traffic from connected peers")
|
|
||||||
rules = append(rules,
|
|
||||||
&mgmProto.FirewallRule{
|
|
||||||
PeerIP: "0.0.0.0",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
&mgmProto.FirewallRule{
|
|
||||||
PeerIP: "0.0.0.0",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
applyFailed := false
|
|
||||||
newRulePairs := make(map[string][]firewall.Rule)
|
|
||||||
for _, r := range rules {
|
|
||||||
pairID, rulePair, err := d.protoRuleToFirewallRule(r)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed to apply firewall rule: %+v, %v", r, err)
|
|
||||||
applyFailed = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
newRulePairs[pairID] = rulePair
|
|
||||||
}
|
|
||||||
if applyFailed {
|
|
||||||
log.Error("failed to apply firewall rules, rollback ACL to previous state")
|
|
||||||
for _, rules := range newRulePairs {
|
|
||||||
for _, rule := range rules {
|
|
||||||
if err := d.manager.DeleteRule(rule); err != nil {
|
|
||||||
log.Errorf("failed to delete new firewall rule (id: %v) during rollback: %v", rule.GetRuleID(), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for pairID, rules := range d.rulesPairs {
|
|
||||||
if _, ok := newRulePairs[pairID]; !ok {
|
|
||||||
for _, rule := range rules {
|
|
||||||
if err := d.manager.DeleteRule(rule); err != nil {
|
|
||||||
log.Errorf("failed to delete firewall rule: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete(d.rulesPairs, pairID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d.rulesPairs = newRulePairs
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop ACL controller and clear firewall state
|
|
||||||
func (d *DefaultManager) Stop() {
|
|
||||||
d.mutex.Lock()
|
|
||||||
defer d.mutex.Unlock()
|
|
||||||
|
|
||||||
if err := d.manager.Reset(); err != nil {
|
|
||||||
log.WithError(err).Error("reset firewall state")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultManager) protoRuleToFirewallRule(r *mgmProto.FirewallRule) (string, []firewall.Rule, error) {
|
|
||||||
ip := net.ParseIP(r.PeerIP)
|
|
||||||
if ip == nil {
|
|
||||||
return "", nil, fmt.Errorf("invalid IP address, skipping firewall rule")
|
|
||||||
}
|
|
||||||
|
|
||||||
protocol := convertToFirewallProtocol(r.Protocol)
|
|
||||||
if protocol == firewall.ProtocolUnknown {
|
|
||||||
return "", nil, fmt.Errorf("invalid protocol type: %d, skipping firewall rule", r.Protocol)
|
|
||||||
}
|
|
||||||
|
|
||||||
action := convertFirewallAction(r.Action)
|
|
||||||
if action == firewall.ActionUnknown {
|
|
||||||
return "", nil, fmt.Errorf("invalid action type: %d, skipping firewall rule", r.Action)
|
|
||||||
}
|
|
||||||
|
|
||||||
var port *firewall.Port
|
|
||||||
if r.Port != "" {
|
|
||||||
value, err := strconv.Atoi(r.Port)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, fmt.Errorf("invalid port, skipping firewall rule")
|
|
||||||
}
|
|
||||||
port = &firewall.Port{
|
|
||||||
Values: []int{value},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleID := d.getRuleID(ip, protocol, int(r.Direction), port, action, "")
|
|
||||||
if rulesPair, ok := d.rulesPairs[ruleID]; ok {
|
|
||||||
return ruleID, rulesPair, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var rules []firewall.Rule
|
|
||||||
var err error
|
|
||||||
switch r.Direction {
|
|
||||||
case mgmProto.FirewallRule_IN:
|
|
||||||
rules, err = d.addInRules(ip, protocol, port, action, "")
|
|
||||||
case mgmProto.FirewallRule_OUT:
|
|
||||||
rules, err = d.addOutRules(ip, protocol, port, action, "")
|
|
||||||
default:
|
|
||||||
return "", nil, fmt.Errorf("invalid direction, skipping firewall rule")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
d.rulesPairs[ruleID] = rules
|
|
||||||
return ruleID, rules, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultManager) addInRules(ip net.IP, protocol firewall.Protocol, port *firewall.Port, action firewall.Action, comment string) ([]firewall.Rule, error) {
|
|
||||||
var rules []firewall.Rule
|
|
||||||
rule, err := d.manager.AddFiltering(ip, protocol, nil, port, firewall.RuleDirectionIN, action, comment)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
|
|
||||||
}
|
|
||||||
rules = append(rules, rule)
|
|
||||||
|
|
||||||
if shouldSkipInvertedRule(protocol, port) {
|
|
||||||
return rules, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rule, err = d.manager.AddFiltering(ip, protocol, port, nil, firewall.RuleDirectionOUT, action, comment)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(rules, rule), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DefaultManager) addOutRules(ip net.IP, protocol firewall.Protocol, port *firewall.Port, action firewall.Action, comment string) ([]firewall.Rule, error) {
|
|
||||||
var rules []firewall.Rule
|
|
||||||
rule, err := d.manager.AddFiltering(ip, protocol, nil, port, firewall.RuleDirectionOUT, action, comment)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
|
|
||||||
}
|
|
||||||
rules = append(rules, rule)
|
|
||||||
|
|
||||||
if shouldSkipInvertedRule(protocol, port) {
|
|
||||||
return rules, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rule, err = d.manager.AddFiltering(ip, protocol, port, nil, firewall.RuleDirectionIN, action, comment)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(rules, rule), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getRuleID() returns unique ID for the rule based on its parameters.
|
|
||||||
func (d *DefaultManager) getRuleID(
|
|
||||||
ip net.IP,
|
|
||||||
proto firewall.Protocol,
|
|
||||||
direction int,
|
|
||||||
port *firewall.Port,
|
|
||||||
action firewall.Action,
|
|
||||||
comment string,
|
|
||||||
) string {
|
|
||||||
idStr := ip.String() + string(proto) + strconv.Itoa(direction) + strconv.Itoa(int(action)) + comment
|
|
||||||
if port != nil {
|
|
||||||
idStr += port.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return hex.EncodeToString(md5.New().Sum([]byte(idStr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// squashAcceptRules does complex logic to convert many rules which allows connection by traffic type
|
|
||||||
// to all peers in the network map to one rule which just accepts that type of the traffic.
|
|
||||||
//
|
|
||||||
// NOTE: It will not squash two rules for same protocol if one covers all peers in the network,
|
|
||||||
// but other has port definitions or has drop policy.
|
|
||||||
func (d *DefaultManager) squashAcceptRules(
|
|
||||||
networkMap *mgmProto.NetworkMap,
|
|
||||||
) ([]*mgmProto.FirewallRule, map[mgmProto.FirewallRuleProtocol]struct{}) {
|
|
||||||
totalIPs := 0
|
|
||||||
for _, p := range append(networkMap.RemotePeers, networkMap.OfflinePeers...) {
|
|
||||||
for range p.AllowedIps {
|
|
||||||
totalIPs++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type protoMatch map[mgmProto.FirewallRuleProtocol]map[string]int
|
|
||||||
|
|
||||||
in := protoMatch{}
|
|
||||||
out := protoMatch{}
|
|
||||||
|
|
||||||
// this function we use to do calculation, can we squash the rules by protocol or not.
|
|
||||||
// We summ amount of Peers IP for given protocol we found in original rules list.
|
|
||||||
// But we zeroed the IP's for protocol if:
|
|
||||||
// 1. Any of the rule has DROP action type.
|
|
||||||
// 2. Any of rule contains Port.
|
|
||||||
//
|
|
||||||
// We zeroed this to notify squash function that this protocol can't be squashed.
|
|
||||||
addRuleToCalculationMap := func(i int, r *mgmProto.FirewallRule, protocols protoMatch) {
|
|
||||||
drop := r.Action == mgmProto.FirewallRule_DROP || r.Port != ""
|
|
||||||
if drop {
|
|
||||||
protocols[r.Protocol] = map[string]int{}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, ok := protocols[r.Protocol]; !ok {
|
|
||||||
protocols[r.Protocol] = map[string]int{}
|
|
||||||
}
|
|
||||||
match := protocols[r.Protocol]
|
|
||||||
|
|
||||||
if _, ok := match[r.PeerIP]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
match[r.PeerIP] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, r := range networkMap.FirewallRules {
|
|
||||||
// calculate squash for different directions
|
|
||||||
if r.Direction == mgmProto.FirewallRule_IN {
|
|
||||||
addRuleToCalculationMap(i, r, in)
|
|
||||||
} else {
|
|
||||||
addRuleToCalculationMap(i, r, out)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// order of squashing by protocol is important
|
|
||||||
// only for ther first element ALL, it must be done first
|
|
||||||
protocolOrders := []mgmProto.FirewallRuleProtocol{
|
|
||||||
mgmProto.FirewallRule_ALL,
|
|
||||||
mgmProto.FirewallRule_ICMP,
|
|
||||||
mgmProto.FirewallRule_TCP,
|
|
||||||
mgmProto.FirewallRule_UDP,
|
|
||||||
}
|
|
||||||
|
|
||||||
// trace which type of protocols was squashed
|
|
||||||
squashedRules := []*mgmProto.FirewallRule{}
|
|
||||||
squashedProtocols := map[mgmProto.FirewallRuleProtocol]struct{}{}
|
|
||||||
squash := func(matches protoMatch, direction mgmProto.FirewallRuleDirection) {
|
|
||||||
for _, protocol := range protocolOrders {
|
|
||||||
if ipset, ok := matches[protocol]; !ok || len(ipset) != totalIPs || len(ipset) < 2 {
|
|
||||||
// don't squash if :
|
|
||||||
// 1. Rules not cover all peers in the network
|
|
||||||
// 2. Rules cover only one peer in the network.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// add special rule 0.0.0.0 which allows all IP's in our firewall implementations
|
|
||||||
squashedRules = append(squashedRules, &mgmProto.FirewallRule{
|
|
||||||
PeerIP: "0.0.0.0",
|
|
||||||
Direction: direction,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: protocol,
|
|
||||||
})
|
|
||||||
squashedProtocols[protocol] = struct{}{}
|
|
||||||
|
|
||||||
if protocol == mgmProto.FirewallRule_ALL {
|
|
||||||
// if we have ALL traffic type squashed rule
|
|
||||||
// it allows all other type of traffic, so we can stop processing
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
squash(in, mgmProto.FirewallRule_IN)
|
|
||||||
squash(out, mgmProto.FirewallRule_OUT)
|
|
||||||
|
|
||||||
// if all protocol was squashed everything is allow and we can ignore all other rules
|
|
||||||
if _, ok := squashedProtocols[mgmProto.FirewallRule_ALL]; ok {
|
|
||||||
return squashedRules, squashedProtocols
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(squashedRules) == 0 {
|
|
||||||
return networkMap.FirewallRules, squashedProtocols
|
|
||||||
}
|
|
||||||
|
|
||||||
var rules []*mgmProto.FirewallRule
|
|
||||||
// filter out rules which was squashed from final list
|
|
||||||
// if we also have other not squashed rules.
|
|
||||||
for i, r := range networkMap.FirewallRules {
|
|
||||||
if _, ok := squashedProtocols[r.Protocol]; ok {
|
|
||||||
if m, ok := in[r.Protocol]; ok && m[r.PeerIP] == i {
|
|
||||||
continue
|
|
||||||
} else if m, ok := out[r.Protocol]; ok && m[r.PeerIP] == i {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rules = append(rules, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(rules, squashedRules...), squashedProtocols
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertToFirewallProtocol(protocol mgmProto.FirewallRuleProtocol) firewall.Protocol {
|
|
||||||
switch protocol {
|
|
||||||
case mgmProto.FirewallRule_TCP:
|
|
||||||
return firewall.ProtocolTCP
|
|
||||||
case mgmProto.FirewallRule_UDP:
|
|
||||||
return firewall.ProtocolUDP
|
|
||||||
case mgmProto.FirewallRule_ICMP:
|
|
||||||
return firewall.ProtocolICMP
|
|
||||||
case mgmProto.FirewallRule_ALL:
|
|
||||||
return firewall.ProtocolALL
|
|
||||||
default:
|
|
||||||
return firewall.ProtocolUnknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldSkipInvertedRule(protocol firewall.Protocol, port *firewall.Port) bool {
|
|
||||||
return protocol == firewall.ProtocolALL || protocol == firewall.ProtocolICMP || port == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertFirewallAction(action mgmProto.FirewallRuleAction) firewall.Action {
|
|
||||||
switch action {
|
|
||||||
case mgmProto.FirewallRule_ACCEPT:
|
|
||||||
return firewall.ActionAccept
|
|
||||||
case mgmProto.FirewallRule_DROP:
|
|
||||||
return firewall.ActionDrop
|
|
||||||
default:
|
|
||||||
return firewall.ActionUnknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//go:build !linux
|
|
||||||
|
|
||||||
package acl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/firewall"
|
|
||||||
"github.com/netbirdio/netbird/client/firewall/uspfilter"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create creates a firewall manager instance
|
|
||||||
func Create(iface IFaceMapper) (manager *DefaultManager, err error) {
|
|
||||||
if iface.IsUserspaceBind() {
|
|
||||||
// use userspace packet filtering firewall
|
|
||||||
fm, err := uspfilter.Create(iface)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &DefaultManager{
|
|
||||||
manager: fm,
|
|
||||||
rulesPairs: make(map[string][]firewall.Rule),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("not implemented for this OS: %s", runtime.GOOS)
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package acl
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/firewall"
|
|
||||||
"github.com/netbirdio/netbird/client/firewall/iptables"
|
|
||||||
"github.com/netbirdio/netbird/client/firewall/nftables"
|
|
||||||
"github.com/netbirdio/netbird/client/firewall/uspfilter"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create creates a firewall manager instance for the Linux
|
|
||||||
func Create(iface IFaceMapper) (manager *DefaultManager, err error) {
|
|
||||||
var fm firewall.Manager
|
|
||||||
if iface.IsUserspaceBind() {
|
|
||||||
// use userspace packet filtering firewall
|
|
||||||
if fm, err = uspfilter.Create(iface); err != nil {
|
|
||||||
log.Debugf("failed to create userspace filtering firewall: %s", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if fm, err = nftables.Create(iface); err != nil {
|
|
||||||
log.Debugf("failed to create nftables manager: %s", err)
|
|
||||||
// fallback to iptables
|
|
||||||
if fm, err = iptables.Create(iface); err != nil {
|
|
||||||
log.Errorf("failed to create iptables manager: %s", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &DefaultManager{
|
|
||||||
manager: fm,
|
|
||||||
rulesPairs: make(map[string][]firewall.Rule),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
@@ -1,333 +0,0 @@
|
|||||||
package acl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/acl/mocks"
|
|
||||||
mgmProto "github.com/netbirdio/netbird/management/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDefaultManager(t *testing.T) {
|
|
||||||
networkMap := &mgmProto.NetworkMap{
|
|
||||||
FirewallRules: []*mgmProto.FirewallRule{
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.1",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_TCP,
|
|
||||||
Port: "80",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.2",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_DROP,
|
|
||||||
Protocol: mgmProto.FirewallRule_UDP,
|
|
||||||
Port: "53",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
iface := mocks.NewMockIFaceMapper(ctrl)
|
|
||||||
iface.EXPECT().IsUserspaceBind().Return(true)
|
|
||||||
// iface.EXPECT().Name().Return("lo")
|
|
||||||
iface.EXPECT().SetFilter(gomock.Any())
|
|
||||||
|
|
||||||
// we receive one rule from the management so for testing purposes ignore it
|
|
||||||
acl, err := Create(iface)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("create ACL manager: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer acl.Stop()
|
|
||||||
|
|
||||||
t.Run("apply firewall rules", func(t *testing.T) {
|
|
||||||
acl.ApplyFiltering(networkMap)
|
|
||||||
|
|
||||||
if len(acl.rulesPairs) != 2 {
|
|
||||||
t.Errorf("firewall rules not applied: %v", acl.rulesPairs)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("add extra rules", func(t *testing.T) {
|
|
||||||
existedPairs := map[string]struct{}{}
|
|
||||||
for id := range acl.rulesPairs {
|
|
||||||
existedPairs[id] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove first rule
|
|
||||||
networkMap.FirewallRules = networkMap.FirewallRules[1:]
|
|
||||||
networkMap.FirewallRules = append(
|
|
||||||
networkMap.FirewallRules,
|
|
||||||
&mgmProto.FirewallRule{
|
|
||||||
PeerIP: "10.93.0.3",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_DROP,
|
|
||||||
Protocol: mgmProto.FirewallRule_ICMP,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
acl.ApplyFiltering(networkMap)
|
|
||||||
|
|
||||||
// we should have one old and one new rule in the existed rules
|
|
||||||
if len(acl.rulesPairs) != 2 {
|
|
||||||
t.Errorf("firewall rules not applied")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that old rule was removed
|
|
||||||
previousCount := 0
|
|
||||||
for id := range acl.rulesPairs {
|
|
||||||
if _, ok := existedPairs[id]; ok {
|
|
||||||
previousCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if previousCount != 1 {
|
|
||||||
t.Errorf("old rule was not removed")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("handle default rules", func(t *testing.T) {
|
|
||||||
networkMap.FirewallRules = networkMap.FirewallRules[:0]
|
|
||||||
|
|
||||||
networkMap.FirewallRulesIsEmpty = true
|
|
||||||
if acl.ApplyFiltering(networkMap); len(acl.rulesPairs) != 0 {
|
|
||||||
t.Errorf("rules should be empty if FirewallRulesIsEmpty is set, got: %v", len(acl.rulesPairs))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
networkMap.FirewallRulesIsEmpty = false
|
|
||||||
acl.ApplyFiltering(networkMap)
|
|
||||||
if len(acl.rulesPairs) != 2 {
|
|
||||||
t.Errorf("rules should contain 2 rules if FirewallRulesIsEmpty is not set, got: %v", len(acl.rulesPairs))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultManagerSquashRules(t *testing.T) {
|
|
||||||
networkMap := &mgmProto.NetworkMap{
|
|
||||||
RemotePeers: []*mgmProto.RemotePeerConfig{
|
|
||||||
{AllowedIps: []string{"10.93.0.1"}},
|
|
||||||
{AllowedIps: []string{"10.93.0.2"}},
|
|
||||||
{AllowedIps: []string{"10.93.0.3"}},
|
|
||||||
{AllowedIps: []string{"10.93.0.4"}},
|
|
||||||
},
|
|
||||||
FirewallRules: []*mgmProto.FirewallRule{
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.1",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.2",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.3",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.4",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.1",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.2",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.3",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.4",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
manager := &DefaultManager{}
|
|
||||||
rules, _ := manager.squashAcceptRules(networkMap)
|
|
||||||
if len(rules) != 2 {
|
|
||||||
t.Errorf("rules should contain 2, got: %v", rules)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r := rules[0]
|
|
||||||
if r.PeerIP != "0.0.0.0" {
|
|
||||||
t.Errorf("IP should be 0.0.0.0, got: %v", r.PeerIP)
|
|
||||||
return
|
|
||||||
} else if r.Direction != mgmProto.FirewallRule_IN {
|
|
||||||
t.Errorf("direction should be IN, got: %v", r.Direction)
|
|
||||||
return
|
|
||||||
} else if r.Protocol != mgmProto.FirewallRule_ALL {
|
|
||||||
t.Errorf("protocol should be ALL, got: %v", r.Protocol)
|
|
||||||
return
|
|
||||||
} else if r.Action != mgmProto.FirewallRule_ACCEPT {
|
|
||||||
t.Errorf("action should be ACCEPT, got: %v", r.Action)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
r = rules[1]
|
|
||||||
if r.PeerIP != "0.0.0.0" {
|
|
||||||
t.Errorf("IP should be 0.0.0.0, got: %v", r.PeerIP)
|
|
||||||
return
|
|
||||||
} else if r.Direction != mgmProto.FirewallRule_OUT {
|
|
||||||
t.Errorf("direction should be OUT, got: %v", r.Direction)
|
|
||||||
return
|
|
||||||
} else if r.Protocol != mgmProto.FirewallRule_ALL {
|
|
||||||
t.Errorf("protocol should be ALL, got: %v", r.Protocol)
|
|
||||||
return
|
|
||||||
} else if r.Action != mgmProto.FirewallRule_ACCEPT {
|
|
||||||
t.Errorf("action should be ACCEPT, got: %v", r.Action)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultManagerSquashRulesNoAffect(t *testing.T) {
|
|
||||||
networkMap := &mgmProto.NetworkMap{
|
|
||||||
RemotePeers: []*mgmProto.RemotePeerConfig{
|
|
||||||
{AllowedIps: []string{"10.93.0.1"}},
|
|
||||||
{AllowedIps: []string{"10.93.0.2"}},
|
|
||||||
{AllowedIps: []string{"10.93.0.3"}},
|
|
||||||
{AllowedIps: []string{"10.93.0.4"}},
|
|
||||||
},
|
|
||||||
FirewallRules: []*mgmProto.FirewallRule{
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.1",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.2",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.3",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.4",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_TCP,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.1",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.2",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.3",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_ALL,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.4",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_UDP,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
manager := &DefaultManager{}
|
|
||||||
if rules, _ := manager.squashAcceptRules(networkMap); len(rules) != len(networkMap.FirewallRules) {
|
|
||||||
t.Errorf("we should got same amount of rules as intput, got %v", len(rules))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultManagerEnableSSHRules(t *testing.T) {
|
|
||||||
networkMap := &mgmProto.NetworkMap{
|
|
||||||
PeerConfig: &mgmProto.PeerConfig{
|
|
||||||
SshConfig: &mgmProto.SSHConfig{
|
|
||||||
SshEnabled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RemotePeers: []*mgmProto.RemotePeerConfig{
|
|
||||||
{AllowedIps: []string{"10.93.0.1"}},
|
|
||||||
{AllowedIps: []string{"10.93.0.2"}},
|
|
||||||
{AllowedIps: []string{"10.93.0.3"}},
|
|
||||||
},
|
|
||||||
FirewallRules: []*mgmProto.FirewallRule{
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.1",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_TCP,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.2",
|
|
||||||
Direction: mgmProto.FirewallRule_IN,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_TCP,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PeerIP: "10.93.0.3",
|
|
||||||
Direction: mgmProto.FirewallRule_OUT,
|
|
||||||
Action: mgmProto.FirewallRule_ACCEPT,
|
|
||||||
Protocol: mgmProto.FirewallRule_UDP,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
iface := mocks.NewMockIFaceMapper(ctrl)
|
|
||||||
iface.EXPECT().IsUserspaceBind().Return(true)
|
|
||||||
// iface.EXPECT().Name().Return("lo")
|
|
||||||
iface.EXPECT().SetFilter(gomock.Any())
|
|
||||||
|
|
||||||
// we receive one rule from the management so for testing purposes ignore it
|
|
||||||
acl, err := Create(iface)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("create ACL manager: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer acl.Stop()
|
|
||||||
|
|
||||||
acl.ApplyFiltering(networkMap)
|
|
||||||
|
|
||||||
if len(acl.rulesPairs) != 4 {
|
|
||||||
t.Errorf("expect 4 rules (last must be SSH), got: %d", len(acl.rulesPairs))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
## Mocks
|
|
||||||
|
|
||||||
To generate (or refresh) mocks from acl package please install [mockgen](https://github.com/golang/mock).
|
|
||||||
Run this command from the `./client/internal/acl` folder to update iface mapper interface mock:
|
|
||||||
```bash
|
|
||||||
mockgen -destination mocks/iface_mapper.go -package mocks . IFaceMapper
|
|
||||||
```
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: github.com/netbirdio/netbird/client/internal/acl (interfaces: IFaceMapper)
|
|
||||||
|
|
||||||
// Package mocks is a generated GoMock package.
|
|
||||||
package mocks
|
|
||||||
|
|
||||||
import (
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
iface "github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockIFaceMapper is a mock of IFaceMapper interface.
|
|
||||||
type MockIFaceMapper struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockIFaceMapperMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockIFaceMapperMockRecorder is the mock recorder for MockIFaceMapper.
|
|
||||||
type MockIFaceMapperMockRecorder struct {
|
|
||||||
mock *MockIFaceMapper
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockIFaceMapper creates a new mock instance.
|
|
||||||
func NewMockIFaceMapper(ctrl *gomock.Controller) *MockIFaceMapper {
|
|
||||||
mock := &MockIFaceMapper{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockIFaceMapperMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockIFaceMapper) EXPECT() *MockIFaceMapperMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Address mocks base method.
|
|
||||||
func (m *MockIFaceMapper) Address() iface.WGAddress {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Address")
|
|
||||||
ret0, _ := ret[0].(iface.WGAddress)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Address indicates an expected call of Address.
|
|
||||||
func (mr *MockIFaceMapperMockRecorder) Address() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Address", reflect.TypeOf((*MockIFaceMapper)(nil).Address))
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsUserspaceBind mocks base method.
|
|
||||||
func (m *MockIFaceMapper) IsUserspaceBind() bool {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "IsUserspaceBind")
|
|
||||||
ret0, _ := ret[0].(bool)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsUserspaceBind indicates an expected call of IsUserspaceBind.
|
|
||||||
func (mr *MockIFaceMapperMockRecorder) IsUserspaceBind() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsUserspaceBind", reflect.TypeOf((*MockIFaceMapper)(nil).IsUserspaceBind))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name mocks base method.
|
|
||||||
func (m *MockIFaceMapper) Name() string {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Name")
|
|
||||||
ret0, _ := ret[0].(string)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name indicates an expected call of Name.
|
|
||||||
func (mr *MockIFaceMapperMockRecorder) Name() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockIFaceMapper)(nil).Name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFilter mocks base method.
|
|
||||||
func (m *MockIFaceMapper) SetFilter(arg0 iface.PacketFilter) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "SetFilter", arg0)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFilter indicates an expected call of SetFilter.
|
|
||||||
func (mr *MockIFaceMapperMockRecorder) SetFilter(arg0 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFilter", reflect.TypeOf((*MockIFaceMapper)(nil).SetFilter), arg0)
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
gstatus "google.golang.org/grpc/status"
|
gstatus "google.golang.org/grpc/status"
|
||||||
|
|
||||||
"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"
|
||||||
@@ -24,7 +23,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// RunClient with main logic.
|
// RunClient with main logic.
|
||||||
func RunClient(ctx context.Context, config *Config, statusRecorder *peer.Status, tunAdapter iface.TunAdapter, iFaceDiscover stdnet.ExternalIFaceDiscover, routeListener routemanager.RouteListener) error {
|
func RunClient(ctx context.Context, config *Config, statusRecorder *peer.Status, tunAdapter iface.TunAdapter, iFaceDiscover stdnet.ExternalIFaceDiscover) error {
|
||||||
backOff := &backoff.ExponentialBackOff{
|
backOff := &backoff.ExponentialBackOff{
|
||||||
InitialInterval: time.Second,
|
InitialInterval: time.Second,
|
||||||
RandomizationFactor: 1,
|
RandomizationFactor: 1,
|
||||||
@@ -151,11 +150,10 @@ func RunClient(ctx context.Context, config *Config, statusRecorder *peer.Status,
|
|||||||
return wrapErr(err)
|
return wrapErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// in case of non Android os these variables will be nil
|
md, err := newMobileDependency(tunAdapter, iFaceDiscover, mgmClient)
|
||||||
md := MobileDependency{
|
if err != nil {
|
||||||
TunAdapter: tunAdapter,
|
log.Error(err)
|
||||||
IFaceDiscover: iFaceDiscover,
|
return wrapErr(err)
|
||||||
RouteListener: routeListener,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
engine := NewEngine(engineCtx, cancel, signalClient, mgmClient, engineConfig, md, statusRecorder)
|
engine := NewEngine(engineCtx, cancel, signalClient, mgmClient, engineConfig, md, statusRecorder)
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//go:build !android
|
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//go:build !android
|
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -34,10 +32,6 @@ func newFileConfigurator() (hostManager, error) {
|
|||||||
return &fileConfigurator{}, nil
|
return &fileConfigurator{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fileConfigurator) supportCustomPort() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
func (f *fileConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||||
backupFileExist := false
|
backupFileExist := false
|
||||||
_, err := os.Stat(fileDefaultResolvConfBackupLocation)
|
_, err := os.Stat(fileDefaultResolvConfBackupLocation)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
type hostManager interface {
|
type hostManager interface {
|
||||||
applyDNSConfig(config hostDNSConfig) error
|
applyDNSConfig(config hostDNSConfig) error
|
||||||
restoreHostDNS() error
|
restoreHostDNS() error
|
||||||
supportCustomPort() bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type hostDNSConfig struct {
|
type hostDNSConfig struct {
|
||||||
@@ -27,9 +26,8 @@ type domainConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type mockHostConfigurator struct {
|
type mockHostConfigurator struct {
|
||||||
applyDNSConfigFunc func(config hostDNSConfig) error
|
applyDNSConfigFunc func(config hostDNSConfig) error
|
||||||
restoreHostDNSFunc func() error
|
restoreHostDNSFunc func() error
|
||||||
supportCustomPortFunc func() bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockHostConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
func (m *mockHostConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||||
@@ -46,18 +44,10 @@ func (m *mockHostConfigurator) restoreHostDNS() error {
|
|||||||
return fmt.Errorf("method restoreHostDNS is not implemented")
|
return fmt.Errorf("method restoreHostDNS is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockHostConfigurator) supportCustomPort() bool {
|
|
||||||
if m.supportCustomPortFunc != nil {
|
|
||||||
return m.supportCustomPortFunc()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func newNoopHostMocker() hostManager {
|
func newNoopHostMocker() hostManager {
|
||||||
return &mockHostConfigurator{
|
return &mockHostConfigurator{
|
||||||
applyDNSConfigFunc: func(config hostDNSConfig) error { return nil },
|
applyDNSConfigFunc: func(config hostDNSConfig) error { return nil },
|
||||||
restoreHostDNSFunc: func() error { return nil },
|
restoreHostDNSFunc: func() error { return nil },
|
||||||
supportCustomPortFunc: func() bool { return true },
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
|
||||||
|
|
||||||
type androidHostManager struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func newHostManager(wgInterface *iface.WGIface) (hostManager, error) {
|
|
||||||
return &androidHostManager{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a androidHostManager) applyDNSConfig(config hostDNSConfig) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a androidHostManager) restoreHostDNS() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a androidHostManager) supportCustomPort() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -8,9 +8,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -40,10 +39,6 @@ func newHostManager(_ *iface.WGIface) (hostManager, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *systemConfigurator) supportCustomPort() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *systemConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
func (s *systemConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//go:build !android
|
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -43,10 +42,6 @@ func newHostManager(wgInterface *iface.WGIface) (hostManager, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *registryConfigurator) supportCustomPort() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *registryConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
func (r *registryConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||||
var err error
|
var err error
|
||||||
if config.routeAll {
|
if config.routeAll {
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type registrationMap map[string]struct{}
|
type registrationMap map[string]struct{}
|
||||||
@@ -17,12 +15,9 @@ type localResolver struct {
|
|||||||
records sync.Map
|
records sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *localResolver) stop() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeDNS handles a DNS request
|
// ServeDNS handles a DNS request
|
||||||
func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||||
log.Tracef("received question: %#v", r.Question[0])
|
log.Tracef("received question: %#v\n", r.Question[0])
|
||||||
replyMessage := &dns.Msg{}
|
replyMessage := &dns.Msg{}
|
||||||
replyMessage.SetReply(r)
|
replyMessage.SetReply(r)
|
||||||
replyMessage.RecursionAvailable = true
|
replyMessage.RecursionAvailable = true
|
||||||
|
|||||||
@@ -7,17 +7,16 @@ import (
|
|||||||
|
|
||||||
// MockServer is the mock instance of a dns server
|
// MockServer is the mock instance of a dns server
|
||||||
type MockServer struct {
|
type MockServer struct {
|
||||||
InitializeFunc func() error
|
StartFunc func()
|
||||||
StopFunc func()
|
StopFunc func()
|
||||||
UpdateDNSServerFunc func(serial uint64, update nbdns.Config) error
|
UpdateDNSServerFunc func(serial uint64, update nbdns.Config) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize mock implementation of Initialize from Server interface
|
// Start mock implementation of Start from Server interface
|
||||||
func (m *MockServer) Initialize() error {
|
func (m *MockServer) Start() {
|
||||||
if m.InitializeFunc != nil {
|
if m.StartFunc != nil {
|
||||||
return m.InitializeFunc()
|
m.StartFunc()
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop mock implementation of Stop from Server interface
|
// Stop mock implementation of Stop from Server interface
|
||||||
@@ -27,10 +26,6 @@ func (m *MockServer) Stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockServer) DnsIP() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDNSServer mock implementation of UpdateDNSServer from Server interface
|
// UpdateDNSServer mock implementation of UpdateDNSServer from Server interface
|
||||||
func (m *MockServer) UpdateDNSServer(serial uint64, update nbdns.Config) error {
|
func (m *MockServer) UpdateDNSServer(serial uint64, update nbdns.Config) error {
|
||||||
if m.UpdateDNSServerFunc != nil {
|
if m.UpdateDNSServerFunc != nil {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//go:build !android
|
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -13,9 +11,8 @@ import (
|
|||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
"github.com/hashicorp/go-version"
|
"github.com/hashicorp/go-version"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -91,10 +88,6 @@ func newNetworkManagerDbusConfigurator(wgInterface *iface.WGIface) (hostManager,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *networkManagerDbusConfigurator) supportCustomPort() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *networkManagerDbusConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
func (n *networkManagerDbusConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||||
connSettings, configVersion, err := n.getAppliedConnectionSettings()
|
connSettings, configVersion, err := n.getAppliedConnectionSettings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//go:build !android
|
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -7,9 +5,8 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const resolvconfCommand = "resolvconf"
|
const resolvconfCommand = "resolvconf"
|
||||||
@@ -24,10 +21,6 @@ func newResolvConfConfigurator(wgInterface *iface.WGIface) (hostManager, error)
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *resolvconf) supportCustomPort() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *resolvconf) applyDNSConfig(config hostDNSConfig) error {
|
func (r *resolvconf) applyDNSConfig(config hostDNSConfig) error {
|
||||||
var err error
|
var err error
|
||||||
if !config.routeAll {
|
if !config.routeAll {
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
"github.com/google/gopacket/layers"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
|
||||||
)
|
|
||||||
|
|
||||||
type responseWriter struct {
|
|
||||||
local net.Addr
|
|
||||||
remote net.Addr
|
|
||||||
packet gopacket.Packet
|
|
||||||
device tun.Device
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalAddr returns the net.Addr of the server
|
|
||||||
func (r *responseWriter) LocalAddr() net.Addr {
|
|
||||||
return r.local
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoteAddr returns the net.Addr of the client that sent the current request.
|
|
||||||
func (r *responseWriter) RemoteAddr() net.Addr {
|
|
||||||
return r.remote
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteMsg writes a reply back to the client.
|
|
||||||
func (r *responseWriter) WriteMsg(msg *dns.Msg) error {
|
|
||||||
buff, err := msg.Pack()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = r.Write(buff)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes a raw buffer back to the client.
|
|
||||||
func (r *responseWriter) Write(data []byte) (int, error) {
|
|
||||||
var ip gopacket.SerializableLayer
|
|
||||||
|
|
||||||
// Get the UDP layer
|
|
||||||
udpLayer := r.packet.Layer(layers.LayerTypeUDP)
|
|
||||||
udp := udpLayer.(*layers.UDP)
|
|
||||||
|
|
||||||
// Swap the source and destination addresses for the response
|
|
||||||
udp.SrcPort, udp.DstPort = udp.DstPort, udp.SrcPort
|
|
||||||
|
|
||||||
// Check if it's an IPv4 packet
|
|
||||||
if ipv4Layer := r.packet.Layer(layers.LayerTypeIPv4); ipv4Layer != nil {
|
|
||||||
ipv4 := ipv4Layer.(*layers.IPv4)
|
|
||||||
ipv4.SrcIP, ipv4.DstIP = ipv4.DstIP, ipv4.SrcIP
|
|
||||||
ip = ipv4
|
|
||||||
} else if ipv6Layer := r.packet.Layer(layers.LayerTypeIPv6); ipv6Layer != nil {
|
|
||||||
ipv6 := ipv6Layer.(*layers.IPv6)
|
|
||||||
ipv6.SrcIP, ipv6.DstIP = ipv6.DstIP, ipv6.SrcIP
|
|
||||||
ip = ipv6
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := udp.SetNetworkLayerForChecksum(ip.(gopacket.NetworkLayer)); err != nil {
|
|
||||||
return 0, fmt.Errorf("failed to set network layer for checksum: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize the packet
|
|
||||||
buffer := gopacket.NewSerializeBuffer()
|
|
||||||
options := gopacket.SerializeOptions{
|
|
||||||
ComputeChecksums: true,
|
|
||||||
FixLengths: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
payload := gopacket.Payload(data)
|
|
||||||
err := gopacket.SerializeLayers(buffer, options, ip, udp, payload)
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("failed to serialize packet: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
send := buffer.Bytes()
|
|
||||||
sendBuffer := make([]byte, 40, len(send)+40)
|
|
||||||
sendBuffer = append(sendBuffer, send...)
|
|
||||||
|
|
||||||
return r.device.Write([][]byte{sendBuffer}, 40)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the connection.
|
|
||||||
func (r *responseWriter) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TsigStatus returns the status of the Tsig.
|
|
||||||
func (r *responseWriter) TsigStatus() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TsigTimersOnly sets the tsig timers only boolean.
|
|
||||||
func (r *responseWriter) TsigTimersOnly(bool) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hijack lets the caller take over the connection.
|
|
||||||
// After a call to Hijack(), the DNS package will not do anything with the connection.
|
|
||||||
func (r *responseWriter) Hijack() {
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
package dns
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
"github.com/google/gopacket/layers"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface/mocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestResponseWriterLocalAddr(t *testing.T) {
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
device := mocks.NewMockDevice(ctrl)
|
|
||||||
device.EXPECT().Write(gomock.Any(), gomock.Any())
|
|
||||||
|
|
||||||
request := &dns.Msg{
|
|
||||||
Question: []dns.Question{{
|
|
||||||
Name: "google.com.",
|
|
||||||
Qtype: dns.TypeA,
|
|
||||||
Qclass: dns.TypeA,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
replyMessage := &dns.Msg{}
|
|
||||||
replyMessage.SetReply(request)
|
|
||||||
replyMessage.RecursionAvailable = true
|
|
||||||
replyMessage.Rcode = dns.RcodeSuccess
|
|
||||||
replyMessage.Answer = []dns.RR{
|
|
||||||
&dns.A{
|
|
||||||
A: net.IPv4(8, 8, 8, 8),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ipv4 := &layers.IPv4{
|
|
||||||
Protocol: layers.IPProtocolUDP,
|
|
||||||
SrcIP: net.IPv4(127, 0, 0, 1),
|
|
||||||
DstIP: net.IPv4(127, 0, 0, 2),
|
|
||||||
}
|
|
||||||
udp := &layers.UDP{
|
|
||||||
DstPort: 53,
|
|
||||||
SrcPort: 45223,
|
|
||||||
}
|
|
||||||
if err := udp.SetNetworkLayerForChecksum(ipv4); err != nil {
|
|
||||||
t.Error("failed to set network layer for checksum")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize the packet
|
|
||||||
buffer := gopacket.NewSerializeBuffer()
|
|
||||||
options := gopacket.SerializeOptions{
|
|
||||||
ComputeChecksums: true,
|
|
||||||
FixLengths: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
requestData, err := request.Pack()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("got an error while packing the request message, error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
payload := gopacket.Payload(requestData)
|
|
||||||
|
|
||||||
if err := gopacket.SerializeLayers(buffer, options, ipv4, udp, payload); err != nil {
|
|
||||||
t.Errorf("failed to serialize packet: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rw := &responseWriter{
|
|
||||||
local: &net.UDPAddr{
|
|
||||||
IP: net.IPv4(127, 0, 0, 1),
|
|
||||||
Port: 55223,
|
|
||||||
},
|
|
||||||
remote: &net.UDPAddr{
|
|
||||||
IP: net.IPv4(127, 0, 0, 1),
|
|
||||||
Port: 53,
|
|
||||||
},
|
|
||||||
packet: gopacket.NewPacket(
|
|
||||||
buffer.Bytes(),
|
|
||||||
layers.LayerTypeIPv4,
|
|
||||||
gopacket.Default,
|
|
||||||
),
|
|
||||||
device: device,
|
|
||||||
}
|
|
||||||
if err := rw.WriteMsg(replyMessage); err != nil {
|
|
||||||
t.Errorf("got an error while writing the local resolver response, error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,615 +1,10 @@
|
|||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import nbdns "github.com/netbirdio/netbird/dns"
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
"github.com/google/gopacket/layers"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"github.com/mitchellh/hashstructure/v2"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultPort = 53
|
|
||||||
customPort = 5053
|
|
||||||
defaultIP = "127.0.0.1"
|
|
||||||
customIP = "127.0.0.153"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Server is a dns server interface
|
// Server is a dns server interface
|
||||||
type Server interface {
|
type Server interface {
|
||||||
Initialize() error
|
Start()
|
||||||
Stop()
|
Stop()
|
||||||
DnsIP() string
|
|
||||||
UpdateDNSServer(serial uint64, update nbdns.Config) error
|
UpdateDNSServer(serial uint64, update nbdns.Config) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type registeredHandlerMap map[string]handlerWithStop
|
|
||||||
|
|
||||||
// DefaultServer dns server object
|
|
||||||
type DefaultServer struct {
|
|
||||||
ctx context.Context
|
|
||||||
ctxCancel context.CancelFunc
|
|
||||||
mux sync.Mutex
|
|
||||||
udpFilterHookID string
|
|
||||||
server *dns.Server
|
|
||||||
dnsMux *dns.ServeMux
|
|
||||||
dnsMuxMap registeredHandlerMap
|
|
||||||
localResolver *localResolver
|
|
||||||
wgInterface *iface.WGIface
|
|
||||||
hostManager hostManager
|
|
||||||
updateSerial uint64
|
|
||||||
listenerIsRunning bool
|
|
||||||
runtimePort int
|
|
||||||
runtimeIP string
|
|
||||||
previousConfigHash uint64
|
|
||||||
currentConfig hostDNSConfig
|
|
||||||
customAddress *netip.AddrPort
|
|
||||||
enabled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type handlerWithStop interface {
|
|
||||||
dns.Handler
|
|
||||||
stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
type muxUpdate struct {
|
|
||||||
domain string
|
|
||||||
handler handlerWithStop
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDefaultServer returns a new dns server
|
|
||||||
func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface, customAddress string, initialDnsCfg *nbdns.Config) (*DefaultServer, error) {
|
|
||||||
mux := dns.NewServeMux()
|
|
||||||
|
|
||||||
var addrPort *netip.AddrPort
|
|
||||||
if customAddress != "" {
|
|
||||||
parsedAddrPort, err := netip.ParseAddrPort(customAddress)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unable to parse the custom dns address, got error: %s", err)
|
|
||||||
}
|
|
||||||
addrPort = &parsedAddrPort
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, stop := context.WithCancel(ctx)
|
|
||||||
|
|
||||||
defaultServer := &DefaultServer{
|
|
||||||
ctx: ctx,
|
|
||||||
ctxCancel: stop,
|
|
||||||
server: &dns.Server{
|
|
||||||
Net: "udp",
|
|
||||||
Handler: mux,
|
|
||||||
UDPSize: 65535,
|
|
||||||
},
|
|
||||||
dnsMux: mux,
|
|
||||||
dnsMuxMap: make(registeredHandlerMap),
|
|
||||||
localResolver: &localResolver{
|
|
||||||
registeredMap: make(registrationMap),
|
|
||||||
},
|
|
||||||
wgInterface: wgInterface,
|
|
||||||
customAddress: addrPort,
|
|
||||||
}
|
|
||||||
|
|
||||||
if initialDnsCfg != nil {
|
|
||||||
defaultServer.enabled = hasValidDnsServer(initialDnsCfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
if wgInterface.IsUserspaceBind() {
|
|
||||||
defaultServer.evelRuntimeAddressForUserspace()
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultServer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize instantiate host manager. It required to be initialized wginterface
|
|
||||||
func (s *DefaultServer) Initialize() (err error) {
|
|
||||||
s.mux.Lock()
|
|
||||||
defer s.mux.Unlock()
|
|
||||||
|
|
||||||
if s.hostManager != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !s.wgInterface.IsUserspaceBind() {
|
|
||||||
s.evalRuntimeAddress()
|
|
||||||
}
|
|
||||||
s.hostManager, err = newHostManager(s.wgInterface)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// listen runs the listener in a go routine
|
|
||||||
func (s *DefaultServer) listen() {
|
|
||||||
// nil check required in unit tests
|
|
||||||
if s.wgInterface != nil && s.wgInterface.IsUserspaceBind() {
|
|
||||||
s.udpFilterHookID = s.filterDNSTraffic()
|
|
||||||
s.setListenerStatus(true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("starting dns on %s", s.server.Addr)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
s.setListenerStatus(true)
|
|
||||||
defer s.setListenerStatus(false)
|
|
||||||
|
|
||||||
err := s.server.ListenAndServe()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("dns server running with %d port returned an error: %v. Will not retry", s.runtimePort, err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DnsIP returns the DNS resolver server IP address
|
|
||||||
//
|
|
||||||
// When kernel space interface used it return real DNS server listener IP address
|
|
||||||
// For bind interface, fake DNS resolver address returned (second last IP address from Nebird network)
|
|
||||||
func (s *DefaultServer) DnsIP() string {
|
|
||||||
if !s.enabled {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return s.runtimeIP
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) getFirstListenerAvailable() (string, int, error) {
|
|
||||||
ips := []string{defaultIP, customIP}
|
|
||||||
if runtime.GOOS != "darwin" && s.wgInterface != nil {
|
|
||||||
ips = append([]string{s.wgInterface.Address().IP.String()}, ips...)
|
|
||||||
}
|
|
||||||
ports := []int{defaultPort, customPort}
|
|
||||||
for _, port := range ports {
|
|
||||||
for _, ip := range ips {
|
|
||||||
addrString := fmt.Sprintf("%s:%d", ip, port)
|
|
||||||
udpAddr := net.UDPAddrFromAddrPort(netip.MustParseAddrPort(addrString))
|
|
||||||
probeListener, err := net.ListenUDP("udp", udpAddr)
|
|
||||||
if err == nil {
|
|
||||||
err = probeListener.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("got an error closing the probe listener, error: %s", err)
|
|
||||||
}
|
|
||||||
return ip, port, nil
|
|
||||||
}
|
|
||||||
log.Warnf("binding dns on %s is not available, error: %s", addrString, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", 0, fmt.Errorf("unable to find an unused ip and port combination. IPs tested: %v and ports %v", ips, ports)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) setListenerStatus(running bool) {
|
|
||||||
s.listenerIsRunning = running
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop stops the server
|
|
||||||
func (s *DefaultServer) Stop() {
|
|
||||||
s.mux.Lock()
|
|
||||||
defer s.mux.Unlock()
|
|
||||||
s.ctxCancel()
|
|
||||||
|
|
||||||
if s.hostManager != nil {
|
|
||||||
err := s.hostManager.restoreHostDNS()
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.stopListener()
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) stopListener() error {
|
|
||||||
if s.wgInterface != nil && s.wgInterface.IsUserspaceBind() && s.listenerIsRunning {
|
|
||||||
// udpFilterHookID here empty only in the unit tests
|
|
||||||
if filter := s.wgInterface.GetFilter(); filter != nil && s.udpFilterHookID != "" {
|
|
||||||
if err := filter.RemovePacketHook(s.udpFilterHookID); err != nil {
|
|
||||||
log.Errorf("unable to remove DNS packet hook: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.udpFilterHookID = ""
|
|
||||||
s.listenerIsRunning = false
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if !s.listenerIsRunning {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
err := s.server.ShutdownContext(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("stopping dns server listener returned an error: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateDNSServer processes an update received from the management service
|
|
||||||
func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) error {
|
|
||||||
select {
|
|
||||||
case <-s.ctx.Done():
|
|
||||||
log.Infof("not updating DNS server as context is closed")
|
|
||||||
return s.ctx.Err()
|
|
||||||
default:
|
|
||||||
if serial < s.updateSerial {
|
|
||||||
return fmt.Errorf("not applying dns update, error: "+
|
|
||||||
"network update is %d behind the last applied update", s.updateSerial-serial)
|
|
||||||
}
|
|
||||||
s.mux.Lock()
|
|
||||||
defer s.mux.Unlock()
|
|
||||||
|
|
||||||
if s.hostManager == nil {
|
|
||||||
return fmt.Errorf("dns service is not initialized yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
hash, err := hashstructure.Hash(update, hashstructure.FormatV2, &hashstructure.HashOptions{
|
|
||||||
ZeroNil: true,
|
|
||||||
IgnoreZeroValue: true,
|
|
||||||
SlicesAsSets: true,
|
|
||||||
UseStringer: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("unable to hash the dns configuration update, got error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.previousConfigHash == hash {
|
|
||||||
log.Debugf("not applying the dns configuration update as there is nothing new")
|
|
||||||
s.updateSerial = serial
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := s.applyConfiguration(update); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.updateSerial = serial
|
|
||||||
s.previousConfigHash = hash
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
|
|
||||||
// is the service should be disabled, we stop the listener or fake resolver
|
|
||||||
// and proceed with a regular update to clean up the handlers and records
|
|
||||||
if !update.ServiceEnable {
|
|
||||||
if err := s.stopListener(); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
} else if !s.listenerIsRunning {
|
|
||||||
s.listen()
|
|
||||||
}
|
|
||||||
|
|
||||||
localMuxUpdates, localRecords, err := s.buildLocalHandlerUpdate(update.CustomZones)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("not applying dns update, error: %v", err)
|
|
||||||
}
|
|
||||||
upstreamMuxUpdates, err := s.buildUpstreamHandlerUpdate(update.NameServerGroups)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("not applying dns update, error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
muxUpdates := append(localMuxUpdates, upstreamMuxUpdates...)
|
|
||||||
|
|
||||||
s.updateMux(muxUpdates)
|
|
||||||
s.updateLocalResolver(localRecords)
|
|
||||||
s.currentConfig = dnsConfigToHostDNSConfig(update, s.runtimeIP, s.runtimePort)
|
|
||||||
|
|
||||||
hostUpdate := s.currentConfig
|
|
||||||
if s.runtimePort != defaultPort && !s.hostManager.supportCustomPort() {
|
|
||||||
log.Warnf("the DNS manager of this peer doesn't support custom port. Disabling primary DNS setup. " +
|
|
||||||
"Learn more at: https://netbird.io/docs/how-to-guides/nameservers#local-resolver")
|
|
||||||
hostUpdate.routeAll = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = s.hostManager.applyDNSConfig(hostUpdate); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) buildLocalHandlerUpdate(customZones []nbdns.CustomZone) ([]muxUpdate, map[string]nbdns.SimpleRecord, error) {
|
|
||||||
var muxUpdates []muxUpdate
|
|
||||||
localRecords := make(map[string]nbdns.SimpleRecord, 0)
|
|
||||||
|
|
||||||
for _, customZone := range customZones {
|
|
||||||
|
|
||||||
if len(customZone.Records) == 0 {
|
|
||||||
return nil, nil, fmt.Errorf("received an empty list of records")
|
|
||||||
}
|
|
||||||
|
|
||||||
muxUpdates = append(muxUpdates, muxUpdate{
|
|
||||||
domain: customZone.Domain,
|
|
||||||
handler: s.localResolver,
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, record := range customZone.Records {
|
|
||||||
var class uint16 = dns.ClassINET
|
|
||||||
if record.Class != nbdns.DefaultClass {
|
|
||||||
return nil, nil, fmt.Errorf("received an invalid class type: %s", record.Class)
|
|
||||||
}
|
|
||||||
key := buildRecordKey(record.Name, class, uint16(record.Type))
|
|
||||||
localRecords[key] = record
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return muxUpdates, localRecords, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.NameServerGroup) ([]muxUpdate, error) {
|
|
||||||
|
|
||||||
var muxUpdates []muxUpdate
|
|
||||||
for _, nsGroup := range nameServerGroups {
|
|
||||||
if len(nsGroup.NameServers) == 0 {
|
|
||||||
log.Warn("received a nameserver group with empty nameserver list")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
handler := newUpstreamResolver(s.ctx)
|
|
||||||
for _, ns := range nsGroup.NameServers {
|
|
||||||
if ns.NSType != nbdns.UDPNameServerType {
|
|
||||||
log.Warnf("skiping nameserver %s with type %s, this peer supports only %s",
|
|
||||||
ns.IP.String(), ns.NSType.String(), nbdns.UDPNameServerType.String())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
handler.upstreamServers = append(handler.upstreamServers, getNSHostPort(ns))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(handler.upstreamServers) == 0 {
|
|
||||||
handler.stop()
|
|
||||||
log.Errorf("received a nameserver group with an invalid nameserver list")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// when upstream fails to resolve domain several times over all it servers
|
|
||||||
// it will calls this hook to exclude self from the configuration and
|
|
||||||
// reapply DNS settings, but it not touch the original configuration and serial number
|
|
||||||
// because it is temporal deactivation until next try
|
|
||||||
//
|
|
||||||
// after some period defined by upstream it trys to reactivate self by calling this hook
|
|
||||||
// everything we need here is just to re-apply current configuration because it already
|
|
||||||
// contains this upstream settings (temporal deactivation not removed it)
|
|
||||||
handler.deactivate, handler.reactivate = s.upstreamCallbacks(nsGroup, handler)
|
|
||||||
|
|
||||||
if nsGroup.Primary {
|
|
||||||
muxUpdates = append(muxUpdates, muxUpdate{
|
|
||||||
domain: nbdns.RootZone,
|
|
||||||
handler: handler,
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(nsGroup.Domains) == 0 {
|
|
||||||
handler.stop()
|
|
||||||
return nil, fmt.Errorf("received a non primary nameserver group with an empty domain list")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, domain := range nsGroup.Domains {
|
|
||||||
if domain == "" {
|
|
||||||
handler.stop()
|
|
||||||
return nil, fmt.Errorf("received a nameserver group with an empty domain element")
|
|
||||||
}
|
|
||||||
muxUpdates = append(muxUpdates, muxUpdate{
|
|
||||||
domain: domain,
|
|
||||||
handler: handler,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return muxUpdates, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) {
|
|
||||||
muxUpdateMap := make(registeredHandlerMap)
|
|
||||||
|
|
||||||
for _, update := range muxUpdates {
|
|
||||||
s.registerMux(update.domain, update.handler)
|
|
||||||
muxUpdateMap[update.domain] = update.handler
|
|
||||||
if existingHandler, ok := s.dnsMuxMap[update.domain]; ok {
|
|
||||||
existingHandler.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, existingHandler := range s.dnsMuxMap {
|
|
||||||
_, found := muxUpdateMap[key]
|
|
||||||
if !found {
|
|
||||||
existingHandler.stop()
|
|
||||||
s.deregisterMux(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.dnsMuxMap = muxUpdateMap
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) updateLocalResolver(update map[string]nbdns.SimpleRecord) {
|
|
||||||
for key := range s.localResolver.registeredMap {
|
|
||||||
_, found := update[key]
|
|
||||||
if !found {
|
|
||||||
s.localResolver.deleteRecord(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedMap := make(registrationMap)
|
|
||||||
for key, record := range update {
|
|
||||||
err := s.localResolver.registerRecord(record)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("got an error while registering the record (%s), error: %v", record.String(), err)
|
|
||||||
}
|
|
||||||
updatedMap[key] = struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.localResolver.registeredMap = updatedMap
|
|
||||||
}
|
|
||||||
|
|
||||||
func getNSHostPort(ns nbdns.NameServer) string {
|
|
||||||
return fmt.Sprintf("%s:%d", ns.IP.String(), ns.Port)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) registerMux(pattern string, handler dns.Handler) {
|
|
||||||
s.dnsMux.Handle(pattern, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) deregisterMux(pattern string) {
|
|
||||||
s.dnsMux.HandleRemove(pattern)
|
|
||||||
}
|
|
||||||
|
|
||||||
// upstreamCallbacks returns two functions, the first one is used to deactivate
|
|
||||||
// the upstream resolver from the configuration, the second one is used to
|
|
||||||
// reactivate it. Not allowed to call reactivate before deactivate.
|
|
||||||
func (s *DefaultServer) upstreamCallbacks(
|
|
||||||
nsGroup *nbdns.NameServerGroup,
|
|
||||||
handler dns.Handler,
|
|
||||||
) (deactivate func(), reactivate func()) {
|
|
||||||
var removeIndex map[string]int
|
|
||||||
deactivate = func() {
|
|
||||||
s.mux.Lock()
|
|
||||||
defer s.mux.Unlock()
|
|
||||||
|
|
||||||
l := log.WithField("nameservers", nsGroup.NameServers)
|
|
||||||
l.Info("temporary deactivate nameservers group due timeout")
|
|
||||||
|
|
||||||
removeIndex = make(map[string]int)
|
|
||||||
for _, domain := range nsGroup.Domains {
|
|
||||||
removeIndex[domain] = -1
|
|
||||||
}
|
|
||||||
if nsGroup.Primary {
|
|
||||||
removeIndex[nbdns.RootZone] = -1
|
|
||||||
s.currentConfig.routeAll = false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, item := range s.currentConfig.domains {
|
|
||||||
if _, found := removeIndex[item.domain]; found {
|
|
||||||
s.currentConfig.domains[i].disabled = true
|
|
||||||
s.deregisterMux(item.domain)
|
|
||||||
removeIndex[item.domain] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := s.hostManager.applyDNSConfig(s.currentConfig); err != nil {
|
|
||||||
l.WithError(err).Error("fail to apply nameserver deactivation on the host")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reactivate = func() {
|
|
||||||
s.mux.Lock()
|
|
||||||
defer s.mux.Unlock()
|
|
||||||
|
|
||||||
for domain, i := range removeIndex {
|
|
||||||
if i == -1 || i >= len(s.currentConfig.domains) || s.currentConfig.domains[i].domain != domain {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
s.currentConfig.domains[i].disabled = false
|
|
||||||
s.registerMux(domain, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
l := log.WithField("nameservers", nsGroup.NameServers)
|
|
||||||
l.Debug("reactivate temporary disabled nameserver group")
|
|
||||||
|
|
||||||
if nsGroup.Primary {
|
|
||||||
s.currentConfig.routeAll = true
|
|
||||||
}
|
|
||||||
if err := s.hostManager.applyDNSConfig(s.currentConfig); err != nil {
|
|
||||||
l.WithError(err).Error("reactivate temporary disabled nameserver group, DNS update apply")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) filterDNSTraffic() string {
|
|
||||||
filter := s.wgInterface.GetFilter()
|
|
||||||
if filter == nil {
|
|
||||||
log.Error("can't set DNS filter, filter not initialized")
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
firstLayerDecoder := layers.LayerTypeIPv4
|
|
||||||
if s.wgInterface.Address().Network.IP.To4() == nil {
|
|
||||||
firstLayerDecoder = layers.LayerTypeIPv6
|
|
||||||
}
|
|
||||||
|
|
||||||
hook := func(packetData []byte) bool {
|
|
||||||
// Decode the packet
|
|
||||||
packet := gopacket.NewPacket(packetData, firstLayerDecoder, gopacket.Default)
|
|
||||||
|
|
||||||
// Get the UDP layer
|
|
||||||
udpLayer := packet.Layer(layers.LayerTypeUDP)
|
|
||||||
udp := udpLayer.(*layers.UDP)
|
|
||||||
|
|
||||||
msg := new(dns.Msg)
|
|
||||||
if err := msg.Unpack(udp.Payload); err != nil {
|
|
||||||
log.Tracef("parse DNS request: %v", err)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
writer := responseWriter{
|
|
||||||
packet: packet,
|
|
||||||
device: s.wgInterface.GetDevice().Device,
|
|
||||||
}
|
|
||||||
go s.dnsMux.ServeDNS(&writer, msg)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return filter.AddUDPPacketHook(false, net.ParseIP(s.runtimeIP), uint16(s.runtimePort), hook)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) evelRuntimeAddressForUserspace() {
|
|
||||||
s.runtimeIP = getLastIPFromNetwork(s.wgInterface.Address().Network, 1)
|
|
||||||
s.runtimePort = defaultPort
|
|
||||||
s.server.Addr = fmt.Sprintf("%s:%d", s.runtimeIP, s.runtimePort)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DefaultServer) evalRuntimeAddress() {
|
|
||||||
defer func() {
|
|
||||||
s.server.Addr = fmt.Sprintf("%s:%d", s.runtimeIP, s.runtimePort)
|
|
||||||
}()
|
|
||||||
|
|
||||||
if s.customAddress != nil {
|
|
||||||
s.runtimeIP = s.customAddress.Addr().String()
|
|
||||||
s.runtimePort = int(s.customAddress.Port())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ip, port, err := s.getFirstListenerAvailable()
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.runtimeIP = ip
|
|
||||||
s.runtimePort = port
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLastIPFromNetwork(network *net.IPNet, fromEnd int) string {
|
|
||||||
// Calculate the last IP in the CIDR range
|
|
||||||
var endIP net.IP
|
|
||||||
for i := 0; i < len(network.IP); i++ {
|
|
||||||
endIP = append(endIP, network.IP[i]|^network.Mask[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert to big.Int
|
|
||||||
endInt := big.NewInt(0)
|
|
||||||
endInt.SetBytes(endIP)
|
|
||||||
|
|
||||||
// subtract fromEnd from the last ip
|
|
||||||
fromEndBig := big.NewInt(int64(fromEnd))
|
|
||||||
resultInt := big.NewInt(0)
|
|
||||||
resultInt.Sub(endInt, fromEndBig)
|
|
||||||
|
|
||||||
return net.IP(resultInt.Bytes()).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasValidDnsServer(cfg *nbdns.Config) bool {
|
|
||||||
for _, c := range cfg.NameServerGroups {
|
|
||||||
if c.Primary {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
32
client/internal/dns/server_android.go
Normal file
32
client/internal/dns/server_android.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultServer dummy dns server
|
||||||
|
type DefaultServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultServer On Android the DNS feature is not supported yet
|
||||||
|
func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface, customAddress string) (*DefaultServer, error) {
|
||||||
|
return &DefaultServer{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start dummy implementation
|
||||||
|
func (s DefaultServer) Start() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop dummy implementation
|
||||||
|
func (s DefaultServer) Stop() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDNSServer dummy implementation
|
||||||
|
func (s DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
465
client/internal/dns/server_nonandroid.go
Normal file
465
client/internal/dns/server_nonandroid.go
Normal file
@@ -0,0 +1,465 @@
|
|||||||
|
//go:build !android
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
"github.com/mitchellh/hashstructure/v2"
|
||||||
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultPort = 53
|
||||||
|
customPort = 5053
|
||||||
|
defaultIP = "127.0.0.1"
|
||||||
|
customIP = "127.0.0.153"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultServer dns server object
|
||||||
|
type DefaultServer struct {
|
||||||
|
ctx context.Context
|
||||||
|
ctxCancel context.CancelFunc
|
||||||
|
upstreamCtxCancel context.CancelFunc
|
||||||
|
mux sync.Mutex
|
||||||
|
server *dns.Server
|
||||||
|
dnsMux *dns.ServeMux
|
||||||
|
dnsMuxMap registrationMap
|
||||||
|
localResolver *localResolver
|
||||||
|
wgInterface *iface.WGIface
|
||||||
|
hostManager hostManager
|
||||||
|
updateSerial uint64
|
||||||
|
listenerIsRunning bool
|
||||||
|
runtimePort int
|
||||||
|
runtimeIP string
|
||||||
|
previousConfigHash uint64
|
||||||
|
currentConfig hostDNSConfig
|
||||||
|
customAddress *netip.AddrPort
|
||||||
|
}
|
||||||
|
|
||||||
|
type muxUpdate struct {
|
||||||
|
domain string
|
||||||
|
handler dns.Handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultServer returns a new dns server
|
||||||
|
func NewDefaultServer(ctx context.Context, wgInterface *iface.WGIface, customAddress string) (*DefaultServer, error) {
|
||||||
|
mux := dns.NewServeMux()
|
||||||
|
|
||||||
|
dnsServer := &dns.Server{
|
||||||
|
Net: "udp",
|
||||||
|
Handler: mux,
|
||||||
|
UDPSize: 65535,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, stop := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
var addrPort *netip.AddrPort
|
||||||
|
if customAddress != "" {
|
||||||
|
parsedAddrPort, err := netip.ParseAddrPort(customAddress)
|
||||||
|
if err != nil {
|
||||||
|
stop()
|
||||||
|
return nil, fmt.Errorf("unable to parse the custom dns address, got error: %s", err)
|
||||||
|
}
|
||||||
|
addrPort = &parsedAddrPort
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultServer := &DefaultServer{
|
||||||
|
ctx: ctx,
|
||||||
|
ctxCancel: stop,
|
||||||
|
server: dnsServer,
|
||||||
|
dnsMux: mux,
|
||||||
|
dnsMuxMap: make(registrationMap),
|
||||||
|
localResolver: &localResolver{
|
||||||
|
registeredMap: make(registrationMap),
|
||||||
|
},
|
||||||
|
wgInterface: wgInterface,
|
||||||
|
runtimePort: defaultPort,
|
||||||
|
customAddress: addrPort,
|
||||||
|
}
|
||||||
|
|
||||||
|
hostmanager, err := newHostManager(wgInterface)
|
||||||
|
if err != nil {
|
||||||
|
stop()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defaultServer.hostManager = hostmanager
|
||||||
|
return defaultServer, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start runs the listener in a go routine
|
||||||
|
func (s *DefaultServer) Start() {
|
||||||
|
if s.customAddress != nil {
|
||||||
|
s.runtimeIP = s.customAddress.Addr().String()
|
||||||
|
s.runtimePort = int(s.customAddress.Port())
|
||||||
|
} else {
|
||||||
|
ip, port, err := s.getFirstListenerAvailable()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.runtimeIP = ip
|
||||||
|
s.runtimePort = port
|
||||||
|
}
|
||||||
|
|
||||||
|
s.server.Addr = fmt.Sprintf("%s:%d", s.runtimeIP, s.runtimePort)
|
||||||
|
|
||||||
|
log.Debugf("starting dns on %s", s.server.Addr)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
s.setListenerStatus(true)
|
||||||
|
defer s.setListenerStatus(false)
|
||||||
|
|
||||||
|
err := s.server.ListenAndServe()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("dns server running with %d port returned an error: %v. Will not retry", s.runtimePort, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) getFirstListenerAvailable() (string, int, error) {
|
||||||
|
ips := []string{defaultIP, customIP}
|
||||||
|
if runtime.GOOS != "darwin" && s.wgInterface != nil {
|
||||||
|
ips = append([]string{s.wgInterface.Address().IP.String()}, ips...)
|
||||||
|
}
|
||||||
|
ports := []int{defaultPort, customPort}
|
||||||
|
for _, port := range ports {
|
||||||
|
for _, ip := range ips {
|
||||||
|
addrString := fmt.Sprintf("%s:%d", ip, port)
|
||||||
|
udpAddr := net.UDPAddrFromAddrPort(netip.MustParseAddrPort(addrString))
|
||||||
|
probeListener, err := net.ListenUDP("udp", udpAddr)
|
||||||
|
if err == nil {
|
||||||
|
err = probeListener.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("got an error closing the probe listener, error: %s", err)
|
||||||
|
}
|
||||||
|
return ip, port, nil
|
||||||
|
}
|
||||||
|
log.Warnf("binding dns on %s is not available, error: %s", addrString, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", 0, fmt.Errorf("unable to find an unused ip and port combination. IPs tested: %v and ports %v", ips, ports)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) setListenerStatus(running bool) {
|
||||||
|
s.listenerIsRunning = running
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the server
|
||||||
|
func (s *DefaultServer) Stop() {
|
||||||
|
s.mux.Lock()
|
||||||
|
defer s.mux.Unlock()
|
||||||
|
s.ctxCancel()
|
||||||
|
|
||||||
|
err := s.hostManager.restoreHostDNS()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.stopListener()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) stopListener() error {
|
||||||
|
if !s.listenerIsRunning {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
err := s.server.ShutdownContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("stopping dns server listener returned an error: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateDNSServer processes an update received from the management service
|
||||||
|
func (s *DefaultServer) UpdateDNSServer(serial uint64, update nbdns.Config) error {
|
||||||
|
select {
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
log.Infof("not updating DNS server as context is closed")
|
||||||
|
return s.ctx.Err()
|
||||||
|
default:
|
||||||
|
if serial < s.updateSerial {
|
||||||
|
return fmt.Errorf("not applying dns update, error: "+
|
||||||
|
"network update is %d behind the last applied update", s.updateSerial-serial)
|
||||||
|
}
|
||||||
|
s.mux.Lock()
|
||||||
|
defer s.mux.Unlock()
|
||||||
|
|
||||||
|
hash, err := hashstructure.Hash(update, hashstructure.FormatV2, &hashstructure.HashOptions{
|
||||||
|
ZeroNil: true,
|
||||||
|
IgnoreZeroValue: true,
|
||||||
|
SlicesAsSets: true,
|
||||||
|
UseStringer: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to hash the dns configuration update, got error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.previousConfigHash == hash {
|
||||||
|
log.Debugf("not applying the dns configuration update as there is nothing new")
|
||||||
|
s.updateSerial = serial
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.applyConfiguration(update); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.updateSerial = serial
|
||||||
|
s.previousConfigHash = hash
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) applyConfiguration(update nbdns.Config) error {
|
||||||
|
// is the service should be disabled, we stop the listener
|
||||||
|
// and proceed with a regular update to clean up the handlers and records
|
||||||
|
if !update.ServiceEnable {
|
||||||
|
err := s.stopListener()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
} else if !s.listenerIsRunning {
|
||||||
|
s.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
localMuxUpdates, localRecords, err := s.buildLocalHandlerUpdate(update.CustomZones)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("not applying dns update, error: %v", err)
|
||||||
|
}
|
||||||
|
upstreamMuxUpdates, err := s.buildUpstreamHandlerUpdate(update.NameServerGroups)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("not applying dns update, error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
muxUpdates := append(localMuxUpdates, upstreamMuxUpdates...)
|
||||||
|
|
||||||
|
s.updateMux(muxUpdates)
|
||||||
|
s.updateLocalResolver(localRecords)
|
||||||
|
s.currentConfig = dnsConfigToHostDNSConfig(update, s.runtimeIP, s.runtimePort)
|
||||||
|
|
||||||
|
if err = s.hostManager.applyDNSConfig(s.currentConfig); err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) buildLocalHandlerUpdate(customZones []nbdns.CustomZone) ([]muxUpdate, map[string]nbdns.SimpleRecord, error) {
|
||||||
|
var muxUpdates []muxUpdate
|
||||||
|
localRecords := make(map[string]nbdns.SimpleRecord, 0)
|
||||||
|
|
||||||
|
for _, customZone := range customZones {
|
||||||
|
|
||||||
|
if len(customZone.Records) == 0 {
|
||||||
|
return nil, nil, fmt.Errorf("received an empty list of records")
|
||||||
|
}
|
||||||
|
|
||||||
|
muxUpdates = append(muxUpdates, muxUpdate{
|
||||||
|
domain: customZone.Domain,
|
||||||
|
handler: s.localResolver,
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, record := range customZone.Records {
|
||||||
|
var class uint16 = dns.ClassINET
|
||||||
|
if record.Class != nbdns.DefaultClass {
|
||||||
|
return nil, nil, fmt.Errorf("received an invalid class type: %s", record.Class)
|
||||||
|
}
|
||||||
|
key := buildRecordKey(record.Name, class, uint16(record.Type))
|
||||||
|
localRecords[key] = record
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return muxUpdates, localRecords, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.NameServerGroup) ([]muxUpdate, error) {
|
||||||
|
// clean up the previous upstream resolver
|
||||||
|
if s.upstreamCtxCancel != nil {
|
||||||
|
s.upstreamCtxCancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
var muxUpdates []muxUpdate
|
||||||
|
for _, nsGroup := range nameServerGroups {
|
||||||
|
if len(nsGroup.NameServers) == 0 {
|
||||||
|
log.Warn("received a nameserver group with empty nameserver list")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var ctx context.Context
|
||||||
|
ctx, s.upstreamCtxCancel = context.WithCancel(s.ctx)
|
||||||
|
|
||||||
|
handler := newUpstreamResolver(ctx)
|
||||||
|
for _, ns := range nsGroup.NameServers {
|
||||||
|
if ns.NSType != nbdns.UDPNameServerType {
|
||||||
|
log.Warnf("skiping nameserver %s with type %s, this peer supports only %s",
|
||||||
|
ns.IP.String(), ns.NSType.String(), nbdns.UDPNameServerType.String())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
handler.upstreamServers = append(handler.upstreamServers, getNSHostPort(ns))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(handler.upstreamServers) == 0 {
|
||||||
|
log.Errorf("received a nameserver group with an invalid nameserver list")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// when upstream fails to resolve domain several times over all it servers
|
||||||
|
// it will calls this hook to exclude self from the configuration and
|
||||||
|
// reapply DNS settings, but it not touch the original configuration and serial number
|
||||||
|
// because it is temporal deactivation until next try
|
||||||
|
//
|
||||||
|
// after some period defined by upstream it trys to reactivate self by calling this hook
|
||||||
|
// everything we need here is just to re-apply current configuration because it already
|
||||||
|
// contains this upstream settings (temporal deactivation not removed it)
|
||||||
|
handler.deactivate, handler.reactivate = s.upstreamCallbacks(nsGroup, handler)
|
||||||
|
|
||||||
|
if nsGroup.Primary {
|
||||||
|
muxUpdates = append(muxUpdates, muxUpdate{
|
||||||
|
domain: nbdns.RootZone,
|
||||||
|
handler: handler,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nsGroup.Domains) == 0 {
|
||||||
|
return nil, fmt.Errorf("received a non primary nameserver group with an empty domain list")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, domain := range nsGroup.Domains {
|
||||||
|
if domain == "" {
|
||||||
|
return nil, fmt.Errorf("received a nameserver group with an empty domain element")
|
||||||
|
}
|
||||||
|
muxUpdates = append(muxUpdates, muxUpdate{
|
||||||
|
domain: domain,
|
||||||
|
handler: handler,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return muxUpdates, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) {
|
||||||
|
muxUpdateMap := make(registrationMap)
|
||||||
|
|
||||||
|
for _, update := range muxUpdates {
|
||||||
|
s.registerMux(update.domain, update.handler)
|
||||||
|
muxUpdateMap[update.domain] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
for key := range s.dnsMuxMap {
|
||||||
|
_, found := muxUpdateMap[key]
|
||||||
|
if !found {
|
||||||
|
s.deregisterMux(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.dnsMuxMap = muxUpdateMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) updateLocalResolver(update map[string]nbdns.SimpleRecord) {
|
||||||
|
for key := range s.localResolver.registeredMap {
|
||||||
|
_, found := update[key]
|
||||||
|
if !found {
|
||||||
|
s.localResolver.deleteRecord(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedMap := make(registrationMap)
|
||||||
|
for key, record := range update {
|
||||||
|
err := s.localResolver.registerRecord(record)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("got an error while registering the record (%s), error: %v", record.String(), err)
|
||||||
|
}
|
||||||
|
updatedMap[key] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.localResolver.registeredMap = updatedMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNSHostPort(ns nbdns.NameServer) string {
|
||||||
|
return fmt.Sprintf("%s:%d", ns.IP.String(), ns.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) registerMux(pattern string, handler dns.Handler) {
|
||||||
|
s.dnsMux.Handle(pattern, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultServer) deregisterMux(pattern string) {
|
||||||
|
s.dnsMux.HandleRemove(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
// upstreamCallbacks returns two functions, the first one is used to deactivate
|
||||||
|
// the upstream resolver from the configuration, the second one is used to
|
||||||
|
// reactivate it. Not allowed to call reactivate before deactivate.
|
||||||
|
func (s *DefaultServer) upstreamCallbacks(
|
||||||
|
nsGroup *nbdns.NameServerGroup,
|
||||||
|
handler dns.Handler,
|
||||||
|
) (deactivate func(), reactivate func()) {
|
||||||
|
var removeIndex map[string]int
|
||||||
|
deactivate = func() {
|
||||||
|
s.mux.Lock()
|
||||||
|
defer s.mux.Unlock()
|
||||||
|
|
||||||
|
l := log.WithField("nameservers", nsGroup.NameServers)
|
||||||
|
l.Info("temporary deactivate nameservers group due timeout")
|
||||||
|
|
||||||
|
removeIndex = make(map[string]int)
|
||||||
|
for _, domain := range nsGroup.Domains {
|
||||||
|
removeIndex[domain] = -1
|
||||||
|
}
|
||||||
|
if nsGroup.Primary {
|
||||||
|
removeIndex[nbdns.RootZone] = -1
|
||||||
|
s.currentConfig.routeAll = false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, item := range s.currentConfig.domains {
|
||||||
|
if _, found := removeIndex[item.domain]; found {
|
||||||
|
s.currentConfig.domains[i].disabled = true
|
||||||
|
s.deregisterMux(item.domain)
|
||||||
|
removeIndex[item.domain] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := s.hostManager.applyDNSConfig(s.currentConfig); err != nil {
|
||||||
|
l.WithError(err).Error("fail to apply nameserver deactivation on the host")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reactivate = func() {
|
||||||
|
s.mux.Lock()
|
||||||
|
defer s.mux.Unlock()
|
||||||
|
|
||||||
|
for domain, i := range removeIndex {
|
||||||
|
if i == -1 || i >= len(s.currentConfig.domains) || s.currentConfig.domains[i].domain != domain {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s.currentConfig.domains[i].disabled = false
|
||||||
|
s.registerMux(domain, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := log.WithField("nameservers", nsGroup.NameServers)
|
||||||
|
l.Debug("reactivate temporary disabled nameserver group")
|
||||||
|
|
||||||
|
if nsGroup.Primary {
|
||||||
|
s.currentConfig.routeAll = true
|
||||||
|
}
|
||||||
|
if err := s.hostManager.applyDNSConfig(s.currentConfig); err != nil {
|
||||||
|
l.WithError(err).Error("reactivate temporary disabled nameserver group, DNS update apply")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -5,18 +5,16 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
pfmock "github.com/netbirdio/netbird/iface/mocks"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var zoneRecords = []nbdns.SimpleRecord{
|
var zoneRecords = []nbdns.SimpleRecord{
|
||||||
@@ -43,23 +41,21 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
dummyHandler := &localResolver{}
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
initUpstreamMap registeredHandlerMap
|
initUpstreamMap registrationMap
|
||||||
initLocalMap registrationMap
|
initLocalMap registrationMap
|
||||||
initSerial uint64
|
initSerial uint64
|
||||||
inputSerial uint64
|
inputSerial uint64
|
||||||
inputUpdate nbdns.Config
|
inputUpdate nbdns.Config
|
||||||
shouldFail bool
|
shouldFail bool
|
||||||
expectedUpstreamMap registeredHandlerMap
|
expectedUpstreamMap registrationMap
|
||||||
expectedLocalMap registrationMap
|
expectedLocalMap registrationMap
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Initial Config Should Succeed",
|
name: "Initial Config Should Succeed",
|
||||||
initLocalMap: make(registrationMap),
|
initLocalMap: make(registrationMap),
|
||||||
initUpstreamMap: make(registeredHandlerMap),
|
initUpstreamMap: make(registrationMap),
|
||||||
initSerial: 0,
|
initSerial: 0,
|
||||||
inputSerial: 1,
|
inputSerial: 1,
|
||||||
inputUpdate: nbdns.Config{
|
inputUpdate: nbdns.Config{
|
||||||
@@ -81,13 +77,13 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedUpstreamMap: registeredHandlerMap{"netbird.io": dummyHandler, "netbird.cloud": dummyHandler, nbdns.RootZone: dummyHandler},
|
expectedUpstreamMap: registrationMap{"netbird.io": struct{}{}, "netbird.cloud": struct{}{}, nbdns.RootZone: struct{}{}},
|
||||||
expectedLocalMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}},
|
expectedLocalMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "New Config Should Succeed",
|
name: "New Config Should Succeed",
|
||||||
initLocalMap: registrationMap{"netbird.cloud": struct{}{}},
|
initLocalMap: registrationMap{"netbird.cloud": struct{}{}},
|
||||||
initUpstreamMap: registeredHandlerMap{buildRecordKey(zoneRecords[0].Name, 1, 1): dummyHandler},
|
initUpstreamMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}},
|
||||||
initSerial: 0,
|
initSerial: 0,
|
||||||
inputSerial: 1,
|
inputSerial: 1,
|
||||||
inputUpdate: nbdns.Config{
|
inputUpdate: nbdns.Config{
|
||||||
@@ -105,13 +101,13 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedUpstreamMap: registeredHandlerMap{"netbird.io": dummyHandler, "netbird.cloud": dummyHandler},
|
expectedUpstreamMap: registrationMap{"netbird.io": struct{}{}, "netbird.cloud": struct{}{}},
|
||||||
expectedLocalMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}},
|
expectedLocalMap: registrationMap{buildRecordKey(zoneRecords[0].Name, 1, 1): struct{}{}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Smaller Config Serial Should Be Skipped",
|
name: "Smaller Config Serial Should Be Skipped",
|
||||||
initLocalMap: make(registrationMap),
|
initLocalMap: make(registrationMap),
|
||||||
initUpstreamMap: make(registeredHandlerMap),
|
initUpstreamMap: make(registrationMap),
|
||||||
initSerial: 2,
|
initSerial: 2,
|
||||||
inputSerial: 1,
|
inputSerial: 1,
|
||||||
shouldFail: true,
|
shouldFail: true,
|
||||||
@@ -119,7 +115,7 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Empty NS Group Domain Or Not Primary Element Should Fail",
|
name: "Empty NS Group Domain Or Not Primary Element Should Fail",
|
||||||
initLocalMap: make(registrationMap),
|
initLocalMap: make(registrationMap),
|
||||||
initUpstreamMap: make(registeredHandlerMap),
|
initUpstreamMap: make(registrationMap),
|
||||||
initSerial: 0,
|
initSerial: 0,
|
||||||
inputSerial: 1,
|
inputSerial: 1,
|
||||||
inputUpdate: nbdns.Config{
|
inputUpdate: nbdns.Config{
|
||||||
@@ -141,7 +137,7 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Invalid NS Group Nameservers list Should Fail",
|
name: "Invalid NS Group Nameservers list Should Fail",
|
||||||
initLocalMap: make(registrationMap),
|
initLocalMap: make(registrationMap),
|
||||||
initUpstreamMap: make(registeredHandlerMap),
|
initUpstreamMap: make(registrationMap),
|
||||||
initSerial: 0,
|
initSerial: 0,
|
||||||
inputSerial: 1,
|
inputSerial: 1,
|
||||||
inputUpdate: nbdns.Config{
|
inputUpdate: nbdns.Config{
|
||||||
@@ -163,7 +159,7 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Invalid Custom Zone Records list Should Fail",
|
name: "Invalid Custom Zone Records list Should Fail",
|
||||||
initLocalMap: make(registrationMap),
|
initLocalMap: make(registrationMap),
|
||||||
initUpstreamMap: make(registeredHandlerMap),
|
initUpstreamMap: make(registrationMap),
|
||||||
initSerial: 0,
|
initSerial: 0,
|
||||||
inputSerial: 1,
|
inputSerial: 1,
|
||||||
inputUpdate: nbdns.Config{
|
inputUpdate: nbdns.Config{
|
||||||
@@ -185,21 +181,21 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Empty Config Should Succeed and Clean Maps",
|
name: "Empty Config Should Succeed and Clean Maps",
|
||||||
initLocalMap: registrationMap{"netbird.cloud": struct{}{}},
|
initLocalMap: registrationMap{"netbird.cloud": struct{}{}},
|
||||||
initUpstreamMap: registeredHandlerMap{zoneRecords[0].Name: dummyHandler},
|
initUpstreamMap: registrationMap{zoneRecords[0].Name: struct{}{}},
|
||||||
initSerial: 0,
|
initSerial: 0,
|
||||||
inputSerial: 1,
|
inputSerial: 1,
|
||||||
inputUpdate: nbdns.Config{ServiceEnable: true},
|
inputUpdate: nbdns.Config{ServiceEnable: true},
|
||||||
expectedUpstreamMap: make(registeredHandlerMap),
|
expectedUpstreamMap: make(registrationMap),
|
||||||
expectedLocalMap: make(registrationMap),
|
expectedLocalMap: make(registrationMap),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Disabled Service Should clean map",
|
name: "Disabled Service Should clean map",
|
||||||
initLocalMap: registrationMap{"netbird.cloud": struct{}{}},
|
initLocalMap: registrationMap{"netbird.cloud": struct{}{}},
|
||||||
initUpstreamMap: registeredHandlerMap{zoneRecords[0].Name: dummyHandler},
|
initUpstreamMap: registrationMap{zoneRecords[0].Name: struct{}{}},
|
||||||
initSerial: 0,
|
initSerial: 0,
|
||||||
inputSerial: 1,
|
inputSerial: 1,
|
||||||
inputUpdate: nbdns.Config{ServiceEnable: false},
|
inputUpdate: nbdns.Config{ServiceEnable: false},
|
||||||
expectedUpstreamMap: make(registeredHandlerMap),
|
expectedUpstreamMap: make(registrationMap),
|
||||||
expectedLocalMap: make(registrationMap),
|
expectedLocalMap: make(registrationMap),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -210,7 +206,7 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
wgIface, err := iface.NewWGIFace(fmt.Sprintf("utun230%d", n), fmt.Sprintf("100.66.100.%d/32", n+1), iface.DefaultMTU, nil, newNet)
|
wgIface, err := iface.NewWGIFace(fmt.Sprintf("utun230%d", n), fmt.Sprintf("100.66.100.%d/32", n+1), iface.DefaultMTU, nil, nil, newNet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -224,11 +220,7 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
t.Log(err)
|
t.Log(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
dnsServer, err := NewDefaultServer(context.Background(), wgIface, "", nil)
|
dnsServer, err := NewDefaultServer(context.Background(), wgIface, "")
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
err = dnsServer.Initialize()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -278,133 +270,6 @@ func TestUpdateDNSServer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDNSFakeResolverHandleUpdates(t *testing.T) {
|
|
||||||
ov := os.Getenv("NB_WG_KERNEL_DISABLED")
|
|
||||||
defer os.Setenv("NB_WG_KERNEL_DISABLED", ov)
|
|
||||||
|
|
||||||
os.Setenv("NB_WG_KERNEL_DISABLED", "true")
|
|
||||||
newNet, err := stdnet.NewNet(nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("create stdnet: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.1/32", iface.DefaultMTU, nil, newNet)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("build interface wireguard: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = wgIface.Create()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("crate and init wireguard interface: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err = wgIface.Close(); err != nil {
|
|
||||||
t.Logf("close wireguard interface: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
_, ipNet, err := net.ParseCIDR("100.66.100.1/32")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("parse CIDR: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
packetfilter := pfmock.NewMockPacketFilter(ctrl)
|
|
||||||
packetfilter.EXPECT().SetNetwork(ipNet)
|
|
||||||
packetfilter.EXPECT().DropOutgoing(gomock.Any()).AnyTimes()
|
|
||||||
packetfilter.EXPECT().AddUDPPacketHook(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes()
|
|
||||||
packetfilter.EXPECT().RemovePacketHook(gomock.Any()).AnyTimes()
|
|
||||||
|
|
||||||
if err := wgIface.SetFilter(packetfilter); err != nil {
|
|
||||||
t.Errorf("set packet filter: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsServer, err := NewDefaultServer(context.Background(), wgIface, "", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("create DNS server: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = dnsServer.Initialize()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("run DNS server: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err = dnsServer.hostManager.restoreHostDNS(); err != nil {
|
|
||||||
t.Logf("restore DNS settings on the host: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
dnsServer.dnsMuxMap = registeredHandlerMap{zoneRecords[0].Name: &localResolver{}}
|
|
||||||
dnsServer.localResolver.registeredMap = registrationMap{"netbird.cloud": struct{}{}}
|
|
||||||
dnsServer.updateSerial = 0
|
|
||||||
|
|
||||||
nameServers := []nbdns.NameServer{
|
|
||||||
{
|
|
||||||
IP: netip.MustParseAddr("8.8.8.8"),
|
|
||||||
NSType: nbdns.UDPNameServerType,
|
|
||||||
Port: 53,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
IP: netip.MustParseAddr("8.8.4.4"),
|
|
||||||
NSType: nbdns.UDPNameServerType,
|
|
||||||
Port: 53,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
update := nbdns.Config{
|
|
||||||
ServiceEnable: true,
|
|
||||||
CustomZones: []nbdns.CustomZone{
|
|
||||||
{
|
|
||||||
Domain: "netbird.cloud",
|
|
||||||
Records: zoneRecords,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NameServerGroups: []*nbdns.NameServerGroup{
|
|
||||||
{
|
|
||||||
Domains: []string{"netbird.io"},
|
|
||||||
NameServers: nameServers,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
NameServers: nameServers,
|
|
||||||
Primary: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the server with regular configuration
|
|
||||||
if err := dnsServer.UpdateDNSServer(1, update); err != nil {
|
|
||||||
t.Fatalf("update dns server should not fail, got error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
update2 := update
|
|
||||||
update2.ServiceEnable = false
|
|
||||||
// Disable the server, stop the listener
|
|
||||||
if err := dnsServer.UpdateDNSServer(2, update2); err != nil {
|
|
||||||
t.Fatalf("update dns server should not fail, got error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
update3 := update2
|
|
||||||
update3.NameServerGroups = update3.NameServerGroups[:1]
|
|
||||||
// But service still get updates and we checking that we handle
|
|
||||||
// internal state in the right way
|
|
||||||
if err := dnsServer.UpdateDNSServer(3, update3); err != nil {
|
|
||||||
t.Fatalf("update dns server should not fail, got error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDNSServerStartStop(t *testing.T) {
|
func TestDNSServerStartStop(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -424,7 +289,7 @@ func TestDNSServerStartStop(t *testing.T) {
|
|||||||
dnsServer := getDefaultServerWithNoHostManager(t, testCase.addrPort)
|
dnsServer := getDefaultServerWithNoHostManager(t, testCase.addrPort)
|
||||||
|
|
||||||
dnsServer.hostManager = newNoopHostMocker()
|
dnsServer.hostManager = newNoopHostMocker()
|
||||||
dnsServer.listen()
|
dnsServer.Start()
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
if !dnsServer.listenerIsRunning {
|
if !dnsServer.listenerIsRunning {
|
||||||
t.Fatal("dns server listener is not running")
|
t.Fatal("dns server listener is not running")
|
||||||
@@ -561,42 +426,15 @@ func getDefaultServerWithNoHostManager(t *testing.T, addrPort string) *DefaultSe
|
|||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.TODO())
|
ctx, cancel := context.WithCancel(context.TODO())
|
||||||
|
|
||||||
ds := &DefaultServer{
|
return &DefaultServer{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
ctxCancel: cancel,
|
ctxCancel: cancel,
|
||||||
server: dnsServer,
|
server: dnsServer,
|
||||||
dnsMux: mux,
|
dnsMux: mux,
|
||||||
dnsMuxMap: make(registeredHandlerMap),
|
dnsMuxMap: make(registrationMap),
|
||||||
localResolver: &localResolver{
|
localResolver: &localResolver{
|
||||||
registeredMap: make(registrationMap),
|
registeredMap: make(registrationMap),
|
||||||
},
|
},
|
||||||
customAddress: parsedAddrPort,
|
customAddress: parsedAddrPort,
|
||||||
}
|
}
|
||||||
ds.evalRuntimeAddress()
|
|
||||||
return ds
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetLastIPFromNetwork(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
addr string
|
|
||||||
ip string
|
|
||||||
}{
|
|
||||||
{"2001:db8::/32", "2001:db8:ffff:ffff:ffff:ffff:ffff:fffe"},
|
|
||||||
{"192.168.0.0/30", "192.168.0.2"},
|
|
||||||
{"192.168.0.0/16", "192.168.255.254"},
|
|
||||||
{"192.168.0.0/24", "192.168.0.254"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
_, ipnet, err := net.ParseCIDR(tt.addr)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Error parsing CIDR: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
lastIP := getLastIPFromNetwork(ipnet, 1)
|
|
||||||
if lastIP != tt.ip {
|
|
||||||
t.Errorf("wrong IP address, expected %s: got %s", tt.ip, lastIP)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
//go:build !android
|
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -11,11 +9,10 @@ import (
|
|||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -78,10 +75,6 @@ func newSystemdDbusConfigurator(wgInterface *iface.WGIface) (hostManager, error)
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *systemdDbusConfigurator) supportCustomPort() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *systemdDbusConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
func (s *systemdDbusConfigurator) applyDNSConfig(config hostDNSConfig) error {
|
||||||
parsedIP, err := netip.ParseAddr(config.serverIP)
|
parsedIP, err := netip.ParseAddr(config.serverIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,31 +3,24 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/cenkalti/backoff/v4"
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
failsTillDeact = int32(5)
|
failsTillDeact = int32(3)
|
||||||
reactivatePeriod = 30 * time.Second
|
reactivatePeriod = time.Minute
|
||||||
upstreamTimeout = 15 * time.Second
|
upstreamTimeout = 15 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
type upstreamClient interface {
|
|
||||||
ExchangeContext(ctx context.Context, m *dns.Msg, a string) (r *dns.Msg, rtt time.Duration, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type upstreamResolver struct {
|
type upstreamResolver struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
upstreamClient *dns.Client
|
||||||
upstreamClient upstreamClient
|
|
||||||
upstreamServers []string
|
upstreamServers []string
|
||||||
disabled bool
|
disabled bool
|
||||||
failsCount atomic.Int32
|
failsCount atomic.Int32
|
||||||
@@ -40,11 +33,9 @@ type upstreamResolver struct {
|
|||||||
reactivate func()
|
reactivate func()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newUpstreamResolver(parentCTX context.Context) *upstreamResolver {
|
func newUpstreamResolver(ctx context.Context) *upstreamResolver {
|
||||||
ctx, cancel := context.WithCancel(parentCTX)
|
|
||||||
return &upstreamResolver{
|
return &upstreamResolver{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
|
||||||
upstreamClient: &dns.Client{},
|
upstreamClient: &dns.Client{},
|
||||||
upstreamTimeout: upstreamTimeout,
|
upstreamTimeout: upstreamTimeout,
|
||||||
reactivatePeriod: reactivatePeriod,
|
reactivatePeriod: reactivatePeriod,
|
||||||
@@ -52,11 +43,6 @@ func newUpstreamResolver(parentCTX context.Context) *upstreamResolver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *upstreamResolver) stop() {
|
|
||||||
log.Debugf("stoping serving DNS for upstreams %s", u.upstreamServers)
|
|
||||||
u.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServeDNS handles a DNS request
|
// ServeDNS handles a DNS request
|
||||||
func (u *upstreamResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
func (u *upstreamResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
|
||||||
defer u.checkUpstreamFails()
|
defer u.checkUpstreamFails()
|
||||||
@@ -121,57 +107,28 @@ func (u *upstreamResolver) checkUpstreamFails() {
|
|||||||
log.Warnf("upstream resolving is disabled for %v", reactivatePeriod)
|
log.Warnf("upstream resolving is disabled for %v", reactivatePeriod)
|
||||||
u.deactivate()
|
u.deactivate()
|
||||||
u.disabled = true
|
u.disabled = true
|
||||||
go u.waitUntilResponse()
|
go u.waitUntilReactivation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitUntilResponse retries, in an exponential interval, querying the upstream servers until it gets a positive response
|
// waitUntilReactivation reset fails counter and activates upstream resolving
|
||||||
func (u *upstreamResolver) waitUntilResponse() {
|
func (u *upstreamResolver) waitUntilReactivation() {
|
||||||
exponentialBackOff := &backoff.ExponentialBackOff{
|
timer := time.NewTimer(u.reactivatePeriod)
|
||||||
InitialInterval: 500 * time.Millisecond,
|
defer func() {
|
||||||
RandomizationFactor: 0.5,
|
if !timer.Stop() {
|
||||||
Multiplier: 1.1,
|
<-timer.C
|
||||||
MaxInterval: u.reactivatePeriod,
|
|
||||||
MaxElapsedTime: 0,
|
|
||||||
Stop: backoff.Stop,
|
|
||||||
Clock: backoff.SystemClock,
|
|
||||||
}
|
|
||||||
|
|
||||||
r := new(dns.Msg).SetQuestion("netbird.io.", dns.TypeA)
|
|
||||||
|
|
||||||
operation := func() error {
|
|
||||||
select {
|
|
||||||
case <-u.ctx.Done():
|
|
||||||
return backoff.Permanent(fmt.Errorf("exiting upstream retry loop for upstreams %s: parent context has been canceled", u.upstreamServers))
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
var err error
|
select {
|
||||||
for _, upstream := range u.upstreamServers {
|
case <-u.ctx.Done():
|
||||||
ctx, cancel := context.WithTimeout(u.ctx, u.upstreamTimeout)
|
|
||||||
_, _, err = u.upstreamClient.ExchangeContext(ctx, r, upstream)
|
|
||||||
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Tracef("checking connectivity with upstreams %s failed with error: %s. Retrying in %s", err, u.upstreamServers, exponentialBackOff.NextBackOff())
|
|
||||||
return fmt.Errorf("got an error from upstream check call")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := backoff.Retry(operation, exponentialBackOff)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(err)
|
|
||||||
return
|
return
|
||||||
|
case <-timer.C:
|
||||||
|
log.Info("upstream resolving is reactivated")
|
||||||
|
u.failsCount.Store(0)
|
||||||
|
u.reactivate()
|
||||||
|
u.disabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("upstreams %s are responsive again. Adding them back to system", u.upstreamServers)
|
|
||||||
u.failsCount.Store(0)
|
|
||||||
u.reactivate()
|
|
||||||
u.disabled = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isTimeout returns true if the given error is a network timeout error.
|
// isTimeout returns true if the given error is a network timeout error.
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/miekg/dns"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUpstreamResolver_ServeDNS(t *testing.T) {
|
func TestUpstreamResolver_ServeDNS(t *testing.T) {
|
||||||
@@ -107,29 +106,8 @@ func TestUpstreamResolver_ServeDNS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockUpstreamResolver struct {
|
|
||||||
r *dns.Msg
|
|
||||||
rtt time.Duration
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExchangeContext mock implementation of ExchangeContext from upstreamResolver
|
|
||||||
func (c mockUpstreamResolver) ExchangeContext(_ context.Context, _ *dns.Msg, _ string) (r *dns.Msg, rtt time.Duration, err error) {
|
|
||||||
return c.r, c.rtt, c.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpstreamResolver_DeactivationReactivation(t *testing.T) {
|
func TestUpstreamResolver_DeactivationReactivation(t *testing.T) {
|
||||||
resolver := &upstreamResolver{
|
resolver := newUpstreamResolver(context.TODO())
|
||||||
ctx: context.TODO(),
|
|
||||||
upstreamClient: &mockUpstreamResolver{
|
|
||||||
err: nil,
|
|
||||||
r: new(dns.Msg),
|
|
||||||
rtt: time.Millisecond,
|
|
||||||
},
|
|
||||||
upstreamTimeout: upstreamTimeout,
|
|
||||||
reactivatePeriod: reactivatePeriod,
|
|
||||||
failsTillDeact: failsTillDeact,
|
|
||||||
}
|
|
||||||
resolver.upstreamServers = []string{"0.0.0.0:-1"}
|
resolver.upstreamServers = []string{"0.0.0.0:-1"}
|
||||||
resolver.failsTillDeact = 0
|
resolver.failsTillDeact = 0
|
||||||
resolver.reactivatePeriod = time.Microsecond * 100
|
resolver.reactivatePeriod = time.Microsecond * 100
|
||||||
|
|||||||
@@ -17,10 +17,8 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/acl"
|
|
||||||
"github.com/netbirdio/netbird/client/internal/dns"
|
"github.com/netbirdio/netbird/client/internal/dns"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/internal/proxy"
|
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||||
nbssh "github.com/netbirdio/netbird/client/ssh"
|
nbssh "github.com/netbirdio/netbird/client/ssh"
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
@@ -115,7 +113,6 @@ type Engine struct {
|
|||||||
statusRecorder *peer.Status
|
statusRecorder *peer.Status
|
||||||
|
|
||||||
routeManager routemanager.Manager
|
routeManager routemanager.Manager
|
||||||
acl acl.Manager
|
|
||||||
|
|
||||||
dnsServer dns.Server
|
dnsServer dns.Server
|
||||||
}
|
}
|
||||||
@@ -182,44 +179,13 @@ func (e *Engine) Start() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to create pion's stdnet: %s", err)
|
log.Errorf("failed to create pion's stdnet: %s", err)
|
||||||
}
|
}
|
||||||
|
e.wgInterface, err = iface.NewWGIFace(wgIFaceName, wgAddr, iface.DefaultMTU, e.mobileDep.Routes, e.mobileDep.TunAdapter, transportNet)
|
||||||
e.wgInterface, err = iface.NewWGIFace(wgIFaceName, wgAddr, iface.DefaultMTU, e.mobileDep.TunAdapter, transportNet)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed creating wireguard interface instance %s: [%s]", wgIFaceName, err.Error())
|
log.Errorf("failed creating wireguard interface instance %s: [%s]", wgIFaceName, err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var routes []*route.Route
|
err = e.wgInterface.Create()
|
||||||
var dnsCfg *nbdns.Config
|
|
||||||
|
|
||||||
if runtime.GOOS == "android" {
|
|
||||||
routes, dnsCfg, err = e.readInitialSettings()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.dnsServer == nil {
|
|
||||||
// todo fix custom address
|
|
||||||
dnsServer, err := dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress, dnsCfg)
|
|
||||||
if err != nil {
|
|
||||||
e.close()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
e.dnsServer = dnsServer
|
|
||||||
}
|
|
||||||
|
|
||||||
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder, routes)
|
|
||||||
e.routeManager.SetRouteChangeListener(e.mobileDep.RouteListener)
|
|
||||||
|
|
||||||
if runtime.GOOS != "android" {
|
|
||||||
err = e.wgInterface.Create()
|
|
||||||
} else {
|
|
||||||
err = e.wgInterface.CreateOnMobile(iface.MobileIFaceArguments{
|
|
||||||
Routes: e.routeManager.InitialRouteRange(),
|
|
||||||
Dns: e.dnsServer.DnsIP(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed creating tunnel interface %s: [%s]", wgIFaceName, err.Error())
|
log.Errorf("failed creating tunnel interface %s: [%s]", wgIFaceName, err.Error())
|
||||||
e.close()
|
e.close()
|
||||||
@@ -253,16 +219,16 @@ func (e *Engine) Start() error {
|
|||||||
e.udpMux = mux
|
e.udpMux = mux
|
||||||
}
|
}
|
||||||
|
|
||||||
if acl, err := acl.Create(e.wgInterface); err != nil {
|
e.routeManager = routemanager.NewManager(e.ctx, e.config.WgPrivateKey.PublicKey().String(), e.wgInterface, e.statusRecorder)
|
||||||
log.Errorf("failed to create ACL manager, policy will not work: %s", err.Error())
|
|
||||||
} else {
|
|
||||||
e.acl = acl
|
|
||||||
}
|
|
||||||
|
|
||||||
err = e.dnsServer.Initialize()
|
if e.dnsServer == nil {
|
||||||
if err != nil {
|
// todo fix custom address
|
||||||
e.close()
|
dnsServer, err := dns.NewDefaultServer(e.ctx, e.wgInterface, e.config.CustomDNSAddress)
|
||||||
return err
|
if err != nil {
|
||||||
|
e.close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.dnsServer = dnsServer
|
||||||
}
|
}
|
||||||
|
|
||||||
e.receiveSignalEvents()
|
e.receiveSignalEvents()
|
||||||
@@ -280,7 +246,7 @@ func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
|||||||
for _, p := range peersUpdate {
|
for _, p := range peersUpdate {
|
||||||
peerPubKey := p.GetWgPubKey()
|
peerPubKey := p.GetWgPubKey()
|
||||||
if peerConn, ok := e.peerConns[peerPubKey]; ok {
|
if peerConn, ok := e.peerConns[peerPubKey]; ok {
|
||||||
if peerConn.GetConf().ProxyConfig.AllowedIps != strings.Join(p.AllowedIps, ",") {
|
if peerConn.WgConfig().AllowedIps != strings.Join(p.AllowedIps, ",") {
|
||||||
modified = append(modified, p)
|
modified = append(modified, p)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -605,7 +571,6 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
// cleanup request, most likely our peer has been deleted
|
// cleanup request, most likely our peer has been deleted
|
||||||
if networkMap.GetRemotePeersIsEmpty() {
|
if networkMap.GetRemotePeersIsEmpty() {
|
||||||
err := e.removeAllPeers()
|
err := e.removeAllPeers()
|
||||||
e.statusRecorder.FinishPeerListModifications()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -625,8 +590,6 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
e.statusRecorder.FinishPeerListModifications()
|
|
||||||
|
|
||||||
// update SSHServer by adding remote peer SSH keys
|
// update SSHServer by adding remote peer SSH keys
|
||||||
if !isNil(e.sshServer) {
|
if !isNil(e.sshServer) {
|
||||||
for _, config := range networkMap.GetRemotePeers() {
|
for _, config := range networkMap.GetRemotePeers() {
|
||||||
@@ -658,9 +621,6 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
log.Errorf("failed to update dns server, err: %v", err)
|
log.Errorf("failed to update dns server, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.acl != nil {
|
|
||||||
e.acl.ApplyFiltering(networkMap)
|
|
||||||
}
|
|
||||||
e.networkSerial = serial
|
e.networkSerial = serial
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -762,13 +722,17 @@ func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig) error {
|
|||||||
}
|
}
|
||||||
e.peerConns[peerKey] = conn
|
e.peerConns[peerKey] = conn
|
||||||
|
|
||||||
err = e.statusRecorder.AddPeer(peerKey, peerConfig.Fqdn)
|
err = e.statusRecorder.AddPeer(peerKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("error adding peer %s to status recorder, got error: %v", peerKey, err)
|
log.Warnf("error adding peer %s to status recorder, got error: %v", peerKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
go e.connWorker(conn, peerKey)
|
go e.connWorker(conn, peerKey)
|
||||||
}
|
}
|
||||||
|
err := e.statusRecorder.UpdatePeerFQDN(peerKey, peerConfig.Fqdn)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("error updating peer's %s fqdn in the status recorder, got error: %v", peerKey, err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -793,9 +757,7 @@ func (e *Engine) connWorker(conn *peer.Conn, peerKey string) {
|
|||||||
|
|
||||||
// we might have received new STUN and TURN servers meanwhile, so update them
|
// we might have received new STUN and TURN servers meanwhile, so update them
|
||||||
e.syncMsgMux.Lock()
|
e.syncMsgMux.Lock()
|
||||||
conf := conn.GetConf()
|
conn.UpdateStunTurn(append(e.STUNs, e.TURNs...))
|
||||||
conf.StunTurn = append(e.STUNs, e.TURNs...)
|
|
||||||
conn.UpdateConf(conf)
|
|
||||||
e.syncMsgMux.Unlock()
|
e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
err := conn.Open()
|
err := conn.Open()
|
||||||
@@ -811,22 +773,22 @@ func (e *Engine) connWorker(conn *peer.Conn, peerKey string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) peerExists(peerKey string) bool {
|
func (e Engine) peerExists(peerKey string) bool {
|
||||||
e.syncMsgMux.Lock()
|
e.syncMsgMux.Lock()
|
||||||
defer e.syncMsgMux.Unlock()
|
defer e.syncMsgMux.Unlock()
|
||||||
_, ok := e.peerConns[peerKey]
|
_, ok := e.peerConns[peerKey]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, error) {
|
func (e Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, error) {
|
||||||
log.Debugf("creating peer connection %s", pubKey)
|
log.Debugf("creating peer connection %s", pubKey)
|
||||||
var stunTurn []*ice.URL
|
var stunTurn []*ice.URL
|
||||||
stunTurn = append(stunTurn, e.STUNs...)
|
stunTurn = append(stunTurn, e.STUNs...)
|
||||||
stunTurn = append(stunTurn, e.TURNs...)
|
stunTurn = append(stunTurn, e.TURNs...)
|
||||||
|
|
||||||
proxyConfig := proxy.Config{
|
wgConfig := peer.WgConfig{
|
||||||
RemoteKey: pubKey,
|
RemoteKey: pubKey,
|
||||||
WgListenAddr: fmt.Sprintf("127.0.0.1:%d", e.config.WgPort),
|
WgListenPort: e.config.WgPort,
|
||||||
WgInterface: e.wgInterface,
|
WgInterface: e.wgInterface,
|
||||||
AllowedIps: allowedIPs,
|
AllowedIps: allowedIPs,
|
||||||
PreSharedKey: e.config.PreSharedKey,
|
PreSharedKey: e.config.PreSharedKey,
|
||||||
@@ -843,7 +805,7 @@ func (e *Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, e
|
|||||||
Timeout: timeout,
|
Timeout: timeout,
|
||||||
UDPMux: e.udpMux.UDPMuxDefault,
|
UDPMux: e.udpMux.UDPMuxDefault,
|
||||||
UDPMuxSrflx: e.udpMux,
|
UDPMuxSrflx: e.udpMux,
|
||||||
ProxyConfig: proxyConfig,
|
WgConfig: wgConfig,
|
||||||
LocalWgPort: e.config.WgPort,
|
LocalWgPort: e.config.WgPort,
|
||||||
NATExternalIPs: e.parseNATExternalIPMappings(),
|
NATExternalIPs: e.parseNATExternalIPMappings(),
|
||||||
UserspaceBind: e.wgInterface.IsUserspaceBind(),
|
UserspaceBind: e.wgInterface.IsUserspaceBind(),
|
||||||
@@ -888,6 +850,7 @@ func (e *Engine) receiveSignalEvents() {
|
|||||||
err := e.signal.Receive(func(msg *sProto.Message) error {
|
err := e.signal.Receive(func(msg *sProto.Message) error {
|
||||||
e.syncMsgMux.Lock()
|
e.syncMsgMux.Lock()
|
||||||
defer e.syncMsgMux.Unlock()
|
defer e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
conn := e.peerConns[msg.Key]
|
conn := e.peerConns[msg.Key]
|
||||||
if conn == nil {
|
if conn == nil {
|
||||||
return fmt.Errorf("wrongly addressed message %s", msg.Key)
|
return fmt.Errorf("wrongly addressed message %s", msg.Key)
|
||||||
@@ -1039,19 +1002,6 @@ func (e *Engine) close() {
|
|||||||
e.dnsServer.Stop()
|
e.dnsServer.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.acl != nil {
|
|
||||||
e.acl.Stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) {
|
|
||||||
netMap, err := e.mgmClient.GetNetworkMap()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
routes := toRoutes(netMap.GetRoutes())
|
|
||||||
dnsCfg := toDNSConfig(netMap.GetDNSConfig())
|
|
||||||
return routes, &dnsCfg, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func findIPFromInterfaceName(ifaceName string) (net.IP, error) {
|
func findIPFromInterfaceName(ifaceName string) (net.IP, error) {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package internal
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/netbirdio/netbird/iface/bind"
|
||||||
|
"github.com/pion/transport/v2/stdnet"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
@@ -13,7 +15,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/transport/v2/stdnet"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -28,7 +29,6 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/system"
|
"github.com/netbirdio/netbird/client/system"
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
"github.com/netbirdio/netbird/iface/bind"
|
|
||||||
mgmt "github.com/netbirdio/netbird/management/client"
|
mgmt "github.com/netbirdio/netbird/management/client"
|
||||||
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
@@ -213,11 +213,11 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", iface.DefaultMTU, nil, newNet)
|
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", iface.DefaultMTU, nil, nil, newNet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
engine.routeManager = routemanager.NewManager(ctx, key.PublicKey().String(), engine.wgInterface, engine.statusRecorder, nil)
|
engine.routeManager = routemanager.NewManager(ctx, key.PublicKey().String(), engine.wgInterface, engine.statusRecorder)
|
||||||
engine.dnsServer = &dns.MockServer{
|
engine.dnsServer = &dns.MockServer{
|
||||||
UpdateDNSServerFunc: func(serial uint64, update nbdns.Config) error { return nil },
|
UpdateDNSServerFunc: func(serial uint64, update nbdns.Config) error { return nil },
|
||||||
}
|
}
|
||||||
@@ -567,7 +567,7 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU, nil, newNet)
|
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU, nil, nil, newNet)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
input := struct {
|
input := struct {
|
||||||
inputSerial uint64
|
inputSerial uint64
|
||||||
@@ -736,7 +736,7 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU, nil, newNet)
|
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU, nil, nil, newNet)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
mockRouteManager := &routemanager.MockManager{
|
mockRouteManager := &routemanager.MockManager{
|
||||||
@@ -1039,7 +1039,7 @@ 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.NewFileStore(config.Datadir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
|
log.Fatalf("failed creating a store: %s: %v", config.Datadir, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"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/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
)
|
)
|
||||||
@@ -10,5 +9,5 @@ import (
|
|||||||
type MobileDependency struct {
|
type MobileDependency struct {
|
||||||
TunAdapter iface.TunAdapter
|
TunAdapter iface.TunAdapter
|
||||||
IFaceDiscover stdnet.ExternalIFaceDiscover
|
IFaceDiscover stdnet.ExternalIFaceDiscover
|
||||||
RouteListener routemanager.RouteListener
|
Routes []string
|
||||||
}
|
}
|
||||||
|
|||||||
29
client/internal/mobile_dependency_android.go
Normal file
29
client/internal/mobile_dependency_android.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
mgm "github.com/netbirdio/netbird/management/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newMobileDependency(tunAdapter iface.TunAdapter, ifaceDiscover stdnet.ExternalIFaceDiscover, mgmClient *mgm.GrpcClient) (MobileDependency, error) {
|
||||||
|
md := MobileDependency{
|
||||||
|
TunAdapter: tunAdapter,
|
||||||
|
IFaceDiscover: ifaceDiscover,
|
||||||
|
}
|
||||||
|
err := md.readMap(mgmClient)
|
||||||
|
return md, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *MobileDependency) readMap(mgmClient *mgm.GrpcClient) error {
|
||||||
|
routes, err := mgmClient.GetRoutes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Routes = make([]string, len(routes))
|
||||||
|
for i, r := range routes {
|
||||||
|
d.Routes[i] = r.GetNetwork()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
13
client/internal/mobile_dependency_nonandroid.go
Normal file
13
client/internal/mobile_dependency_nonandroid.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
//go:build !android
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
mgm "github.com/netbirdio/netbird/management/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newMobileDependency(tunAdapter iface.TunAdapter, ifaceDiscover stdnet.ExternalIFaceDiscover, mgmClient *mgm.GrpcClient) (MobileDependency, error) {
|
||||||
|
return MobileDependency{}, nil
|
||||||
|
}
|
||||||
@@ -4,14 +4,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/ice/v2"
|
"github.com/pion/ice/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/proxy"
|
|
||||||
"github.com/netbirdio/netbird/client/internal/stdnet"
|
"github.com/netbirdio/netbird/client/internal/stdnet"
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
"github.com/netbirdio/netbird/iface/bind"
|
"github.com/netbirdio/netbird/iface/bind"
|
||||||
@@ -21,10 +23,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
envICEKeepAliveIntervalSec = "NB_ICE_KEEP_ALIVE_INTERVAL_SEC"
|
||||||
|
envICEDisconnectedTimeoutSec = "NB_ICE_DISCONNECTED_TIMEOUT_SEC"
|
||||||
|
|
||||||
iceKeepAliveDefault = 4 * time.Second
|
iceKeepAliveDefault = 4 * time.Second
|
||||||
iceDisconnectedTimeoutDefault = 6 * time.Second
|
iceDisconnectedTimeoutDefault = 6 * time.Second
|
||||||
|
|
||||||
|
defaultWgKeepAlive = 25 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type WgConfig struct {
|
||||||
|
WgListenPort int
|
||||||
|
RemoteKey string
|
||||||
|
WgInterface *iface.WGIface
|
||||||
|
AllowedIps string
|
||||||
|
PreSharedKey *wgtypes.Key
|
||||||
|
}
|
||||||
|
|
||||||
// ConnConfig is a peer Connection configuration
|
// ConnConfig is a peer Connection configuration
|
||||||
type ConnConfig struct {
|
type ConnConfig struct {
|
||||||
|
|
||||||
@@ -43,7 +58,7 @@ type ConnConfig struct {
|
|||||||
|
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
|
|
||||||
ProxyConfig proxy.Config
|
WgConfig WgConfig
|
||||||
|
|
||||||
UDPMux ice.UDPMux
|
UDPMux ice.UDPMux
|
||||||
UDPMuxSrflx ice.UniversalUDPMux
|
UDPMuxSrflx ice.UniversalUDPMux
|
||||||
@@ -98,7 +113,7 @@ type Conn struct {
|
|||||||
|
|
||||||
statusRecorder *Status
|
statusRecorder *Status
|
||||||
|
|
||||||
proxy proxy.Proxy
|
proxy *WireGuardProxy
|
||||||
remoteModeCh chan ModeMessage
|
remoteModeCh chan ModeMessage
|
||||||
meta meta
|
meta meta
|
||||||
|
|
||||||
@@ -122,9 +137,14 @@ func (conn *Conn) GetConf() ConnConfig {
|
|||||||
return conn.config
|
return conn.config
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateConf updates the connection config
|
// WgConfig returns the WireGuard config
|
||||||
func (conn *Conn) UpdateConf(conf ConnConfig) {
|
func (conn *Conn) WgConfig() WgConfig {
|
||||||
conn.config = conf
|
return conn.config.WgConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateStunTurn update the turn and stun addresses
|
||||||
|
func (conn *Conn) UpdateStunTurn(turnStun []*ice.URL) {
|
||||||
|
conn.config.StunTurn = turnStun
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConn creates a new not opened Conn to the remote peer.
|
// NewConn creates a new not opened Conn to the remote peer.
|
||||||
@@ -156,14 +176,13 @@ func (conn *Conn) reCreateAgent() error {
|
|||||||
log.Errorf("failed to create pion's stdnet: %s", err)
|
log.Errorf("failed to create pion's stdnet: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
iceKeepAlive := iceKeepAlive()
|
iceKeepAlive, iceDisconnectedTimeout := readICEAgentConfigProperties()
|
||||||
iceDisconnectedTimeout := iceDisconnectedTimeout()
|
|
||||||
|
|
||||||
agentConfig := &ice.AgentConfig{
|
agentConfig := &ice.AgentConfig{
|
||||||
MulticastDNSMode: ice.MulticastDNSModeDisabled,
|
MulticastDNSMode: ice.MulticastDNSModeDisabled,
|
||||||
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4, ice.NetworkTypeUDP6},
|
NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4, ice.NetworkTypeUDP6},
|
||||||
Urls: conn.config.StunTurn,
|
Urls: conn.config.StunTurn,
|
||||||
CandidateTypes: conn.candidateTypes(),
|
CandidateTypes: []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay},
|
||||||
FailedTimeout: &failedTimeout,
|
FailedTimeout: &failedTimeout,
|
||||||
InterfaceFilter: stdnet.InterfaceFilter(conn.config.InterfaceBlackList),
|
InterfaceFilter: stdnet.InterfaceFilter(conn.config.InterfaceBlackList),
|
||||||
UDPMux: conn.config.UDPMux,
|
UDPMux: conn.config.UDPMux,
|
||||||
@@ -202,11 +221,32 @@ func (conn *Conn) reCreateAgent() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) candidateTypes() []ice.CandidateType {
|
func readICEAgentConfigProperties() (time.Duration, time.Duration) {
|
||||||
if hasICEForceRelayConn() {
|
iceKeepAlive := iceKeepAliveDefault
|
||||||
return []ice.CandidateType{ice.CandidateTypeRelay}
|
iceDisconnectedTimeout := iceDisconnectedTimeoutDefault
|
||||||
|
|
||||||
|
keepAliveEnv := os.Getenv(envICEKeepAliveIntervalSec)
|
||||||
|
if keepAliveEnv != "" {
|
||||||
|
log.Debugf("setting ICE keep alive interval to %s seconds", keepAliveEnv)
|
||||||
|
keepAliveEnvSec, err := strconv.Atoi(keepAliveEnv)
|
||||||
|
if err == nil {
|
||||||
|
iceKeepAlive = time.Duration(keepAliveEnvSec) * time.Second
|
||||||
|
} else {
|
||||||
|
log.Warnf("invalid value %s set for %s, using default %v", keepAliveEnv, envICEKeepAliveIntervalSec, iceKeepAlive)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay}
|
|
||||||
|
disconnectedTimeoutEnv := os.Getenv(envICEDisconnectedTimeoutSec)
|
||||||
|
if disconnectedTimeoutEnv != "" {
|
||||||
|
log.Debugf("setting ICE disconnected timeout to %s seconds", disconnectedTimeoutEnv)
|
||||||
|
disconnectedTimeoutSec, err := strconv.Atoi(disconnectedTimeoutEnv)
|
||||||
|
if err == nil {
|
||||||
|
iceDisconnectedTimeout = time.Duration(disconnectedTimeoutSec) * time.Second
|
||||||
|
} else {
|
||||||
|
log.Warnf("invalid value %s set for %s, using default %v", disconnectedTimeoutEnv, envICEDisconnectedTimeoutSec, iceDisconnectedTimeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iceKeepAlive, iceDisconnectedTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open opens connection to the remote peer starting ICE candidate gathering process.
|
// Open opens connection to the remote peer starting ICE candidate gathering process.
|
||||||
@@ -215,12 +255,12 @@ func (conn *Conn) candidateTypes() []ice.CandidateType {
|
|||||||
func (conn *Conn) Open() error {
|
func (conn *Conn) Open() error {
|
||||||
log.Debugf("trying to connect to peer %s", conn.config.Key)
|
log.Debugf("trying to connect to peer %s", conn.config.Key)
|
||||||
|
|
||||||
peerState := State{PubKey: conn.config.Key}
|
peerState := State{
|
||||||
|
PubKey: conn.config.Key,
|
||||||
peerState.IP = strings.Split(conn.config.ProxyConfig.AllowedIps, "/")[0]
|
IP: strings.Split(conn.config.WgConfig.AllowedIps, "/")[0],
|
||||||
peerState.ConnStatusUpdate = time.Now()
|
ConnStatusUpdate: time.Now(),
|
||||||
peerState.ConnStatus = conn.status
|
ConnStatus: conn.status,
|
||||||
|
}
|
||||||
err := conn.statusRecorder.UpdatePeerState(peerState)
|
err := conn.statusRecorder.UpdatePeerState(peerState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("erro while updating the state of peer %s,err: %v", conn.config.Key, err)
|
log.Warnf("erro while updating the state of peer %s,err: %v", conn.config.Key, err)
|
||||||
@@ -275,10 +315,11 @@ func (conn *Conn) Open() error {
|
|||||||
defer conn.notifyDisconnected()
|
defer conn.notifyDisconnected()
|
||||||
conn.mu.Unlock()
|
conn.mu.Unlock()
|
||||||
|
|
||||||
peerState = State{PubKey: conn.config.Key}
|
peerState = State{
|
||||||
|
PubKey: conn.config.Key,
|
||||||
peerState.ConnStatus = conn.status
|
ConnStatus: conn.status,
|
||||||
peerState.ConnStatusUpdate = time.Now()
|
ConnStatusUpdate: time.Now(),
|
||||||
|
}
|
||||||
err = conn.statusRecorder.UpdatePeerState(peerState)
|
err = conn.statusRecorder.UpdatePeerState(peerState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("erro while updating the state of peer %s,err: %v", conn.config.Key, err)
|
log.Warnf("erro while updating the state of peer %s,err: %v", conn.config.Key, err)
|
||||||
@@ -309,19 +350,12 @@ func (conn *Conn) Open() error {
|
|||||||
remoteWgPort = remoteOfferAnswer.WgListenPort
|
remoteWgPort = remoteOfferAnswer.WgListenPort
|
||||||
}
|
}
|
||||||
// the ice connection has been established successfully so we are ready to start the proxy
|
// the ice connection has been established successfully so we are ready to start the proxy
|
||||||
err = conn.startProxy(remoteConn, remoteWgPort)
|
remoteAddr, err := conn.configureConnection(remoteConn, remoteWgPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if conn.proxy.Type() == proxy.TypeDirectNoProxy {
|
log.Infof("connected to peer %s, proxy: %v, remote address: %s", conn.config.Key, conn.proxy != nil, remoteAddr.String())
|
||||||
host, _, _ := net.SplitHostPort(remoteConn.LocalAddr().String())
|
|
||||||
rhost, _, _ := net.SplitHostPort(remoteConn.RemoteAddr().String())
|
|
||||||
// direct Wireguard connection
|
|
||||||
log.Infof("directly connected to peer %s [laddr <-> raddr] [%s:%d <-> %s:%d]", conn.config.Key, host, conn.config.LocalWgPort, rhost, remoteWgPort)
|
|
||||||
} else {
|
|
||||||
log.Infof("connected to peer %s [laddr <-> raddr] [%s <-> %s]", conn.config.Key, remoteConn.LocalAddr().String(), remoteConn.RemoteAddr().String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait until connection disconnected or has been closed externally (upper layer, e.g. engine)
|
// wait until connection disconnected or has been closed externally (upper layer, e.g. engine)
|
||||||
select {
|
select {
|
||||||
@@ -338,54 +372,58 @@ func isRelayCandidate(candidate ice.Candidate) bool {
|
|||||||
return candidate.Type() == ice.CandidateTypeRelay
|
return candidate.Type() == ice.CandidateTypeRelay
|
||||||
}
|
}
|
||||||
|
|
||||||
// startProxy starts proxying traffic from/to local Wireguard and sets connection status to StatusConnected
|
// configureConnection starts proxying traffic from/to local Wireguard and sets connection status to StatusConnected
|
||||||
func (conn *Conn) startProxy(remoteConn net.Conn, remoteWgPort int) error {
|
func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int) (net.Addr, error) {
|
||||||
conn.mu.Lock()
|
conn.mu.Lock()
|
||||||
defer conn.mu.Unlock()
|
defer conn.mu.Unlock()
|
||||||
|
|
||||||
var pair *ice.CandidatePair
|
|
||||||
pair, err := conn.agent.GetSelectedCandidatePair()
|
pair, err := conn.agent.GetSelectedCandidatePair()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peerState := State{PubKey: conn.config.Key}
|
var endpoint net.Addr
|
||||||
p := conn.getProxy(pair, remoteWgPort)
|
if isRelayCandidate(pair.Local) {
|
||||||
conn.proxy = p
|
conn.proxy = NewWireGuardProxy(conn.config.WgConfig.WgListenPort, conn.config.WgConfig.RemoteKey, remoteConn)
|
||||||
err = p.Start(remoteConn)
|
endpoint, err = conn.proxy.Start()
|
||||||
|
if err != nil {
|
||||||
|
conn.proxy = nil
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// To support old version's with direct mode we attempt to punch an additional role with the remote wireguard port
|
||||||
|
go conn.punchRemoteWGPort(pair, remoteWgPort)
|
||||||
|
endpoint = remoteConn.RemoteAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conn.config.WgConfig.WgInterface.UpdatePeer(conn.config.WgConfig.RemoteKey, conn.config.WgConfig.AllowedIps, defaultWgKeepAlive, endpoint, conn.config.WgConfig.PreSharedKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
if conn.proxy != nil {
|
||||||
|
_ = conn.proxy.Close()
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.status = StatusConnected
|
conn.status = StatusConnected
|
||||||
|
|
||||||
peerState.ConnStatus = conn.status
|
peerState := State{
|
||||||
peerState.ConnStatusUpdate = time.Now()
|
PubKey: conn.config.Key,
|
||||||
peerState.LocalIceCandidateType = pair.Local.Type().String()
|
ConnStatus: conn.status,
|
||||||
peerState.RemoteIceCandidateType = pair.Remote.Type().String()
|
ConnStatusUpdate: time.Now(),
|
||||||
|
LocalIceCandidateType: pair.Local.Type().String(),
|
||||||
|
RemoteIceCandidateType: pair.Remote.Type().String(),
|
||||||
|
Direct: conn.proxy == nil,
|
||||||
|
}
|
||||||
if pair.Local.Type() == ice.CandidateTypeRelay || pair.Remote.Type() == ice.CandidateTypeRelay {
|
if pair.Local.Type() == ice.CandidateTypeRelay || pair.Remote.Type() == ice.CandidateTypeRelay {
|
||||||
peerState.Relayed = true
|
peerState.Relayed = true
|
||||||
}
|
}
|
||||||
peerState.Direct = p.Type() == proxy.TypeDirectNoProxy || p.Type() == proxy.TypeNoProxy
|
|
||||||
|
|
||||||
err = conn.statusRecorder.UpdatePeerState(peerState)
|
err = conn.statusRecorder.UpdatePeerState(peerState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("unable to save peer's state, got error: %v", err)
|
log.Warnf("unable to save peer's state, got error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return endpoint, nil
|
||||||
}
|
|
||||||
|
|
||||||
// todo rename this method and the proxy package to something more appropriate
|
|
||||||
func (conn *Conn) getProxy(pair *ice.CandidatePair, remoteWgPort int) proxy.Proxy {
|
|
||||||
if isRelayCandidate(pair.Local) {
|
|
||||||
return proxy.NewWireGuardProxy(conn.config.ProxyConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// To support old version's with direct mode we attempt to punch an additional role with the remote wireguard port
|
|
||||||
go conn.punchRemoteWGPort(pair, remoteWgPort)
|
|
||||||
|
|
||||||
return proxy.NewNoProxy(conn.config.ProxyConfig)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conn *Conn) punchRemoteWGPort(pair *ice.CandidatePair, remoteWgPort int) {
|
func (conn *Conn) punchRemoteWGPort(pair *ice.CandidatePair, remoteWgPort int) {
|
||||||
@@ -414,20 +452,22 @@ func (conn *Conn) cleanup() error {
|
|||||||
conn.mu.Lock()
|
conn.mu.Lock()
|
||||||
defer conn.mu.Unlock()
|
defer conn.mu.Unlock()
|
||||||
|
|
||||||
|
var err1, err2, err3 error
|
||||||
if conn.agent != nil {
|
if conn.agent != nil {
|
||||||
err := conn.agent.Close()
|
err1 = conn.agent.Close()
|
||||||
if err != nil {
|
if err1 == nil {
|
||||||
return err
|
conn.agent = nil
|
||||||
}
|
}
|
||||||
conn.agent = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: is it problem if we try to remove a peer what is never existed?
|
||||||
|
err2 = conn.config.WgConfig.WgInterface.RemovePeer(conn.config.WgConfig.RemoteKey)
|
||||||
|
|
||||||
if conn.proxy != nil {
|
if conn.proxy != nil {
|
||||||
err := conn.proxy.Close()
|
err3 = conn.proxy.Close()
|
||||||
if err != nil {
|
if err3 != nil {
|
||||||
return err
|
conn.proxy = nil
|
||||||
}
|
}
|
||||||
conn.proxy = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if conn.notifyDisconnected != nil {
|
if conn.notifyDisconnected != nil {
|
||||||
@@ -437,10 +477,11 @@ func (conn *Conn) cleanup() error {
|
|||||||
|
|
||||||
conn.status = StatusDisconnected
|
conn.status = StatusDisconnected
|
||||||
|
|
||||||
peerState := State{PubKey: conn.config.Key}
|
peerState := State{
|
||||||
peerState.ConnStatus = conn.status
|
PubKey: conn.config.Key,
|
||||||
peerState.ConnStatusUpdate = time.Now()
|
ConnStatus: conn.status,
|
||||||
|
ConnStatusUpdate: time.Now(),
|
||||||
|
}
|
||||||
err := conn.statusRecorder.UpdatePeerState(peerState)
|
err := conn.statusRecorder.UpdatePeerState(peerState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// pretty common error because by that time Engine can already remove the peer and status won't be available.
|
// pretty common error because by that time Engine can already remove the peer and status won't be available.
|
||||||
@@ -449,8 +490,13 @@ func (conn *Conn) cleanup() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("cleaned up connection to peer %s", conn.config.Key)
|
log.Debugf("cleaned up connection to peer %s", conn.config.Key)
|
||||||
|
if err1 != nil {
|
||||||
return nil
|
return err1
|
||||||
|
}
|
||||||
|
if err2 != nil {
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
return err3
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSignalOffer sets a handler function to be triggered by Conn when a new connection offer has to be signalled to the remote peer
|
// SetSignalOffer sets a handler function to be triggered by Conn when a new connection offer has to be signalled to the remote peer
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
package peer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
envICEKeepAliveIntervalSec = "NB_ICE_KEEP_ALIVE_INTERVAL_SEC"
|
|
||||||
envICEDisconnectedTimeoutSec = "NB_ICE_DISCONNECTED_TIMEOUT_SEC"
|
|
||||||
envICEForceRelayConn = "NB_ICE_FORCE_RELAY_CONN"
|
|
||||||
)
|
|
||||||
|
|
||||||
func iceKeepAlive() time.Duration {
|
|
||||||
keepAliveEnv := os.Getenv(envICEKeepAliveIntervalSec)
|
|
||||||
if keepAliveEnv == "" {
|
|
||||||
return iceKeepAliveDefault
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("setting ICE keep alive interval to %s seconds", keepAliveEnv)
|
|
||||||
keepAliveEnvSec, err := strconv.Atoi(keepAliveEnv)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("invalid value %s set for %s, using default %v", keepAliveEnv, envICEKeepAliveIntervalSec, iceKeepAliveDefault)
|
|
||||||
return iceKeepAliveDefault
|
|
||||||
}
|
|
||||||
|
|
||||||
return time.Duration(keepAliveEnvSec) * time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
func iceDisconnectedTimeout() time.Duration {
|
|
||||||
disconnectedTimeoutEnv := os.Getenv(envICEDisconnectedTimeoutSec)
|
|
||||||
if disconnectedTimeoutEnv == "" {
|
|
||||||
return iceDisconnectedTimeoutDefault
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("setting ICE disconnected timeout to %s seconds", disconnectedTimeoutEnv)
|
|
||||||
disconnectedTimeoutSec, err := strconv.Atoi(disconnectedTimeoutEnv)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("invalid value %s set for %s, using default %v", disconnectedTimeoutEnv, envICEDisconnectedTimeoutSec, iceDisconnectedTimeoutDefault)
|
|
||||||
return iceDisconnectedTimeoutDefault
|
|
||||||
}
|
|
||||||
|
|
||||||
return time.Duration(disconnectedTimeoutSec) * time.Second
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasICEForceRelayConn() bool {
|
|
||||||
disconnectedTimeoutEnv := os.Getenv(envICEForceRelayConn)
|
|
||||||
return strings.ToLower(disconnectedTimeoutEnv) == "true"
|
|
||||||
}
|
|
||||||
@@ -59,11 +59,6 @@ type Status struct {
|
|||||||
mgmAddress string
|
mgmAddress string
|
||||||
signalAddress string
|
signalAddress string
|
||||||
notifier *notifier
|
notifier *notifier
|
||||||
|
|
||||||
// To reduce the number of notification invocation this bool will be true when need to call the notification
|
|
||||||
// Some Peer actions mostly used by in a batch when the network map has been synchronized. In these type of events
|
|
||||||
// set to true this variable and at the end of the processing we will reset it by the FinishPeerListModifications()
|
|
||||||
peerListChangedForNotification bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRecorder returns a new Status instance
|
// NewRecorder returns a new Status instance
|
||||||
@@ -83,13 +78,11 @@ func (d *Status) ReplaceOfflinePeers(replacement []State) {
|
|||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
d.offlinePeers = make([]State, len(replacement))
|
d.offlinePeers = make([]State, len(replacement))
|
||||||
copy(d.offlinePeers, replacement)
|
copy(d.offlinePeers, replacement)
|
||||||
|
d.notifyPeerListChanged()
|
||||||
// todo we should set to true in case if the list changed only
|
|
||||||
d.peerListChangedForNotification = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPeer adds peer to Daemon status map
|
// AddPeer adds peer to Daemon status map
|
||||||
func (d *Status) AddPeer(peerPubKey string, fqdn string) error {
|
func (d *Status) AddPeer(peerPubKey string) error {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
@@ -97,12 +90,7 @@ func (d *Status) AddPeer(peerPubKey string, fqdn string) error {
|
|||||||
if ok {
|
if ok {
|
||||||
return errors.New("peer already exist")
|
return errors.New("peer already exist")
|
||||||
}
|
}
|
||||||
d.peers[peerPubKey] = State{
|
d.peers[peerPubKey] = State{PubKey: peerPubKey, ConnStatus: StatusDisconnected}
|
||||||
PubKey: peerPubKey,
|
|
||||||
ConnStatus: StatusDisconnected,
|
|
||||||
FQDN: fqdn,
|
|
||||||
}
|
|
||||||
d.peerListChangedForNotification = true
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,13 +112,13 @@ func (d *Status) RemovePeer(peerPubKey string) error {
|
|||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
_, ok := d.peers[peerPubKey]
|
_, ok := d.peers[peerPubKey]
|
||||||
if !ok {
|
if ok {
|
||||||
return errors.New("no peer with to remove")
|
delete(d.peers, peerPubKey)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(d.peers, peerPubKey)
|
d.notifyPeerListChanged()
|
||||||
d.peerListChangedForNotification = true
|
return errors.New("no peer with to remove")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePeerState updates peer status
|
// UpdatePeerState updates peer status
|
||||||
@@ -147,8 +135,6 @@ func (d *Status) UpdatePeerState(receivedState State) error {
|
|||||||
peerState.IP = receivedState.IP
|
peerState.IP = receivedState.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
skipNotification := shouldSkipNotify(receivedState, peerState)
|
|
||||||
|
|
||||||
if receivedState.ConnStatus != peerState.ConnStatus {
|
if receivedState.ConnStatus != peerState.ConnStatus {
|
||||||
peerState.ConnStatus = receivedState.ConnStatus
|
peerState.ConnStatus = receivedState.ConnStatus
|
||||||
peerState.ConnStatusUpdate = receivedState.ConnStatusUpdate
|
peerState.ConnStatusUpdate = receivedState.ConnStatusUpdate
|
||||||
@@ -160,10 +146,6 @@ func (d *Status) UpdatePeerState(receivedState State) error {
|
|||||||
|
|
||||||
d.peers[receivedState.PubKey] = peerState
|
d.peers[receivedState.PubKey] = peerState
|
||||||
|
|
||||||
if skipNotification {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ch, found := d.changeNotify[receivedState.PubKey]
|
ch, found := d.changeNotify[receivedState.PubKey]
|
||||||
if found && ch != nil {
|
if found && ch != nil {
|
||||||
close(ch)
|
close(ch)
|
||||||
@@ -174,19 +156,6 @@ func (d *Status) UpdatePeerState(receivedState State) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldSkipNotify(new, curr State) bool {
|
|
||||||
switch {
|
|
||||||
case new.ConnStatus == StatusConnecting:
|
|
||||||
return true
|
|
||||||
case new.ConnStatus == StatusDisconnected && curr.ConnStatus == StatusConnecting:
|
|
||||||
return true
|
|
||||||
case new.ConnStatus == StatusDisconnected && curr.ConnStatus == StatusDisconnected:
|
|
||||||
return curr.IP != ""
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePeerFQDN update peer's state fqdn only
|
// UpdatePeerFQDN update peer's state fqdn only
|
||||||
func (d *Status) UpdatePeerFQDN(peerPubKey, fqdn string) error {
|
func (d *Status) UpdatePeerFQDN(peerPubKey, fqdn string) error {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
@@ -200,21 +169,8 @@ func (d *Status) UpdatePeerFQDN(peerPubKey, fqdn string) error {
|
|||||||
peerState.FQDN = fqdn
|
peerState.FQDN = fqdn
|
||||||
d.peers[peerPubKey] = peerState
|
d.peers[peerPubKey] = peerState
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FinishPeerListModifications this event invoke the notification
|
|
||||||
func (d *Status) FinishPeerListModifications() {
|
|
||||||
d.mux.Lock()
|
|
||||||
|
|
||||||
if !d.peerListChangedForNotification {
|
|
||||||
d.mux.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d.peerListChangedForNotification = false
|
|
||||||
d.mux.Unlock()
|
|
||||||
|
|
||||||
d.notifyPeerListChanged()
|
d.notifyPeerListChanged()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPeerStateChangeNotifier returns a change notifier channel for a peer
|
// GetPeerStateChangeNotifier returns a change notifier channel for a peer
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import (
|
|||||||
func TestAddPeer(t *testing.T) {
|
func TestAddPeer(t *testing.T) {
|
||||||
key := "abc"
|
key := "abc"
|
||||||
status := NewRecorder("https://mgm")
|
status := NewRecorder("https://mgm")
|
||||||
err := status.AddPeer(key, "abc.netbird")
|
err := status.AddPeer(key)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
_, exists := status.peers[key]
|
_, exists := status.peers[key]
|
||||||
assert.True(t, exists, "value was found")
|
assert.True(t, exists, "value was found")
|
||||||
|
|
||||||
err = status.AddPeer(key, "abc.netbird")
|
err = status.AddPeer(key)
|
||||||
|
|
||||||
assert.Error(t, err, "should return error on duplicate")
|
assert.Error(t, err, "should return error on duplicate")
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ func TestAddPeer(t *testing.T) {
|
|||||||
func TestGetPeer(t *testing.T) {
|
func TestGetPeer(t *testing.T) {
|
||||||
key := "abc"
|
key := "abc"
|
||||||
status := NewRecorder("https://mgm")
|
status := NewRecorder("https://mgm")
|
||||||
err := status.AddPeer(key, "abc.netbird")
|
err := status.AddPeer(key)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
peerStatus, err := status.GetPeer(key)
|
peerStatus, err := status.GetPeer(key)
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package proxy
|
package peer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
@@ -11,67 +12,45 @@ type WireGuardProxy struct {
|
|||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
|
||||||
config Config
|
wgListenPort int
|
||||||
|
remoteKey string
|
||||||
|
|
||||||
remoteConn net.Conn
|
remoteConn net.Conn
|
||||||
localConn net.Conn
|
localConn net.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWireGuardProxy(config Config) *WireGuardProxy {
|
func NewWireGuardProxy(wgListenPort int, remoteKey string, remoteConn net.Conn) *WireGuardProxy {
|
||||||
p := &WireGuardProxy{config: config}
|
p := &WireGuardProxy{
|
||||||
|
wgListenPort: wgListenPort,
|
||||||
|
remoteKey: remoteKey,
|
||||||
|
remoteConn: remoteConn,
|
||||||
|
}
|
||||||
p.ctx, p.cancel = context.WithCancel(context.Background())
|
p.ctx, p.cancel = context.WithCancel(context.Background())
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *WireGuardProxy) updateEndpoint() error {
|
func (p *WireGuardProxy) Start() (net.Addr, error) {
|
||||||
udpAddr, err := net.ResolveUDPAddr(p.localConn.LocalAddr().Network(), p.localConn.LocalAddr().String())
|
lConn, err := net.Dial("udp", fmt.Sprintf("127.0.0.1:%d", p.wgListenPort))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// add local proxy connection as a Wireguard peer
|
|
||||||
err = p.config.WgInterface.UpdatePeer(p.config.RemoteKey, p.config.AllowedIps, DefaultWgKeepAlive,
|
|
||||||
udpAddr, p.config.PreSharedKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *WireGuardProxy) Start(remoteConn net.Conn) error {
|
|
||||||
p.remoteConn = remoteConn
|
|
||||||
|
|
||||||
var err error
|
|
||||||
p.localConn, err = net.Dial("udp", p.config.WgListenAddr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed dialing to local Wireguard port %s", err)
|
log.Errorf("failed dialing to local Wireguard port %s", err)
|
||||||
return err
|
return nil, err
|
||||||
}
|
|
||||||
|
|
||||||
err = p.updateEndpoint()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("error while updating Wireguard peer endpoint [%s] %v", p.config.RemoteKey, err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
p.localConn = lConn
|
||||||
|
|
||||||
go p.proxyToRemote()
|
go p.proxyToRemote()
|
||||||
go p.proxyToLocal()
|
go p.proxyToLocal()
|
||||||
|
|
||||||
return nil
|
return lConn.LocalAddr(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *WireGuardProxy) Close() error {
|
func (p *WireGuardProxy) Close() error {
|
||||||
p.cancel()
|
p.cancel()
|
||||||
if c := p.localConn; c != nil {
|
if p.localConn != nil {
|
||||||
err := p.localConn.Close()
|
err := p.localConn.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err := p.config.WgInterface.RemovePeer(p.config.RemoteKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +62,7 @@ func (p *WireGuardProxy) proxyToRemote() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-p.ctx.Done():
|
case <-p.ctx.Done():
|
||||||
log.Debugf("stopped proxying to remote peer %s due to closed connection", p.config.RemoteKey)
|
log.Debugf("stopped proxying to remote peer %s due to closed connection", p.remoteKey)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
n, err := p.localConn.Read(buf)
|
n, err := p.localConn.Read(buf)
|
||||||
@@ -107,7 +86,7 @@ func (p *WireGuardProxy) proxyToLocal() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-p.ctx.Done():
|
case <-p.ctx.Done():
|
||||||
log.Debugf("stopped proxying from remote peer %s due to closed connection", p.config.RemoteKey)
|
log.Debugf("stopped proxying from remote peer %s due to closed connection", p.remoteKey)
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
n, err := p.remoteConn.Read(buf)
|
n, err := p.remoteConn.Read(buf)
|
||||||
@@ -122,7 +101,3 @@ func (p *WireGuardProxy) proxyToLocal() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *WireGuardProxy) Type() Type {
|
|
||||||
return TypeWireGuard
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DummyProxy just sends pings to the RemoteKey peer and reads responses
|
|
||||||
type DummyProxy struct {
|
|
||||||
conn net.Conn
|
|
||||||
remote string
|
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDummyProxy(remote string) *DummyProxy {
|
|
||||||
p := &DummyProxy{remote: remote}
|
|
||||||
p.ctx, p.cancel = context.WithCancel(context.Background())
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *DummyProxy) Close() error {
|
|
||||||
p.cancel()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *DummyProxy) Start(remoteConn net.Conn) error {
|
|
||||||
p.conn = remoteConn
|
|
||||||
go func() {
|
|
||||||
buf := make([]byte, 1500)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-p.ctx.Done():
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
_, err := p.conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("error while reading RemoteKey %s proxy %v", p.remote, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//log.Debugf("received %s from %s", string(buf[:n]), p.remote)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-p.ctx.Done():
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
_, err := p.conn.Write([]byte("hello"))
|
|
||||||
//log.Debugf("sent ping to %s", p.remote)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("error while writing to RemoteKey %s proxy %v", p.remote, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *DummyProxy) Type() Type {
|
|
||||||
return TypeDummy
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NoProxy is used just to configure WireGuard without any local proxy in between.
|
|
||||||
// Used when the WireGuard interface is userspace and uses bind.ICEBind
|
|
||||||
type NoProxy struct {
|
|
||||||
config Config
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNoProxy creates a new NoProxy with a provided config
|
|
||||||
func NewNoProxy(config Config) *NoProxy {
|
|
||||||
return &NoProxy{config: config}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close removes peer from the WireGuard interface
|
|
||||||
func (p *NoProxy) Close() error {
|
|
||||||
err := p.config.WgInterface.RemovePeer(p.config.RemoteKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start just updates WireGuard peer with the remote address
|
|
||||||
func (p *NoProxy) Start(remoteConn net.Conn) error {
|
|
||||||
|
|
||||||
log.Debugf("using NoProxy to connect to peer %s at %s", p.config.RemoteKey, remoteConn.RemoteAddr().String())
|
|
||||||
addr, err := net.ResolveUDPAddr("udp", remoteConn.RemoteAddr().String())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return p.config.WgInterface.UpdatePeer(p.config.RemoteKey, p.config.AllowedIps, DefaultWgKeepAlive,
|
|
||||||
addr, p.config.PreSharedKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *NoProxy) Type() Type {
|
|
||||||
return TypeNoProxy
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const DefaultWgKeepAlive = 25 * time.Second
|
|
||||||
|
|
||||||
type Type string
|
|
||||||
|
|
||||||
const (
|
|
||||||
TypeDirectNoProxy Type = "DirectNoProxy"
|
|
||||||
TypeWireGuard Type = "WireGuard"
|
|
||||||
TypeDummy Type = "Dummy"
|
|
||||||
TypeNoProxy Type = "NoProxy"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
WgListenAddr string
|
|
||||||
RemoteKey string
|
|
||||||
WgInterface *iface.WGIface
|
|
||||||
AllowedIps string
|
|
||||||
PreSharedKey *wgtypes.Key
|
|
||||||
}
|
|
||||||
|
|
||||||
type Proxy interface {
|
|
||||||
io.Closer
|
|
||||||
// Start creates a local remoteConn and starts proxying data from/to remoteConn
|
|
||||||
Start(remoteConn net.Conn) error
|
|
||||||
Type() Type
|
|
||||||
}
|
|
||||||
@@ -71,7 +71,7 @@ func (c *clientNetwork) getRouterPeerStatuses() map[string]routerPeerStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[string]routerPeerStatus) string {
|
func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[string]routerPeerStatus) string {
|
||||||
chosen := ""
|
var chosen string
|
||||||
chosenScore := 0
|
chosenScore := 0
|
||||||
|
|
||||||
currID := ""
|
currID := ""
|
||||||
@@ -85,26 +85,17 @@ func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[string]ro
|
|||||||
if !found || !peerStatus.connected {
|
if !found || !peerStatus.connected {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Metric < route.MaxMetric {
|
if r.Metric < route.MaxMetric {
|
||||||
metricDiff := route.MaxMetric - r.Metric
|
metricDiff := route.MaxMetric - r.Metric
|
||||||
tempScore = metricDiff * 10
|
tempScore = metricDiff * 10
|
||||||
}
|
}
|
||||||
|
|
||||||
if !peerStatus.relayed {
|
if !peerStatus.relayed {
|
||||||
tempScore++
|
tempScore++
|
||||||
}
|
}
|
||||||
|
if !peerStatus.direct {
|
||||||
if peerStatus.direct {
|
|
||||||
tempScore++
|
tempScore++
|
||||||
}
|
}
|
||||||
|
if tempScore > chosenScore || (tempScore == chosenScore && currID == r.ID) {
|
||||||
if tempScore > chosenScore || (tempScore == chosenScore && r.ID == currID) {
|
|
||||||
chosen = r.ID
|
|
||||||
chosenScore = tempScore
|
|
||||||
}
|
|
||||||
|
|
||||||
if chosen == "" && currID == "" {
|
|
||||||
chosen = r.ID
|
chosen = r.ID
|
||||||
chosenScore = tempScore
|
chosenScore = tempScore
|
||||||
}
|
}
|
||||||
@@ -115,9 +106,7 @@ func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[string]ro
|
|||||||
for _, r := range c.routes {
|
for _, r := range c.routes {
|
||||||
peers = append(peers, r.Peer)
|
peers = append(peers, r.Peer)
|
||||||
}
|
}
|
||||||
|
log.Warnf("no route was chosen for network %s because no peers from list %s were 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", chosen, c.routes[chosen].Peer, chosenScore)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,199 +0,0 @@
|
|||||||
package routemanager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/netip"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/route"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetBestrouteFromStatuses(t *testing.T) {
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
statuses map[string]routerPeerStatus
|
|
||||||
expectedRouteID string
|
|
||||||
currentRoute *route.Route
|
|
||||||
existingRoutes map[string]*route.Route
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "one route",
|
|
||||||
statuses: map[string]routerPeerStatus{
|
|
||||||
"route1": {
|
|
||||||
connected: true,
|
|
||||||
relayed: false,
|
|
||||||
direct: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
existingRoutes: map[string]*route.Route{
|
|
||||||
"route1": {
|
|
||||||
ID: "route1",
|
|
||||||
Metric: route.MaxMetric,
|
|
||||||
Peer: "peer1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
currentRoute: nil,
|
|
||||||
expectedRouteID: "route1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "one connected routes with relayed and direct",
|
|
||||||
statuses: map[string]routerPeerStatus{
|
|
||||||
"route1": {
|
|
||||||
connected: true,
|
|
||||||
relayed: true,
|
|
||||||
direct: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
existingRoutes: map[string]*route.Route{
|
|
||||||
"route1": {
|
|
||||||
ID: "route1",
|
|
||||||
Metric: route.MaxMetric,
|
|
||||||
Peer: "peer1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
currentRoute: nil,
|
|
||||||
expectedRouteID: "route1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "one connected routes with relayed and no direct",
|
|
||||||
statuses: map[string]routerPeerStatus{
|
|
||||||
"route1": {
|
|
||||||
connected: true,
|
|
||||||
relayed: true,
|
|
||||||
direct: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
existingRoutes: map[string]*route.Route{
|
|
||||||
"route1": {
|
|
||||||
ID: "route1",
|
|
||||||
Metric: route.MaxMetric,
|
|
||||||
Peer: "peer1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
currentRoute: nil,
|
|
||||||
expectedRouteID: "route1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no connected peers",
|
|
||||||
statuses: map[string]routerPeerStatus{
|
|
||||||
"route1": {
|
|
||||||
connected: false,
|
|
||||||
relayed: false,
|
|
||||||
direct: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
existingRoutes: map[string]*route.Route{
|
|
||||||
"route1": {
|
|
||||||
ID: "route1",
|
|
||||||
Metric: route.MaxMetric,
|
|
||||||
Peer: "peer1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
currentRoute: nil,
|
|
||||||
expectedRouteID: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple connected peers with different metrics",
|
|
||||||
statuses: map[string]routerPeerStatus{
|
|
||||||
"route1": {
|
|
||||||
connected: true,
|
|
||||||
relayed: false,
|
|
||||||
direct: true,
|
|
||||||
},
|
|
||||||
"route2": {
|
|
||||||
connected: true,
|
|
||||||
relayed: false,
|
|
||||||
direct: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
existingRoutes: map[string]*route.Route{
|
|
||||||
"route1": {
|
|
||||||
ID: "route1",
|
|
||||||
Metric: 9000,
|
|
||||||
Peer: "peer1",
|
|
||||||
},
|
|
||||||
"route2": {
|
|
||||||
ID: "route2",
|
|
||||||
Metric: route.MaxMetric,
|
|
||||||
Peer: "peer2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
currentRoute: nil,
|
|
||||||
expectedRouteID: "route1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple connected peers with one relayed",
|
|
||||||
statuses: map[string]routerPeerStatus{
|
|
||||||
"route1": {
|
|
||||||
connected: true,
|
|
||||||
relayed: false,
|
|
||||||
direct: true,
|
|
||||||
},
|
|
||||||
"route2": {
|
|
||||||
connected: true,
|
|
||||||
relayed: true,
|
|
||||||
direct: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
existingRoutes: map[string]*route.Route{
|
|
||||||
"route1": {
|
|
||||||
ID: "route1",
|
|
||||||
Metric: route.MaxMetric,
|
|
||||||
Peer: "peer1",
|
|
||||||
},
|
|
||||||
"route2": {
|
|
||||||
ID: "route2",
|
|
||||||
Metric: route.MaxMetric,
|
|
||||||
Peer: "peer2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
currentRoute: nil,
|
|
||||||
expectedRouteID: "route1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple connected peers with one direct",
|
|
||||||
statuses: map[string]routerPeerStatus{
|
|
||||||
"route1": {
|
|
||||||
connected: true,
|
|
||||||
relayed: false,
|
|
||||||
direct: true,
|
|
||||||
},
|
|
||||||
"route2": {
|
|
||||||
connected: true,
|
|
||||||
relayed: false,
|
|
||||||
direct: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
existingRoutes: map[string]*route.Route{
|
|
||||||
"route1": {
|
|
||||||
ID: "route1",
|
|
||||||
Metric: route.MaxMetric,
|
|
||||||
Peer: "peer1",
|
|
||||||
},
|
|
||||||
"route2": {
|
|
||||||
ID: "route2",
|
|
||||||
Metric: route.MaxMetric,
|
|
||||||
Peer: "peer2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
currentRoute: nil,
|
|
||||||
expectedRouteID: "route1",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
// create new clientNetwork
|
|
||||||
client := &clientNetwork{
|
|
||||||
network: netip.MustParsePrefix("192.168.0.0/24"),
|
|
||||||
routes: tc.existingRoutes,
|
|
||||||
chosenRoute: tc.currentRoute,
|
|
||||||
}
|
|
||||||
|
|
||||||
chosenRoute := client.getBestRouteFromStatuses(tc.statuses)
|
|
||||||
if chosenRoute != tc.expectedRouteID {
|
|
||||||
t.Errorf("expected routeID %s, got %s", tc.expectedRouteID, chosenRoute)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,8 +16,6 @@ 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)
|
|
||||||
InitialRouteRange() []string
|
|
||||||
Stop()
|
Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,14 +29,12 @@ type DefaultManager struct {
|
|||||||
statusRecorder *peer.Status
|
statusRecorder *peer.Status
|
||||||
wgInterface *iface.WGIface
|
wgInterface *iface.WGIface
|
||||||
pubKey string
|
pubKey string
|
||||||
notifier *notifier
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager returns a new route manager
|
// NewManager returns a new route manager
|
||||||
func NewManager(ctx context.Context, pubKey string, wgInterface *iface.WGIface, statusRecorder *peer.Status, initialRoutes []*route.Route) *DefaultManager {
|
func NewManager(ctx context.Context, pubKey string, wgInterface *iface.WGIface, statusRecorder *peer.Status) *DefaultManager {
|
||||||
mCTX, cancel := context.WithCancel(ctx)
|
mCTX, cancel := context.WithCancel(ctx)
|
||||||
|
return &DefaultManager{
|
||||||
dm := &DefaultManager{
|
|
||||||
ctx: mCTX,
|
ctx: mCTX,
|
||||||
stop: cancel,
|
stop: cancel,
|
||||||
clientNetworks: make(map[string]*clientNetwork),
|
clientNetworks: make(map[string]*clientNetwork),
|
||||||
@@ -46,21 +42,13 @@ func NewManager(ctx context.Context, pubKey string, wgInterface *iface.WGIface,
|
|||||||
statusRecorder: statusRecorder,
|
statusRecorder: statusRecorder,
|
||||||
wgInterface: wgInterface,
|
wgInterface: wgInterface,
|
||||||
pubKey: pubKey,
|
pubKey: pubKey,
|
||||||
notifier: newNotifier(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtime.GOOS == "android" {
|
|
||||||
cr := dm.clientRoutes(initialRoutes)
|
|
||||||
dm.notifier.setInitialClientRoutes(cr)
|
|
||||||
}
|
|
||||||
return dm
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the manager watchers and clean firewall rules
|
// Stop stops the manager watchers and clean firewall rules
|
||||||
func (m *DefaultManager) Stop() {
|
func (m *DefaultManager) Stop() {
|
||||||
m.stop()
|
m.stop()
|
||||||
m.serverRouter.cleanUp()
|
m.serverRouter.cleanUp()
|
||||||
m.ctx = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRoutes compares received routes with existing routes and remove, update or add them to the client and server maps
|
// UpdateRoutes compares received routes with existing routes and remove, update or add them to the client and server maps
|
||||||
@@ -73,10 +61,39 @@ func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Ro
|
|||||||
m.mux.Lock()
|
m.mux.Lock()
|
||||||
defer m.mux.Unlock()
|
defer m.mux.Unlock()
|
||||||
|
|
||||||
newServerRoutesMap, newClientRoutesIDMap := m.classifiesRoutes(newRoutes)
|
newClientRoutesIDMap := make(map[string][]*route.Route)
|
||||||
|
newServerRoutesMap := make(map[string]*route.Route)
|
||||||
|
ownNetworkIDs := make(map[string]bool)
|
||||||
|
|
||||||
|
for _, newRoute := range newRoutes {
|
||||||
|
networkID := route.GetHAUniqueID(newRoute)
|
||||||
|
if newRoute.Peer == m.pubKey {
|
||||||
|
ownNetworkIDs[networkID] = true
|
||||||
|
// only linux is supported for now
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
log.Warnf("received a route to manage, but agent doesn't support router mode on %s OS", runtime.GOOS)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newServerRoutesMap[newRoute.ID] = newRoute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, newRoute := range newRoutes {
|
||||||
|
networkID := route.GetHAUniqueID(newRoute)
|
||||||
|
if !ownNetworkIDs[networkID] {
|
||||||
|
// if prefix is too small, lets assume is a possible default route which is not yet supported
|
||||||
|
// we skip this route management
|
||||||
|
if newRoute.Network.Bits() < 7 {
|
||||||
|
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skiping this route",
|
||||||
|
version.NetbirdVersion(), newRoute.Network)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
newClientRoutesIDMap[networkID] = append(newClientRoutesIDMap[networkID], newRoute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m.updateClientNetworks(updateSerial, newClientRoutesIDMap)
|
m.updateClientNetworks(updateSerial, newClientRoutesIDMap)
|
||||||
m.notifier.onNewRoutes(newClientRoutesIDMap)
|
|
||||||
err := m.serverRouter.updateRoutes(newServerRoutesMap)
|
err := m.serverRouter.updateRoutes(newServerRoutesMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -86,16 +103,6 @@ func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Ro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRouteChangeListener set RouteListener for route change notifier
|
|
||||||
func (m *DefaultManager) SetRouteChangeListener(listener RouteListener) {
|
|
||||||
m.notifier.setListener(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitialRouteRange return the list of initial routes. It used by mobile systems
|
|
||||||
func (m *DefaultManager) InitialRouteRange() []string {
|
|
||||||
return m.notifier.initialRouteRanges()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks map[string][]*route.Route) {
|
func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks map[string][]*route.Route) {
|
||||||
// removing routes that do not exist as per the update from the Management service.
|
// removing routes that do not exist as per the update from the Management service.
|
||||||
for id, client := range m.clientNetworks {
|
for id, client := range m.clientNetworks {
|
||||||
@@ -121,47 +128,3 @@ func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks map[
|
|||||||
clientNetworkWatcher.sendUpdateToClientNetworkWatcher(update)
|
clientNetworkWatcher.sendUpdateToClientNetworkWatcher(update)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DefaultManager) classifiesRoutes(newRoutes []*route.Route) (map[string]*route.Route, map[string][]*route.Route) {
|
|
||||||
newClientRoutesIDMap := make(map[string][]*route.Route)
|
|
||||||
newServerRoutesMap := make(map[string]*route.Route)
|
|
||||||
ownNetworkIDs := make(map[string]bool)
|
|
||||||
|
|
||||||
for _, newRoute := range newRoutes {
|
|
||||||
networkID := route.GetHAUniqueID(newRoute)
|
|
||||||
if newRoute.Peer == m.pubKey {
|
|
||||||
ownNetworkIDs[networkID] = true
|
|
||||||
// only linux is supported for now
|
|
||||||
if runtime.GOOS != "linux" {
|
|
||||||
log.Warnf("received a route to manage, but agent doesn't support router mode on %s OS", runtime.GOOS)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newServerRoutesMap[newRoute.ID] = newRoute
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, newRoute := range newRoutes {
|
|
||||||
networkID := route.GetHAUniqueID(newRoute)
|
|
||||||
if !ownNetworkIDs[networkID] {
|
|
||||||
// if prefix is too small, lets assume is a possible default route which is not yet supported
|
|
||||||
// we skip this route management
|
|
||||||
if newRoute.Network.Bits() < 7 {
|
|
||||||
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skiping this route",
|
|
||||||
version.NetbirdVersion(), newRoute.Network)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newClientRoutesIDMap[networkID] = append(newClientRoutesIDMap[networkID], newRoute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newServerRoutesMap, newClientRoutesIDMap
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *DefaultManager) clientRoutes(initialRoutes []*route.Route) []*route.Route {
|
|
||||||
_, crMap := m.classifiesRoutes(initialRoutes)
|
|
||||||
rs := make([]*route.Route, 0)
|
|
||||||
for _, routes := range crMap {
|
|
||||||
rs = append(rs, routes...)
|
|
||||||
}
|
|
||||||
return rs
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -397,7 +397,7 @@ func TestManagerUpdateRoutes(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun43%d", n), "100.65.65.2/24", iface.DefaultMTU, nil, newNet)
|
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun43%d", n), "100.65.65.2/24", iface.DefaultMTU, nil, nil, newNet)
|
||||||
require.NoError(t, err, "should create testing WGIface interface")
|
require.NoError(t, err, "should create testing WGIface interface")
|
||||||
defer wgInterface.Close()
|
defer wgInterface.Close()
|
||||||
|
|
||||||
@@ -406,7 +406,7 @@ func TestManagerUpdateRoutes(t *testing.T) {
|
|||||||
|
|
||||||
statusRecorder := peer.NewRecorder("https://mgm")
|
statusRecorder := peer.NewRecorder("https://mgm")
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
routeManager := NewManager(ctx, localPeerKey, wgInterface, statusRecorder, nil)
|
routeManager := NewManager(ctx, localPeerKey, wgInterface, statusRecorder)
|
||||||
defer routeManager.Stop()
|
defer routeManager.Stop()
|
||||||
|
|
||||||
if len(testCase.inputInitRoutes) > 0 {
|
if len(testCase.inputInitRoutes) > 0 {
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package routemanager
|
package routemanager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,11 +11,6 @@ type MockManager struct {
|
|||||||
StopFunc func()
|
StopFunc func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitialRouteRange mock implementation of InitialRouteRange from Manager interface
|
|
||||||
func (m *MockManager) InitialRouteRange() []string {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateRoutes mock implementation of UpdateRoutes from Manager interface
|
// UpdateRoutes mock implementation of UpdateRoutes from Manager interface
|
||||||
func (m *MockManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error {
|
func (m *MockManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) error {
|
||||||
if m.UpdateRoutesFunc != nil {
|
if m.UpdateRoutesFunc != nil {
|
||||||
@@ -27,15 +19,6 @@ func (m *MockManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route
|
|||||||
return fmt.Errorf("method UpdateRoutes is not implemented")
|
return fmt.Errorf("method UpdateRoutes is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start mock implementation of Start from Manager interface
|
|
||||||
func (m *MockManager) Start(ctx context.Context, iface *iface.WGIface) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRouteChangeListener mock implementation of SetRouteChangeListener from Manager interface
|
|
||||||
func (m *MockManager) SetRouteChangeListener(listener RouteListener) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop mock implementation of Stop from Manager interface
|
// Stop mock implementation of Stop from Manager interface
|
||||||
func (m *MockManager) Stop() {
|
func (m *MockManager) Stop() {
|
||||||
if m.StopFunc != nil {
|
if m.StopFunc != nil {
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
package routemanager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/route"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RouteListener is a callback interface for mobile system
|
|
||||||
type RouteListener interface {
|
|
||||||
// OnNewRouteSetting invoke when new route setting has been arrived
|
|
||||||
OnNewRouteSetting()
|
|
||||||
}
|
|
||||||
|
|
||||||
type notifier struct {
|
|
||||||
initialRouteRangers []string
|
|
||||||
routeRangers []string
|
|
||||||
|
|
||||||
routeListener RouteListener
|
|
||||||
routeListenerMux sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newNotifier() *notifier {
|
|
||||||
return ¬ifier{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *notifier) setListener(listener RouteListener) {
|
|
||||||
n.routeListenerMux.Lock()
|
|
||||||
defer n.routeListenerMux.Unlock()
|
|
||||||
n.routeListener = listener
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *notifier) setInitialClientRoutes(clientRoutes []*route.Route) {
|
|
||||||
nets := make([]string, 0)
|
|
||||||
for _, r := range clientRoutes {
|
|
||||||
nets = append(nets, r.Network.String())
|
|
||||||
}
|
|
||||||
sort.Strings(nets)
|
|
||||||
n.initialRouteRangers = nets
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *notifier) onNewRoutes(idMap map[string][]*route.Route) {
|
|
||||||
newNets := make([]string, 0)
|
|
||||||
for _, routes := range idMap {
|
|
||||||
for _, r := range routes {
|
|
||||||
newNets = append(newNets, r.Network.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(newNets)
|
|
||||||
if !n.hasDiff(n.routeRangers, newNets) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
n.routeRangers = newNets
|
|
||||||
|
|
||||||
if !n.hasDiff(n.initialRouteRangers, newNets) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
n.notify()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *notifier) notify() {
|
|
||||||
n.routeListenerMux.Lock()
|
|
||||||
defer n.routeListenerMux.Unlock()
|
|
||||||
if n.routeListener == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
go func(l RouteListener) {
|
|
||||||
l.OnNewRouteSetting()
|
|
||||||
}(n.routeListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *notifier) hasDiff(a []string, b []string) bool {
|
|
||||||
if len(a) != len(b) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for i, v := range a {
|
|
||||||
if v != b[i] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *notifier) initialRouteRanges() []string {
|
|
||||||
return n.initialRouteRangers
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
|
|
||||||
// +build darwin dragonfly freebsd netbsd openbsd
|
|
||||||
|
|
||||||
package routemanager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"golang.org/x/net/route"
|
|
||||||
)
|
|
||||||
|
|
||||||
// selected BSD Route flags.
|
|
||||||
const (
|
|
||||||
RTF_UP = 0x1
|
|
||||||
RTF_GATEWAY = 0x2
|
|
||||||
RTF_HOST = 0x4
|
|
||||||
RTF_REJECT = 0x8
|
|
||||||
RTF_DYNAMIC = 0x10
|
|
||||||
RTF_MODIFIED = 0x20
|
|
||||||
RTF_STATIC = 0x800
|
|
||||||
RTF_BLACKHOLE = 0x1000
|
|
||||||
RTF_LOCAL = 0x200000
|
|
||||||
RTF_BROADCAST = 0x400000
|
|
||||||
RTF_MULTICAST = 0x800000
|
|
||||||
)
|
|
||||||
|
|
||||||
func existsInRouteTable(prefix netip.Prefix) (bool, error) {
|
|
||||||
tab, err := route.FetchRIB(syscall.AF_UNSPEC, route.RIBTypeRoute, 0)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
msgs, err := route.ParseRIB(route.RIBTypeRoute, tab)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, msg := range msgs {
|
|
||||||
m := msg.(*route.RouteMessage)
|
|
||||||
|
|
||||||
if m.Version < 3 || m.Version > 5 {
|
|
||||||
return false, fmt.Errorf("unexpected RIB message version: %d", m.Version)
|
|
||||||
}
|
|
||||||
if m.Type != 4 /* RTM_GET */ {
|
|
||||||
return true, fmt.Errorf("unexpected RIB message type: %d", m.Type)
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.Flags&RTF_UP == 0 ||
|
|
||||||
m.Flags&(RTF_REJECT|RTF_BLACKHOLE) != 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dst, err := toIPAddr(m.Addrs[0])
|
|
||||||
if err != nil {
|
|
||||||
return true, fmt.Errorf("unexpected RIB destination: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mask, _ := toIPAddr(m.Addrs[2])
|
|
||||||
cidr, _ := net.IPMask(mask.To4()).Size()
|
|
||||||
if dst.String() == prefix.Addr().String() && cidr == prefix.Bits() {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func toIPAddr(a route.Addr) (net.IP, error) {
|
|
||||||
switch t := a.(type) {
|
|
||||||
case *route.Inet4Addr:
|
|
||||||
ip := net.IPv4(t.IP[0], t.IP[1], t.IP[2], t.IP[3])
|
|
||||||
return ip, nil
|
|
||||||
case *route.Inet6Addr:
|
|
||||||
ip := make(net.IP, net.IPv6len)
|
|
||||||
copy(ip, t.IP[:])
|
|
||||||
return ip, nil
|
|
||||||
default:
|
|
||||||
return net.IP{}, fmt.Errorf("unknown family: %v", t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,28 +6,10 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pulled from http://man7.org/linux/man-pages/man7/rtnetlink.7.html
|
|
||||||
// See the section on RTM_NEWROUTE, specifically 'struct rtmsg'.
|
|
||||||
type routeInfoInMemory struct {
|
|
||||||
Family byte
|
|
||||||
DstLen byte
|
|
||||||
SrcLen byte
|
|
||||||
TOS byte
|
|
||||||
|
|
||||||
Table byte
|
|
||||||
Protocol byte
|
|
||||||
Scope byte
|
|
||||||
Type byte
|
|
||||||
|
|
||||||
Flags uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const ipv4ForwardingPath = "/proc/sys/net/ipv4/ip_forward"
|
const ipv4ForwardingPath = "/proc/sys/net/ipv4/ip_forward"
|
||||||
|
|
||||||
func addToRouteTable(prefix netip.Prefix, addr string) error {
|
func addToRouteTable(prefix netip.Prefix, addr string) error {
|
||||||
@@ -79,56 +61,7 @@ func removeFromRouteTable(prefix netip.Prefix) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func existsInRouteTable(prefix netip.Prefix) (bool, error) {
|
|
||||||
tab, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC)
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
msgs, err := syscall.ParseNetlinkMessage(tab)
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
loop:
|
|
||||||
for _, m := range msgs {
|
|
||||||
switch m.Header.Type {
|
|
||||||
case syscall.NLMSG_DONE:
|
|
||||||
break loop
|
|
||||||
case syscall.RTM_NEWROUTE:
|
|
||||||
rt := (*routeInfoInMemory)(unsafe.Pointer(&m.Data[0]))
|
|
||||||
attrs, err := syscall.ParseNetlinkRouteAttr(&m)
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
if rt.Family != syscall.AF_INET {
|
|
||||||
continue loop
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, attr := range attrs {
|
|
||||||
if attr.Attr.Type == syscall.RTA_DST {
|
|
||||||
ip := net.IP(attr.Value)
|
|
||||||
mask := net.CIDRMask(int(rt.DstLen), len(attr.Value)*8)
|
|
||||||
cidr, _ := mask.Size()
|
|
||||||
if ip.String() == prefix.Addr().String() && cidr == prefix.Bits() {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func enableIPForwarding() error {
|
func enableIPForwarding() error {
|
||||||
bytes, err := os.ReadFile(ipv4ForwardingPath)
|
err := os.WriteFile(ipv4ForwardingPath, []byte("1"), 0644)
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if it is already enabled
|
|
||||||
// see more: https://github.com/netbirdio/netbird/issues/872
|
|
||||||
if len(bytes) > 0 && bytes[0] == 49 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.WriteFile(ipv4ForwardingPath, []byte("1"), 0644)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,26 +14,19 @@ import (
|
|||||||
var errRouteNotFound = fmt.Errorf("route not found")
|
var errRouteNotFound = fmt.Errorf("route not found")
|
||||||
|
|
||||||
func addToRouteTableIfNoExists(prefix netip.Prefix, addr string) error {
|
func addToRouteTableIfNoExists(prefix netip.Prefix, addr string) error {
|
||||||
defaultGateway, err := getExistingRIBRouteGateway(netip.MustParsePrefix("0.0.0.0/0"))
|
gateway, err := getExistingRIBRouteGateway(netip.MustParsePrefix("0.0.0.0/0"))
|
||||||
|
if err != nil && err != errRouteNotFound {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
prefixGateway, err := getExistingRIBRouteGateway(prefix)
|
||||||
if err != nil && err != errRouteNotFound {
|
if err != nil && err != errRouteNotFound {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
gatewayIP := netip.MustParseAddr(defaultGateway.String())
|
if prefixGateway != nil && !prefixGateway.Equal(gateway) {
|
||||||
if prefix.Contains(gatewayIP) {
|
log.Warnf("skipping adding a new route for network %s because it already exists and is pointing to the non default gateway: %s", prefix, prefixGateway)
|
||||||
log.Warnf("skipping adding a new route for network %s because it overlaps with the default gateway: %s", prefix, gatewayIP)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, err := existsInRouteTable(prefix)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
log.Warnf("skipping adding a new route for network %s because it already exists", prefix)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return addToRouteTable(prefix, addr)
|
return addToRouteTable(prefix, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +53,6 @@ func getExistingRIBRouteGateway(prefix netip.Prefix) (net.IP, error) {
|
|||||||
log.Errorf("getting routes returned an error: %v", err)
|
log.Errorf("getting routes returned an error: %v", err)
|
||||||
return nil, errRouteNotFound
|
return nil, errRouteNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if gateway == nil {
|
if gateway == nil {
|
||||||
return preferredSrc, nil
|
return preferredSrc, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
package routemanager
|
package routemanager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
"github.com/pion/transport/v2/stdnet"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pion/transport/v2/stdnet"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAddRemoveRoutes(t *testing.T) {
|
func TestAddRemoveRoutes(t *testing.T) {
|
||||||
@@ -43,7 +37,7 @@ func TestAddRemoveRoutes(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", iface.DefaultMTU, nil, newNet)
|
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", iface.DefaultMTU, nil, nil, newNet)
|
||||||
require.NoError(t, err, "should create testing WGIface interface")
|
require.NoError(t, err, "should create testing WGIface interface")
|
||||||
defer wgInterface.Close()
|
defer wgInterface.Close()
|
||||||
|
|
||||||
@@ -120,98 +114,3 @@ func TestGetExistingRIBRouteGateway(t *testing.T) {
|
|||||||
t.Fatalf("local ip should match with testing IP: want %s got %s", testingIP, localIP.String())
|
t.Fatalf("local ip should match with testing IP: want %s got %s", testingIP, localIP.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddExistAndRemoveRouteNonAndroid(t *testing.T) {
|
|
||||||
defaultGateway, err := getExistingRIBRouteGateway(netip.MustParsePrefix("0.0.0.0/0"))
|
|
||||||
fmt.Println("defaultGateway: ", defaultGateway)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("shouldn't return error when fetching the gateway: ", err)
|
|
||||||
}
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
prefix netip.Prefix
|
|
||||||
preExistingPrefix netip.Prefix
|
|
||||||
shouldAddRoute bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Should Add And Remove random Route",
|
|
||||||
prefix: netip.MustParsePrefix("99.99.99.99/32"),
|
|
||||||
shouldAddRoute: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Should Not Add Route if overlaps with default gateway",
|
|
||||||
prefix: netip.MustParsePrefix(defaultGateway.String() + "/31"),
|
|
||||||
shouldAddRoute: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Should Add Route if bigger network exists",
|
|
||||||
prefix: netip.MustParsePrefix("100.100.100.0/24"),
|
|
||||||
preExistingPrefix: netip.MustParsePrefix("100.100.0.0/16"),
|
|
||||||
shouldAddRoute: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Should Add Route if smaller network exists",
|
|
||||||
prefix: netip.MustParsePrefix("100.100.0.0/16"),
|
|
||||||
preExistingPrefix: netip.MustParsePrefix("100.100.100.0/24"),
|
|
||||||
shouldAddRoute: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Should Not Add Route if same network exists",
|
|
||||||
prefix: netip.MustParsePrefix("100.100.0.0/16"),
|
|
||||||
preExistingPrefix: netip.MustParsePrefix("100.100.0.0/16"),
|
|
||||||
shouldAddRoute: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for n, testCase := range testCases {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
log.SetOutput(&buf)
|
|
||||||
defer func() {
|
|
||||||
log.SetOutput(os.Stderr)
|
|
||||||
}()
|
|
||||||
t.Run(testCase.name, func(t *testing.T) {
|
|
||||||
newNet, err := stdnet.NewNet()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", iface.DefaultMTU, nil, newNet)
|
|
||||||
require.NoError(t, err, "should create testing WGIface interface")
|
|
||||||
defer wgInterface.Close()
|
|
||||||
|
|
||||||
err = wgInterface.Create()
|
|
||||||
require.NoError(t, err, "should create testing wireguard interface")
|
|
||||||
|
|
||||||
MockAddr := wgInterface.Address().IP.String()
|
|
||||||
|
|
||||||
// Prepare the environment
|
|
||||||
if testCase.preExistingPrefix.IsValid() {
|
|
||||||
err := addToRouteTableIfNoExists(testCase.preExistingPrefix, MockAddr)
|
|
||||||
require.NoError(t, err, "should not return err when adding pre-existing route")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the route
|
|
||||||
err = addToRouteTableIfNoExists(testCase.prefix, MockAddr)
|
|
||||||
require.NoError(t, err, "should not return err when adding route")
|
|
||||||
|
|
||||||
if testCase.shouldAddRoute {
|
|
||||||
// test if route exists after adding
|
|
||||||
ok, err := existsInRouteTable(testCase.prefix)
|
|
||||||
require.NoError(t, err, "should not return err")
|
|
||||||
require.True(t, ok, "route should exist")
|
|
||||||
|
|
||||||
// remove route again if added
|
|
||||||
err = removeFromRouteTableIfNonSystem(testCase.prefix, MockAddr)
|
|
||||||
require.NoError(t, err, "should not return err")
|
|
||||||
}
|
|
||||||
|
|
||||||
// route should either not have been added or should have been removed
|
|
||||||
// In case of already existing route, it should not have been added (but still exist)
|
|
||||||
ok, err := existsInRouteTable(testCase.prefix)
|
|
||||||
fmt.Println("Buffer string: ", buf.String())
|
|
||||||
require.NoError(t, err, "should not return err")
|
|
||||||
if !strings.Contains(buf.String(), "because it already exists") {
|
|
||||||
require.False(t, ok, "route should not exist")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
//go:build windows
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package routemanager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/yusufpapurcu/wmi"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Win32_IP4RouteTable struct {
|
|
||||||
Destination string
|
|
||||||
Mask string
|
|
||||||
}
|
|
||||||
|
|
||||||
func existsInRouteTable(prefix netip.Prefix) (bool, error) {
|
|
||||||
var routes []Win32_IP4RouteTable
|
|
||||||
query := "SELECT Destination, Mask FROM Win32_IP4RouteTable"
|
|
||||||
|
|
||||||
err := wmi.Query(query, &routes)
|
|
||||||
if err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, route := range routes {
|
|
||||||
ip := net.ParseIP(route.Mask)
|
|
||||||
ip = ip.To4()
|
|
||||||
mask := net.IPv4Mask(ip[0], ip[1], ip[2], ip[3])
|
|
||||||
cidr, _ := mask.Size()
|
|
||||||
if route.Destination == prefix.Addr().String() && cidr == prefix.Bits() {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/cmd"
|
"github.com/netbirdio/netbird/client/cmd"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ func (s *Server) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := internal.RunClient(ctx, config, s.statusRecorder, nil, nil, nil); err != nil {
|
if err := internal.RunClient(ctx, config, s.statusRecorder, nil, nil); err != nil {
|
||||||
log.Errorf("init connections: %v", err)
|
log.Errorf("init connections: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -236,9 +236,7 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro
|
|||||||
}, nil
|
}, nil
|
||||||
} else {
|
} else {
|
||||||
log.Warnf("canceling previous waiting execution")
|
log.Warnf("canceling previous waiting execution")
|
||||||
if s.oauthAuthFlow.waitCancel != nil {
|
s.oauthAuthFlow.waitCancel()
|
||||||
s.oauthAuthFlow.waitCancel()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,7 +389,7 @@ func (s *Server) Up(callerCtx context.Context, _ *proto.UpRequest) (*proto.UpRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := internal.RunClient(ctx, s.config, s.statusRecorder, nil, nil, nil); err != nil {
|
if err := internal.RunClient(ctx, s.config, s.statusRecorder, nil, nil); err != nil {
|
||||||
log.Errorf("run client connection: %v", err)
|
log.Errorf("run client connection: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
62
go.mod
62
go.mod
@@ -5,7 +5,7 @@ go 1.20
|
|||||||
require (
|
require (
|
||||||
github.com/cenkalti/backoff/v4 v4.1.3
|
github.com/cenkalti/backoff/v4 v4.1.3
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible
|
github.com/golang-jwt/jwt v3.2.2+incompatible
|
||||||
github.com/golang/protobuf v1.5.3
|
github.com/golang/protobuf v1.5.2
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
|
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
|
||||||
@@ -17,17 +17,18 @@ 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.7.0
|
||||||
golang.org/x/sys v0.8.0
|
golang.org/x/sys v0.6.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.1
|
||||||
google.golang.org/grpc v1.55.0
|
google.golang.org/grpc v1.52.3
|
||||||
google.golang.org/protobuf v1.30.0
|
google.golang.org/protobuf v1.28.1
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
codeberg.org/ac/base62 v0.0.0-20210305150220-e793b546833a
|
||||||
fyne.io/fyne/v2 v2.1.4
|
fyne.io/fyne/v2 v2.1.4
|
||||||
github.com/c-robinson/iplib v1.0.3
|
github.com/c-robinson/iplib v1.0.3
|
||||||
github.com/coreos/go-iptables v0.6.0
|
github.com/coreos/go-iptables v0.6.0
|
||||||
@@ -36,7 +37,6 @@ require (
|
|||||||
github.com/getlantern/systray v1.2.1
|
github.com/getlantern/systray v1.2.1
|
||||||
github.com/gliderlabs/ssh v0.3.4
|
github.com/gliderlabs/ssh v0.3.4
|
||||||
github.com/godbus/dbus/v5 v5.1.0
|
github.com/godbus/dbus/v5 v5.1.0
|
||||||
github.com/golang/mock v1.6.0
|
|
||||||
github.com/google/go-cmp v0.5.9
|
github.com/google/go-cmp v0.5.9
|
||||||
github.com/google/gopacket v1.1.19
|
github.com/google/gopacket v1.1.19
|
||||||
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
|
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
|
||||||
@@ -48,7 +48,7 @@ require (
|
|||||||
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/okta/okta-sdk-golang/v2 v2.18.0
|
github.com/open-policy-agent/opa v0.49.0
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/pion/logging v0.2.2
|
github.com/pion/logging v0.2.2
|
||||||
github.com/pion/stun v0.4.0
|
github.com/pion/stun v0.4.0
|
||||||
@@ -57,33 +57,26 @@ require (
|
|||||||
github.com/rs/xid v1.3.0
|
github.com/rs/xid v1.3.0
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/yusufpapurcu/wmi v1.2.3
|
|
||||||
go.opentelemetry.io/otel v1.11.1
|
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.33.0
|
go.opentelemetry.io/otel/exporters/prometheus v0.33.0
|
||||||
go.opentelemetry.io/otel/metric v0.33.0
|
go.opentelemetry.io/otel/metric v0.33.0
|
||||||
go.opentelemetry.io/otel/sdk/metric v0.33.0
|
go.opentelemetry.io/otel/sdk/metric v0.33.0
|
||||||
goauthentik.io/api/v3 v3.2023051.3
|
|
||||||
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/net v0.8.0
|
||||||
golang.org/x/net v0.10.0
|
golang.org/x/sync v0.1.0
|
||||||
golang.org/x/oauth2 v0.8.0
|
golang.org/x/term v0.6.0
|
||||||
golang.org/x/sync v0.2.0
|
|
||||||
golang.org/x/term v0.8.0
|
|
||||||
google.golang.org/api v0.126.0
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/compute v1.19.3 // indirect
|
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
|
||||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||||
|
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
|
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
|
||||||
|
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
@@ -93,22 +86,18 @@ require (
|
|||||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect
|
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect
|
||||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect
|
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect
|
||||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect
|
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect
|
||||||
|
github.com/ghodss/yaml v1.0.0 // indirect
|
||||||
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f // indirect
|
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f // indirect
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be // indirect
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be // indirect
|
||||||
github.com/go-logr/logr v1.2.3 // indirect
|
github.com/go-logr/logr v1.2.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
|
||||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||||
github.com/go-stack/stack v1.8.0 // indirect
|
github.com/go-stack/stack v1.8.0 // indirect
|
||||||
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
|
||||||
github.com/google/s2a-go v0.1.4 // indirect
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // 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/josharian/native v1.0.0 // indirect
|
github.com/josharian/native v1.0.0 // indirect
|
||||||
github.com/kelseyhightower/envconfig v1.4.0 // indirect
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mdlayher/genetlink v1.1.0 // indirect
|
github.com/mdlayher/genetlink v1.1.0 // indirect
|
||||||
github.com/mdlayher/netlink v1.7.1 // indirect
|
github.com/mdlayher/netlink v1.7.1 // indirect
|
||||||
@@ -124,25 +113,26 @@ require (
|
|||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
github.com/prometheus/common v0.37.0 // indirect
|
||||||
github.com/prometheus/procfs v0.8.0 // indirect
|
github.com/prometheus/procfs v0.8.0 // indirect
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
|
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
|
||||||
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
|
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
|
||||||
|
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
|
github.com/yashtewari/glob-intersection v0.1.0 // indirect
|
||||||
github.com/yuin/goldmark v1.4.13 // indirect
|
github.com/yuin/goldmark v1.4.13 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opentelemetry.io/otel v1.11.1 // 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.0.0-20200430140353-33d19683fad8 // 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.8.0 // 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
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
|
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
@@ -154,4 +144,4 @@ replace github.com/kardianos/service => github.com/netbirdio/service v0.0.0-2023
|
|||||||
|
|
||||||
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-20221012095658-dc8eda872c0c
|
||||||
|
|
||||||
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-20230426151838-5c7986a94d53
|
||||||
|
|||||||
135
go.sum
135
go.sum
@@ -20,11 +20,6 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
|
|||||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||||
cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds=
|
|
||||||
cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI=
|
|
||||||
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
|
||||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
@@ -36,6 +31,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
|||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||||
|
codeberg.org/ac/base62 v0.0.0-20210305150220-e793b546833a h1:U6cY/g6VSiy59vuvnBU6J/eSir0qVg4BeTnCDLaX+20=
|
||||||
|
codeberg.org/ac/base62 v0.0.0-20210305150220-e793b546833a/go.mod h1:ykEpkLT4JtH3I4Rb4gwkDsNLfgUg803qRDeIX88t3e8=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
fyne.io/fyne/v2 v2.1.4 h1:bt1+28++kAzRzPB0GM2EuSV4cnl8rXNX4cjfd8G06Rc=
|
fyne.io/fyne/v2 v2.1.4 h1:bt1+28++kAzRzPB0GM2EuSV4cnl8rXNX4cjfd8G06Rc=
|
||||||
fyne.io/fyne/v2 v2.1.4/go.mod h1:p+E/Dh+wPW8JwR2DVcsZ9iXgR9ZKde80+Y+40Is54AQ=
|
fyne.io/fyne/v2 v2.1.4/go.mod h1:p+E/Dh+wPW8JwR2DVcsZ9iXgR9ZKde80+Y+40Is54AQ=
|
||||||
@@ -57,12 +54,16 @@ github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpz
|
|||||||
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
|
github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
|
||||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 h1:pami0oPhVosjOu/qRHepRmdjD6hGILF7DBr+qQZeP10=
|
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 h1:pami0oPhVosjOu/qRHepRmdjD6hGILF7DBr+qQZeP10=
|
||||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2/go.mod h1:jNIx5ykW1MroBuaTja9+VpglmaJOUzezumfhLlER3oY=
|
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2/go.mod h1:jNIx5ykW1MroBuaTja9+VpglmaJOUzezumfhLlER3oY=
|
||||||
|
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
||||||
|
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
@@ -73,6 +74,8 @@ github.com/allegro/bigcache/v3 v3.0.2 h1:AKZCw+5eAaVyNTBmI2fgyPVJhHkdWder3O9Irpr
|
|||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||||
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/bazelbuild/rules_go v0.30.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
|
github.com/bazelbuild/rules_go v0.30.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
|
||||||
@@ -82,6 +85,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
|
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||||
|
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA=
|
||||||
github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdPU=
|
github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdPU=
|
||||||
github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
|
github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
|
||||||
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
|
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
|
||||||
@@ -89,11 +93,11 @@ github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq
|
|||||||
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
||||||
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
@@ -106,7 +110,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
|||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
@@ -147,13 +150,14 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg=
|
||||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||||
|
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
|
||||||
|
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
@@ -169,13 +173,13 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||||
|
github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8=
|
||||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||||
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
|
||||||
@@ -198,6 +202,7 @@ github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2
|
|||||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
|
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
|
||||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||||
@@ -226,7 +231,6 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
|||||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||||
@@ -243,6 +247,8 @@ github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq
|
|||||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
@@ -252,20 +258,20 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
|||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
|
||||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
|
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
@@ -274,7 +280,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
|||||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
|
||||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
@@ -291,13 +296,14 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
|
|||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||||
|
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
@@ -310,7 +316,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||||
@@ -331,19 +336,13 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
|
|||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
|
|
||||||
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
|
|
||||||
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/googleapis/gax-go/v2 v2.10.0 h1:ebSgKfMxynOdxw8QQuFOKMgomqeLGPqNLQox2bo42zg=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw=
|
|
||||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||||
github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
github.com/googleapis/gnostic v0.4.0/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
||||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||||
@@ -406,12 +405,11 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
|
|||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
|
||||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
|
||||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
@@ -489,16 +487,14 @@ github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0 h1:hirFRfx3grVA/
|
|||||||
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-20221012095658-dc8eda872c0c h1:wK/s4nyZj/GF/kFJQjX6nqNfE0G3gcqd6hhnPCyp4sw=
|
||||||
github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c/go.mod h1:AecygODWIsBquJCJFop8MEQcJbWFfw/1yWbVabNgpCM=
|
github.com/netbirdio/systray v0.0.0-20221012095658-dc8eda872c0c/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-20230426151838-5c7986a94d53 h1:OPbKpisDyMbOf/TDYS0Niw7yc/uoviED/pKyO+8A1C0=
|
||||||
github.com/netbirdio/wireguard-go v0.0.0-20230524172305-5a498a82b33f/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4=
|
github.com/netbirdio/wireguard-go v0.0.0-20230426151838-5c7986a94d53/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=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
github.com/okta/okta-sdk-golang/v2 v2.18.0 h1:cfDasMb7CShbZvOrF6n+DnLevWwiHgedWMGJ8M8xKDc=
|
|
||||||
github.com/okta/okta-sdk-golang/v2 v2.18.0/go.mod h1:dz30v3ctAiMb7jpsCngGfQUAEGm1/NsWT92uTbNDQIs=
|
|
||||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
@@ -518,6 +514,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
|||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||||
|
github.com/open-policy-agent/opa v0.49.0 h1:TIlpCT1B5FSm8Dqo/a4t23gKmHkQysC3+7W77F99P4k=
|
||||||
|
github.com/open-policy-agent/opa v0.49.0/go.mod h1:WTLWtu498/QNTDkiHx76Xj7jaJUPvLJAPtdMkCcst0w=
|
||||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||||
@@ -591,6 +589,8 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
|||||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
@@ -652,6 +652,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
|
|||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||||
|
github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
|
||||||
|
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||||
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
|
github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM=
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
@@ -665,8 +667,14 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp
|
|||||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
|
||||||
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg=
|
||||||
|
github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@@ -677,17 +685,14 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
|||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
|
||||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
|
||||||
go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4=
|
go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4=
|
||||||
go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE=
|
go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE=
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.33.0 h1:xXhPj7SLKWU5/Zd4Hxmd+X1C4jdmvc0Xy+kvjFx2z60=
|
go.opentelemetry.io/otel/exporters/prometheus v0.33.0 h1:xXhPj7SLKWU5/Zd4Hxmd+X1C4jdmvc0Xy+kvjFx2z60=
|
||||||
@@ -704,8 +709,6 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
|
|||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||||
goauthentik.io/api/v3 v3.2023051.3 h1:NebAhD/TeTWNo/9X3/Uj+rM5fG1HaiLOlKTNLQv9Qq4=
|
|
||||||
goauthentik.io/api/v3 v3.2023051.3/go.mod h1:nYECml4jGbp/541hj8GcylKQG1gVBsKppHy4+7G8u4U=
|
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
@@ -717,11 +720,10 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/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.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
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=
|
||||||
@@ -736,9 +738,8 @@ golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf h1:oXVg4h2qJDd9htKxb5SCpFBHL
|
|||||||
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys=
|
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf/go.mod h1:yh0Ynu2b5ZUe3MQfp2nM0ecK7wsgouWTDN0FNeJuIys=
|
||||||
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 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
|
||||||
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.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
|
||||||
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=
|
||||||
@@ -751,7 +752,6 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI
|
|||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
|
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
@@ -828,8 +828,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
|||||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
golang.org/x/net v0.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.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
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=
|
||||||
@@ -838,8 +838,6 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4Iltr
|
|||||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||||
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
|
|
||||||
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -852,9 +850,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
|
||||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@@ -947,21 +944,20 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.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.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
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=
|
||||||
@@ -971,17 +967,15 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
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.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
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=
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
|
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
|
||||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
@@ -1030,7 +1024,6 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
|
|||||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
@@ -1046,8 +1039,8 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
|
|||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de h1:qDZ+lyO5jC9RNJ7ANJA0GWXk3pSn0Fu5SlcAIlgw+6w=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de h1:qDZ+lyO5jC9RNJ7ANJA0GWXk3pSn0Fu5SlcAIlgw+6w=
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de/go.mod h1:Q2XNgour4QSkFj0BWCkVlW0HWJwQgNMsMahpSlI0Eno=
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de/go.mod h1:Q2XNgour4QSkFj0BWCkVlW0HWJwQgNMsMahpSlI0Eno=
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
golang.zx2c4.com/wireguard/windows v0.5.1 h1:OnYw96PF+CsIMrqWo5QP3Q59q5hY1rFErk/yN3cS+JQ=
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
|
golang.zx2c4.com/wireguard/windows v0.5.1/go.mod h1:EApyTk/ZNrkbZjurHL1nleDYnsPpJYBO7LZEBCyDAHk=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||||
@@ -1064,15 +1057,12 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
|||||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||||
google.golang.org/api v0.126.0 h1:q4GJq+cAdMAC7XP7njvQ4tvohGLiSlytuL4BQxbIZ+o=
|
|
||||||
google.golang.org/api v0.126.0/go.mod h1:mBwVAtz+87bEN6CbA1GtZPDOqY2R5ONPqJeIlvyo4Aw=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
|
||||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
@@ -1107,10 +1097,8 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
|
|||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||||
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao=
|
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM=
|
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc h1:XSJ8Vk1SWuNr8S18z1NZSziL0CPIXLCCMDOEFtHBOFc=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
@@ -1129,10 +1117,9 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp
|
|||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
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.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.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ=
|
||||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
|
||||||
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=
|
||||||
@@ -1146,16 +1133,14 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
|||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
@@ -1164,8 +1149,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
|||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
|
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline
|
|||||||
|
|
||||||
// otherwise, make a STUN request to discover the address
|
// otherwise, make a STUN request to discover the address
|
||||||
// or wait for already sent request to complete
|
// or wait for already sent request to complete
|
||||||
waitAddrReceived, err := m.sendSTUN(serverAddr)
|
waitAddrReceived, err := m.sendStun(serverAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("%s: %s", "failed to send STUN packet", err)
|
return nil, fmt.Errorf("%s: %s", "failed to send STUN packet", err)
|
||||||
}
|
}
|
||||||
@@ -218,31 +218,23 @@ func (m *UniversalUDPMuxDefault) GetXORMappedAddr(serverAddr net.Addr, deadline
|
|||||||
select {
|
select {
|
||||||
case <-waitAddrReceived:
|
case <-waitAddrReceived:
|
||||||
// when channel closed, addr was obtained
|
// when channel closed, addr was obtained
|
||||||
var addr *stun.XORMappedAddress
|
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
// A very odd case that mappedAddr is nil.
|
mappedAddr := *m.xorMappedMap[serverAddr.String()]
|
||||||
// Can happen when the deadline property is larger than params.XORMappedAddrCacheTTL.
|
|
||||||
// Or when we don't receive a response to our m.sendSTUN request (the response is handled asynchronously) and
|
|
||||||
// the XORMapped expires meanwhile triggering a closure of the waitAddrReceived channel.
|
|
||||||
// We protect the code from panic here.
|
|
||||||
if mappedAddr, ok := m.xorMappedMap[serverAddr.String()]; ok {
|
|
||||||
addr = mappedAddr.addr
|
|
||||||
}
|
|
||||||
m.mu.Unlock()
|
m.mu.Unlock()
|
||||||
if addr == nil {
|
if mappedAddr.addr == nil {
|
||||||
return nil, fmt.Errorf("no XOR address mapping")
|
return nil, fmt.Errorf("no XOR address mapping")
|
||||||
}
|
}
|
||||||
return addr, nil
|
return mappedAddr.addr, nil
|
||||||
case <-time.After(deadline):
|
case <-time.After(deadline):
|
||||||
return nil, fmt.Errorf("timeout while waiting for XORMappedAddr")
|
return nil, fmt.Errorf("timeout while waiting for XORMappedAddr")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendSTUN sends a STUN request via UDP conn.
|
// sendStun sends a STUN request via UDP conn.
|
||||||
//
|
//
|
||||||
// The returned channel is closed when the STUN response has been received.
|
// The returned channel is closed when the STUN response has been received.
|
||||||
// Method is safe for concurrent use.
|
// Method is safe for concurrent use.
|
||||||
func (m *UniversalUDPMuxDefault) sendSTUN(serverAddr net.Addr) (chan struct{}, error) {
|
func (m *UniversalUDPMuxDefault) sendStun(serverAddr net.Addr) (chan struct{}, error) {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
package iface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PacketFilter interface for firewall abilities
|
|
||||||
type PacketFilter interface {
|
|
||||||
// DropOutgoing filter outgoing packets from host to external destinations
|
|
||||||
DropOutgoing(packetData []byte) bool
|
|
||||||
|
|
||||||
// DropIncoming filter incoming packets from external sources to host
|
|
||||||
DropIncoming(packetData []byte) bool
|
|
||||||
|
|
||||||
// AddUDPPacketHook calls hook when UDP packet from given direction matched
|
|
||||||
//
|
|
||||||
// Hook function returns flag which indicates should be the matched package dropped or not.
|
|
||||||
// Hook function receives raw network packet data as argument.
|
|
||||||
AddUDPPacketHook(in bool, ip net.IP, dPort uint16, hook func(packet []byte) bool) string
|
|
||||||
|
|
||||||
// RemovePacketHook removes hook by ID
|
|
||||||
RemovePacketHook(hookID string) error
|
|
||||||
|
|
||||||
// SetNetwork of the wireguard interface to which filtering applied
|
|
||||||
SetNetwork(*net.IPNet)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeviceWrapper to override Read or Write of packets
|
|
||||||
type DeviceWrapper struct {
|
|
||||||
tun.Device
|
|
||||||
filter PacketFilter
|
|
||||||
mutex sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDeviceWrapper constructor function
|
|
||||||
func newDeviceWrapper(device tun.Device) *DeviceWrapper {
|
|
||||||
return &DeviceWrapper{
|
|
||||||
Device: device,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read wraps read method with filtering feature
|
|
||||||
func (d *DeviceWrapper) Read(bufs [][]byte, sizes []int, offset int) (n int, err error) {
|
|
||||||
if n, err = d.Device.Read(bufs, sizes, offset); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
d.mutex.RLock()
|
|
||||||
filter := d.filter
|
|
||||||
d.mutex.RUnlock()
|
|
||||||
|
|
||||||
if filter == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
if filter.DropOutgoing(bufs[i][offset : offset+sizes[i]]) {
|
|
||||||
bufs = append(bufs[:i], bufs[i+1:]...)
|
|
||||||
sizes = append(sizes[:i], sizes[i+1:]...)
|
|
||||||
n--
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write wraps write method with filtering feature
|
|
||||||
func (d *DeviceWrapper) Write(bufs [][]byte, offset int) (int, error) {
|
|
||||||
d.mutex.RLock()
|
|
||||||
filter := d.filter
|
|
||||||
d.mutex.RUnlock()
|
|
||||||
|
|
||||||
if filter == nil {
|
|
||||||
return d.Device.Write(bufs, offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredBufs := make([][]byte, 0, len(bufs))
|
|
||||||
dropped := 0
|
|
||||||
for _, buf := range bufs {
|
|
||||||
if !filter.DropIncoming(buf[offset:]) {
|
|
||||||
filteredBufs = append(filteredBufs, buf)
|
|
||||||
dropped++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n, err := d.Device.Write(filteredBufs, offset)
|
|
||||||
n += dropped
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFilter sets packet filter to device
|
|
||||||
func (d *DeviceWrapper) SetFilter(filter PacketFilter) {
|
|
||||||
d.mutex.Lock()
|
|
||||||
d.filter = filter
|
|
||||||
d.mutex.Unlock()
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
package iface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
"github.com/google/gopacket/layers"
|
|
||||||
mocks "github.com/netbirdio/netbird/iface/mocks"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDeviceWrapperRead(t *testing.T) {
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
defer ctrl.Finish()
|
|
||||||
|
|
||||||
t.Run("read ICMP", func(t *testing.T) {
|
|
||||||
ipLayer := &layers.IPv4{
|
|
||||||
Version: 4,
|
|
||||||
TTL: 64,
|
|
||||||
Protocol: layers.IPProtocolICMPv4,
|
|
||||||
SrcIP: net.IP{192, 168, 0, 1},
|
|
||||||
DstIP: net.IP{100, 200, 0, 1},
|
|
||||||
}
|
|
||||||
|
|
||||||
icmpLayer := &layers.ICMPv4{
|
|
||||||
TypeCode: layers.CreateICMPv4TypeCode(layers.ICMPv4TypeEchoRequest, 0),
|
|
||||||
Id: 1,
|
|
||||||
Seq: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer := gopacket.NewSerializeBuffer()
|
|
||||||
err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
|
|
||||||
ipLayer,
|
|
||||||
icmpLayer,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("serialize packet: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mockBufs := [][]byte{{}}
|
|
||||||
mockSizes := []int{0}
|
|
||||||
mockOffset := 0
|
|
||||||
|
|
||||||
tun := mocks.NewMockDevice(ctrl)
|
|
||||||
tun.EXPECT().Read(mockBufs, mockSizes, mockOffset).
|
|
||||||
DoAndReturn(func(bufs [][]byte, sizes []int, offset int) (int, error) {
|
|
||||||
bufs[0] = buffer.Bytes()
|
|
||||||
sizes[0] = len(bufs[0])
|
|
||||||
return 1, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
wrapped := newDeviceWrapper(tun)
|
|
||||||
|
|
||||||
bufs := [][]byte{{}}
|
|
||||||
sizes := []int{0}
|
|
||||||
offset := 0
|
|
||||||
|
|
||||||
n, err := wrapped.Read(bufs, sizes, offset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpeted error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if n != 1 {
|
|
||||||
t.Errorf("expected n=1, got %d", n)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("write TCP", func(t *testing.T) {
|
|
||||||
ipLayer := &layers.IPv4{
|
|
||||||
Version: 4,
|
|
||||||
TTL: 64,
|
|
||||||
Protocol: layers.IPProtocolICMPv4,
|
|
||||||
SrcIP: net.IP{100, 200, 0, 9},
|
|
||||||
DstIP: net.IP{100, 200, 0, 10},
|
|
||||||
}
|
|
||||||
|
|
||||||
// create TCP layer packet
|
|
||||||
tcpLayer := &layers.TCP{
|
|
||||||
SrcPort: layers.TCPPort(34423),
|
|
||||||
DstPort: layers.TCPPort(8080),
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer := gopacket.NewSerializeBuffer()
|
|
||||||
err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
|
|
||||||
ipLayer,
|
|
||||||
tcpLayer,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("serialize packet: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mockBufs := [][]byte{buffer.Bytes()}
|
|
||||||
|
|
||||||
mockBufs[0] = buffer.Bytes()
|
|
||||||
tun := mocks.NewMockDevice(ctrl)
|
|
||||||
tun.EXPECT().Write(mockBufs, 0).Return(1, nil)
|
|
||||||
|
|
||||||
wrapped := newDeviceWrapper(tun)
|
|
||||||
|
|
||||||
bufs := [][]byte{buffer.Bytes()}
|
|
||||||
|
|
||||||
n, err := wrapped.Write(bufs, 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpeted error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if n != 1 {
|
|
||||||
t.Errorf("expected n=1, got %d", n)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("drop write UDP package", func(t *testing.T) {
|
|
||||||
ipLayer := &layers.IPv4{
|
|
||||||
Version: 4,
|
|
||||||
TTL: 64,
|
|
||||||
Protocol: layers.IPProtocolICMPv4,
|
|
||||||
SrcIP: net.IP{100, 200, 0, 11},
|
|
||||||
DstIP: net.IP{100, 200, 0, 20},
|
|
||||||
}
|
|
||||||
|
|
||||||
// create TCP layer packet
|
|
||||||
tcpLayer := &layers.UDP{
|
|
||||||
SrcPort: layers.UDPPort(27278),
|
|
||||||
DstPort: layers.UDPPort(53),
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer := gopacket.NewSerializeBuffer()
|
|
||||||
err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
|
|
||||||
ipLayer,
|
|
||||||
tcpLayer,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("serialize packet: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mockBufs := [][]byte{}
|
|
||||||
|
|
||||||
tun := mocks.NewMockDevice(ctrl)
|
|
||||||
tun.EXPECT().Write(mockBufs, 0).Return(0, nil)
|
|
||||||
|
|
||||||
filter := mocks.NewMockPacketFilter(ctrl)
|
|
||||||
filter.EXPECT().DropIncoming(gomock.Any()).Return(true)
|
|
||||||
|
|
||||||
wrapped := newDeviceWrapper(tun)
|
|
||||||
wrapped.filter = filter
|
|
||||||
|
|
||||||
bufs := [][]byte{buffer.Bytes()}
|
|
||||||
|
|
||||||
n, err := wrapped.Write(bufs, 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpeted error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if n != 0 {
|
|
||||||
t.Errorf("expected n=1, got %d", n)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("drop read UDP package", func(t *testing.T) {
|
|
||||||
ipLayer := &layers.IPv4{
|
|
||||||
Version: 4,
|
|
||||||
TTL: 64,
|
|
||||||
Protocol: layers.IPProtocolICMPv4,
|
|
||||||
SrcIP: net.IP{100, 200, 0, 11},
|
|
||||||
DstIP: net.IP{100, 200, 0, 20},
|
|
||||||
}
|
|
||||||
|
|
||||||
// create TCP layer packet
|
|
||||||
tcpLayer := &layers.UDP{
|
|
||||||
SrcPort: layers.UDPPort(19243),
|
|
||||||
DstPort: layers.UDPPort(1024),
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer := gopacket.NewSerializeBuffer()
|
|
||||||
err := gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
|
|
||||||
ipLayer,
|
|
||||||
tcpLayer,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("serialize packet: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mockBufs := [][]byte{{}}
|
|
||||||
mockSizes := []int{0}
|
|
||||||
mockOffset := 0
|
|
||||||
|
|
||||||
tun := mocks.NewMockDevice(ctrl)
|
|
||||||
tun.EXPECT().Read(mockBufs, mockSizes, mockOffset).
|
|
||||||
DoAndReturn(func(bufs [][]byte, sizes []int, offset int) (int, error) {
|
|
||||||
bufs[0] = buffer.Bytes()
|
|
||||||
sizes[0] = len(bufs[0])
|
|
||||||
return 1, nil
|
|
||||||
})
|
|
||||||
filter := mocks.NewMockPacketFilter(ctrl)
|
|
||||||
filter.EXPECT().DropOutgoing(gomock.Any()).Return(true)
|
|
||||||
|
|
||||||
wrapped := newDeviceWrapper(tun)
|
|
||||||
wrapped.filter = filter
|
|
||||||
|
|
||||||
bufs := [][]byte{{}}
|
|
||||||
sizes := []int{0}
|
|
||||||
offset := 0
|
|
||||||
|
|
||||||
n, err := wrapped.Read(bufs, sizes, offset)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpeted error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if n != 0 {
|
|
||||||
t.Errorf("expected n=0, got %d", n)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package iface
|
package iface
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -23,7 +22,6 @@ type WGIface struct {
|
|||||||
configurer wGConfigurer
|
configurer wGConfigurer
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
userspaceBind bool
|
userspaceBind bool
|
||||||
filter PacketFilter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUserspaceBind indicates whether this interfaces is userspace with bind.ICEBind
|
// IsUserspaceBind indicates whether this interfaces is userspace with bind.ICEBind
|
||||||
@@ -36,6 +34,15 @@ func (w *WGIface) GetBind() *bind.ICEBind {
|
|||||||
return w.tun.iceBind
|
return w.tun.iceBind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create creates a new Wireguard interface, sets a given IP and brings it up.
|
||||||
|
// Will reuse an existing one.
|
||||||
|
func (w *WGIface) Create() error {
|
||||||
|
w.mu.Lock()
|
||||||
|
defer w.mu.Unlock()
|
||||||
|
log.Debugf("create WireGuard interface %s", w.tun.DeviceName())
|
||||||
|
return w.tun.Create()
|
||||||
|
}
|
||||||
|
|
||||||
// Name returns the interface name
|
// Name returns the interface name
|
||||||
func (w *WGIface) Name() string {
|
func (w *WGIface) Name() string {
|
||||||
return w.tun.DeviceName()
|
return w.tun.DeviceName()
|
||||||
@@ -70,12 +77,17 @@ func (w *WGIface) UpdateAddr(newAddr string) error {
|
|||||||
|
|
||||||
// UpdatePeer updates existing Wireguard Peer or creates a new one if doesn't exist
|
// UpdatePeer updates existing Wireguard Peer or creates a new one if doesn't exist
|
||||||
// Endpoint is optional
|
// Endpoint is optional
|
||||||
func (w *WGIface) UpdatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
|
func (w *WGIface) UpdatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint net.Addr, preSharedKey *wgtypes.Key) error {
|
||||||
w.mu.Lock()
|
w.mu.Lock()
|
||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
|
|
||||||
log.Debugf("updating interface %s peer %s: endpoint %s ", w.tun.DeviceName(), peerKey, endpoint)
|
rAddr, err := net.ResolveUDPAddr(endpoint.Network(), endpoint.String())
|
||||||
return w.configurer.updatePeer(peerKey, allowedIps, keepAlive, endpoint, preSharedKey)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("updating interface %s peer %s, endpoint %s ", w.tun.DeviceName(), peerKey, endpoint)
|
||||||
|
return w.configurer.updatePeer(peerKey, allowedIps, keepAlive, rAddr, preSharedKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemovePeer removes a Wireguard Peer from the interface iface
|
// RemovePeer removes a Wireguard Peer from the interface iface
|
||||||
@@ -111,35 +123,3 @@ func (w *WGIface) Close() error {
|
|||||||
defer w.mu.Unlock()
|
defer w.mu.Unlock()
|
||||||
return w.tun.Close()
|
return w.tun.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFilter sets packet filters for the userspace impelemntation
|
|
||||||
func (w *WGIface) SetFilter(filter PacketFilter) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
if w.tun.wrapper == nil {
|
|
||||||
return fmt.Errorf("userspace packet filtering not handled on this device")
|
|
||||||
}
|
|
||||||
|
|
||||||
w.filter = filter
|
|
||||||
w.filter.SetNetwork(w.tun.address.Network)
|
|
||||||
|
|
||||||
w.tun.wrapper.SetFilter(filter)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFilter returns packet filter used by interface if it uses userspace device implementation
|
|
||||||
func (w *WGIface) GetFilter() PacketFilter {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
return w.filter
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDevice to interact with raw device (with filtering)
|
|
||||||
func (w *WGIface) GetDevice() *DeviceWrapper {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
|
|
||||||
return w.tun.wrapper
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
package iface
|
package iface
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewWGIFace Creates a new WireGuard interface instance
|
// NewWGIFace Creates a new WireGuard interface instance
|
||||||
func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
func NewWGIFace(ifaceName string, address string, mtu int, routes []string, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
||||||
wgIFace := &WGIface{
|
wgIFace := &WGIface{
|
||||||
mu: sync.Mutex{},
|
mu: sync.Mutex{},
|
||||||
}
|
}
|
||||||
@@ -19,7 +17,7 @@ func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter
|
|||||||
return wgIFace, err
|
return wgIFace, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tun := newTunDevice(wgAddress, mtu, tunAdapter, transportNet)
|
tun := newTunDevice(wgAddress, mtu, routes, tunAdapter, transportNet)
|
||||||
wgIFace.tun = tun
|
wgIFace.tun = tun
|
||||||
|
|
||||||
wgIFace.configurer = newWGConfigurer(tun)
|
wgIFace.configurer = newWGConfigurer(tun)
|
||||||
@@ -28,17 +26,3 @@ func NewWGIFace(ifaceName string, address string, mtu int, tunAdapter TunAdapter
|
|||||||
|
|
||||||
return wgIFace, nil
|
return wgIFace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOnMobile creates a new Wireguard interface, sets a given IP and brings it up.
|
|
||||||
// Will reuse an existing one.
|
|
||||||
func (w *WGIface) CreateOnMobile(mIFaceArgs MobileIFaceArguments) error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
log.Debugf("create WireGuard interface %s", w.tun.DeviceName())
|
|
||||||
return w.tun.Create(mIFaceArgs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create this function make sense on mobile only
|
|
||||||
func (w *WGIface) Create() error {
|
|
||||||
return fmt.Errorf("this function has not implemented on mobile")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,15 +3,13 @@
|
|||||||
package iface
|
package iface
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewWGIFace Creates a new WireGuard interface instance
|
// NewWGIFace Creates a new WireGuard interface instance
|
||||||
func NewWGIFace(iFaceName string, address string, mtu int, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
func NewWGIFace(iFaceName string, address string, mtu int, routes []string, tunAdapter TunAdapter, transportNet transport.Net) (*WGIface, error) {
|
||||||
wgIFace := &WGIface{
|
wgIFace := &WGIface{
|
||||||
mu: sync.Mutex{},
|
mu: sync.Mutex{},
|
||||||
}
|
}
|
||||||
@@ -27,17 +25,3 @@ func NewWGIFace(iFaceName string, address string, mtu int, tunAdapter TunAdapter
|
|||||||
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
|
wgIFace.userspaceBind = !WireGuardModuleIsLoaded()
|
||||||
return wgIFace, nil
|
return wgIFace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateOnMobile this function make sense on mobile only
|
|
||||||
func (w *WGIface) CreateOnMobile(mIFaceArgs MobileIFaceArguments) error {
|
|
||||||
return fmt.Errorf("this function has not implemented on non mobile")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create creates a new Wireguard interface, sets a given IP and brings it up.
|
|
||||||
// Will reuse an existing one.
|
|
||||||
func (w *WGIface) Create() error {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
log.Debugf("create WireGuard interface %s", w.tun.DeviceName())
|
|
||||||
return w.tun.Create()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func TestWGIface_UpdateAddr(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
iface, err := NewWGIFace(ifaceName, addr, DefaultMTU, nil, newNet)
|
iface, err := NewWGIFace(ifaceName, addr, DefaultMTU, nil, nil, newNet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ func Test_CreateInterface(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, nil, newNet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ func Test_Close(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, nil, newNet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -168,7 +168,7 @@ func Test_ConfigureInterface(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, nil, newNet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ func Test_UpdatePeer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, nil, newNet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -282,7 +282,7 @@ func Test_RemovePeer(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, newNet)
|
iface, err := NewWGIFace(ifaceName, wgIP, DefaultMTU, nil, nil, newNet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -335,7 +335,7 @@ func Test_ConnectPeers(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface1, err := NewWGIFace(peer1ifaceName, peer1wgIP, DefaultMTU, nil, newNet)
|
iface1, err := NewWGIFace(peer1ifaceName, peer1wgIP, DefaultMTU, nil, nil, newNet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -356,7 +356,7 @@ func Test_ConnectPeers(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
iface2, err := NewWGIFace(peer2ifaceName, peer2wgIP, DefaultMTU, nil, newNet)
|
iface2, err := NewWGIFace(peer2ifaceName, peer2wgIP, DefaultMTU, nil, nil, newNet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
## Mocks
|
|
||||||
|
|
||||||
To generate (or refresh) mocks from iface package interfaces please install [mockgen](https://github.com/golang/mock).
|
|
||||||
Run this command to update PacketFilter mock:
|
|
||||||
```bash
|
|
||||||
mockgen -destination iface/mocks/filter.go -package mocks github.com/netbirdio/netbird/iface PacketFilter
|
|
||||||
```
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: github.com/netbirdio/netbird/iface (interfaces: PacketFilter)
|
|
||||||
|
|
||||||
// Package mocks is a generated GoMock package.
|
|
||||||
package mocks
|
|
||||||
|
|
||||||
import (
|
|
||||||
net "net"
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockPacketFilter is a mock of PacketFilter interface.
|
|
||||||
type MockPacketFilter struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockPacketFilterMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockPacketFilterMockRecorder is the mock recorder for MockPacketFilter.
|
|
||||||
type MockPacketFilterMockRecorder struct {
|
|
||||||
mock *MockPacketFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockPacketFilter creates a new mock instance.
|
|
||||||
func NewMockPacketFilter(ctrl *gomock.Controller) *MockPacketFilter {
|
|
||||||
mock := &MockPacketFilter{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockPacketFilterMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockPacketFilter) EXPECT() *MockPacketFilterMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddUDPPacketHook mocks base method.
|
|
||||||
func (m *MockPacketFilter) AddUDPPacketHook(arg0 bool, arg1 net.IP, arg2 uint16, arg3 func([]byte) bool) string {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "AddUDPPacketHook", arg0, arg1, arg2, arg3)
|
|
||||||
ret0, _ := ret[0].(string)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddUDPPacketHook indicates an expected call of AddUDPPacketHook.
|
|
||||||
func (mr *MockPacketFilterMockRecorder) AddUDPPacketHook(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUDPPacketHook", reflect.TypeOf((*MockPacketFilter)(nil).AddUDPPacketHook), arg0, arg1, arg2, arg3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropIncoming mocks base method.
|
|
||||||
func (m *MockPacketFilter) DropIncoming(arg0 []byte) bool {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "DropIncoming", arg0)
|
|
||||||
ret0, _ := ret[0].(bool)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropIncoming indicates an expected call of DropIncoming.
|
|
||||||
func (mr *MockPacketFilterMockRecorder) DropIncoming(arg0 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropIncoming", reflect.TypeOf((*MockPacketFilter)(nil).DropIncoming), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropOutgoing mocks base method.
|
|
||||||
func (m *MockPacketFilter) DropOutgoing(arg0 []byte) bool {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "DropOutgoing", arg0)
|
|
||||||
ret0, _ := ret[0].(bool)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropOutgoing indicates an expected call of DropOutgoing.
|
|
||||||
func (mr *MockPacketFilterMockRecorder) DropOutgoing(arg0 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropOutgoing", reflect.TypeOf((*MockPacketFilter)(nil).DropOutgoing), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemovePacketHook mocks base method.
|
|
||||||
func (m *MockPacketFilter) RemovePacketHook(arg0 string) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "RemovePacketHook", arg0)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemovePacketHook indicates an expected call of RemovePacketHook.
|
|
||||||
func (mr *MockPacketFilterMockRecorder) RemovePacketHook(arg0 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemovePacketHook", reflect.TypeOf((*MockPacketFilter)(nil).RemovePacketHook), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNetwork mocks base method.
|
|
||||||
func (m *MockPacketFilter) SetNetwork(arg0 *net.IPNet) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
m.ctrl.Call(m, "SetNetwork", arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNetwork indicates an expected call of SetNetwork.
|
|
||||||
func (mr *MockPacketFilterMockRecorder) SetNetwork(arg0 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNetwork", reflect.TypeOf((*MockPacketFilter)(nil).SetNetwork), arg0)
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: github.com/netbirdio/netbird/iface (interfaces: PacketFilter)
|
|
||||||
|
|
||||||
// Package mocks is a generated GoMock package.
|
|
||||||
package mocks
|
|
||||||
|
|
||||||
import (
|
|
||||||
net "net"
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockPacketFilter is a mock of PacketFilter interface.
|
|
||||||
type MockPacketFilter struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockPacketFilterMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockPacketFilterMockRecorder is the mock recorder for MockPacketFilter.
|
|
||||||
type MockPacketFilterMockRecorder struct {
|
|
||||||
mock *MockPacketFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockPacketFilter creates a new mock instance.
|
|
||||||
func NewMockPacketFilter(ctrl *gomock.Controller) *MockPacketFilter {
|
|
||||||
mock := &MockPacketFilter{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockPacketFilterMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockPacketFilter) EXPECT() *MockPacketFilterMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddUDPPacketHook mocks base method.
|
|
||||||
func (m *MockPacketFilter) AddUDPPacketHook(arg0 bool, arg1 net.IP, arg2 uint16, arg3 func(*net.UDPAddr, []byte) bool) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
m.ctrl.Call(m, "AddUDPPacketHook", arg0, arg1, arg2, arg3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddUDPPacketHook indicates an expected call of AddUDPPacketHook.
|
|
||||||
func (mr *MockPacketFilterMockRecorder) AddUDPPacketHook(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUDPPacketHook", reflect.TypeOf((*MockPacketFilter)(nil).AddUDPPacketHook), arg0, arg1, arg2, arg3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropIncoming mocks base method.
|
|
||||||
func (m *MockPacketFilter) DropIncoming(arg0 []byte) bool {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "DropIncoming", arg0)
|
|
||||||
ret0, _ := ret[0].(bool)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropIncoming indicates an expected call of DropIncoming.
|
|
||||||
func (mr *MockPacketFilterMockRecorder) DropIncoming(arg0 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropIncoming", reflect.TypeOf((*MockPacketFilter)(nil).DropIncoming), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropOutgoing mocks base method.
|
|
||||||
func (m *MockPacketFilter) DropOutgoing(arg0 []byte) bool {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "DropOutgoing", arg0)
|
|
||||||
ret0, _ := ret[0].(bool)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropOutgoing indicates an expected call of DropOutgoing.
|
|
||||||
func (mr *MockPacketFilterMockRecorder) DropOutgoing(arg0 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropOutgoing", reflect.TypeOf((*MockPacketFilter)(nil).DropOutgoing), arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNetwork mocks base method.
|
|
||||||
func (m *MockPacketFilter) SetNetwork(arg0 *net.IPNet) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
m.ctrl.Call(m, "SetNetwork", arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNetwork indicates an expected call of SetNetwork.
|
|
||||||
func (mr *MockPacketFilterMockRecorder) SetNetwork(arg0 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNetwork", reflect.TypeOf((*MockPacketFilter)(nil).SetNetwork), arg0)
|
|
||||||
}
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
// Code generated by MockGen. DO NOT EDIT.
|
|
||||||
// Source: golang.zx2c4.com/wireguard/tun (interfaces: Device)
|
|
||||||
|
|
||||||
// Package mocks is a generated GoMock package.
|
|
||||||
package mocks
|
|
||||||
|
|
||||||
import (
|
|
||||||
os "os"
|
|
||||||
reflect "reflect"
|
|
||||||
|
|
||||||
gomock "github.com/golang/mock/gomock"
|
|
||||||
tun "golang.zx2c4.com/wireguard/tun"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MockDevice is a mock of Device interface.
|
|
||||||
type MockDevice struct {
|
|
||||||
ctrl *gomock.Controller
|
|
||||||
recorder *MockDeviceMockRecorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// MockDeviceMockRecorder is the mock recorder for MockDevice.
|
|
||||||
type MockDeviceMockRecorder struct {
|
|
||||||
mock *MockDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMockDevice creates a new mock instance.
|
|
||||||
func NewMockDevice(ctrl *gomock.Controller) *MockDevice {
|
|
||||||
mock := &MockDevice{ctrl: ctrl}
|
|
||||||
mock.recorder = &MockDeviceMockRecorder{mock}
|
|
||||||
return mock
|
|
||||||
}
|
|
||||||
|
|
||||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
|
||||||
func (m *MockDevice) EXPECT() *MockDeviceMockRecorder {
|
|
||||||
return m.recorder
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchSize mocks base method.
|
|
||||||
func (m *MockDevice) BatchSize() int {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "BatchSize")
|
|
||||||
ret0, _ := ret[0].(int)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchSize indicates an expected call of BatchSize.
|
|
||||||
func (mr *MockDeviceMockRecorder) BatchSize() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BatchSize", reflect.TypeOf((*MockDevice)(nil).BatchSize))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close mocks base method.
|
|
||||||
func (m *MockDevice) Close() error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Close")
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close indicates an expected call of Close.
|
|
||||||
func (mr *MockDeviceMockRecorder) Close() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDevice)(nil).Close))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Events mocks base method.
|
|
||||||
func (m *MockDevice) Events() <-chan tun.Event {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Events")
|
|
||||||
ret0, _ := ret[0].(<-chan tun.Event)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Events indicates an expected call of Events.
|
|
||||||
func (mr *MockDeviceMockRecorder) Events() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockDevice)(nil).Events))
|
|
||||||
}
|
|
||||||
|
|
||||||
// File mocks base method.
|
|
||||||
func (m *MockDevice) File() *os.File {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "File")
|
|
||||||
ret0, _ := ret[0].(*os.File)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// File indicates an expected call of File.
|
|
||||||
func (mr *MockDeviceMockRecorder) File() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "File", reflect.TypeOf((*MockDevice)(nil).File))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MTU mocks base method.
|
|
||||||
func (m *MockDevice) MTU() (int, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "MTU")
|
|
||||||
ret0, _ := ret[0].(int)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// MTU indicates an expected call of MTU.
|
|
||||||
func (mr *MockDeviceMockRecorder) MTU() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MTU", reflect.TypeOf((*MockDevice)(nil).MTU))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name mocks base method.
|
|
||||||
func (m *MockDevice) Name() (string, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Name")
|
|
||||||
ret0, _ := ret[0].(string)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name indicates an expected call of Name.
|
|
||||||
func (mr *MockDeviceMockRecorder) Name() *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockDevice)(nil).Name))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read mocks base method.
|
|
||||||
func (m *MockDevice) Read(arg0 [][]byte, arg1 []int, arg2 int) (int, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Read", arg0, arg1, arg2)
|
|
||||||
ret0, _ := ret[0].(int)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read indicates an expected call of Read.
|
|
||||||
func (mr *MockDeviceMockRecorder) Read(arg0, arg1, arg2 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockDevice)(nil).Read), arg0, arg1, arg2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write mocks base method.
|
|
||||||
func (m *MockDevice) Write(arg0 [][]byte, arg1 int) (int, error) {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Write", arg0, arg1)
|
|
||||||
ret0, _ := ret[0].(int)
|
|
||||||
ret1, _ := ret[1].(error)
|
|
||||||
return ret0, ret1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write indicates an expected call of Write.
|
|
||||||
func (mr *MockDeviceMockRecorder) Write(arg0, arg1 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockDevice)(nil).Write), arg0, arg1)
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,5 @@
|
|||||||
package iface
|
package iface
|
||||||
|
|
||||||
type MobileIFaceArguments struct {
|
|
||||||
Routes []string
|
|
||||||
Dns string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetInterface represents a generic network tunnel interface
|
// NetInterface represents a generic network tunnel interface
|
||||||
type NetInterface interface {
|
type NetInterface interface {
|
||||||
Close() error
|
Close() error
|
||||||
|
|||||||
@@ -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, routes string) (int, error)
|
||||||
UpdateAddr(address string) error
|
UpdateAddr(address string) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,28 +15,29 @@ import (
|
|||||||
type tunDevice struct {
|
type tunDevice struct {
|
||||||
address WGAddress
|
address WGAddress
|
||||||
mtu int
|
mtu int
|
||||||
|
routes []string
|
||||||
tunAdapter TunAdapter
|
tunAdapter TunAdapter
|
||||||
iceBind *bind.ICEBind
|
|
||||||
|
|
||||||
fd int
|
fd int
|
||||||
name string
|
name string
|
||||||
device *device.Device
|
device *device.Device
|
||||||
wrapper *DeviceWrapper
|
iceBind *bind.ICEBind
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTunDevice(address WGAddress, mtu int, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
|
func newTunDevice(address WGAddress, mtu int, routes []string, tunAdapter TunAdapter, transportNet transport.Net) *tunDevice {
|
||||||
return &tunDevice{
|
return &tunDevice{
|
||||||
address: address,
|
address: address,
|
||||||
mtu: mtu,
|
mtu: mtu,
|
||||||
|
routes: routes,
|
||||||
tunAdapter: tunAdapter,
|
tunAdapter: tunAdapter,
|
||||||
iceBind: bind.NewICEBind(transportNet),
|
iceBind: bind.NewICEBind(transportNet),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
|
func (t *tunDevice) Create() error {
|
||||||
var err error
|
var err error
|
||||||
routesString := t.routesToString(mIFaceArgs.Routes)
|
routesString := t.routesToString()
|
||||||
t.fd, err = t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, mIFaceArgs.Dns, routesString)
|
t.fd, err = t.tunAdapter.ConfigureInterface(t.address.String(), t.mtu, 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
|
||||||
@@ -48,10 +49,9 @@ func (t *tunDevice) Create(mIFaceArgs MobileIFaceArguments) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.name = name
|
t.name = name
|
||||||
t.wrapper = newDeviceWrapper(tunDevice)
|
|
||||||
|
|
||||||
log.Debugf("attaching to interface %v", name)
|
log.Debugf("attaching to interface %v", name)
|
||||||
t.device = device.NewDevice(t.wrapper, t.iceBind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
t.device = device.NewDevice(tunDevice, t.iceBind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))
|
||||||
// without this property mobile devices can discover remote endpoints if the configured one was wrong.
|
// without this property mobile devices can discover remote endpoints if the configured one was wrong.
|
||||||
// this helps with support for the older NetBird clients that had a hardcoded direct mode
|
// this helps with support for the older NetBird clients that had a hardcoded direct mode
|
||||||
//t.device.DisableSomeRoamingForBrokenMobileSemantics()
|
//t.device.DisableSomeRoamingForBrokenMobileSemantics()
|
||||||
@@ -90,6 +90,6 @@ func (t *tunDevice) Close() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunDevice) routesToString(routes []string) string {
|
func (t *tunDevice) routesToString() string {
|
||||||
return strings.Join(routes, ";")
|
return strings.Join(t.routes, ";")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ type tunDevice struct {
|
|||||||
netInterface NetInterface
|
netInterface NetInterface
|
||||||
iceBind *bind.ICEBind
|
iceBind *bind.ICEBind
|
||||||
uapi net.Listener
|
uapi net.Listener
|
||||||
wrapper *DeviceWrapper
|
|
||||||
close chan struct{}
|
close chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,14 +90,9 @@ func (c *tunDevice) createWithUserspace() (NetInterface, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.wrapper = newDeviceWrapper(tunIface)
|
|
||||||
|
|
||||||
// We need to create a wireguard-go device and listen to configuration requests
|
// We need to create a wireguard-go device and listen to configuration requests
|
||||||
tunDev := device.NewDevice(
|
tunDev := device.NewDevice(tunIface, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] "))
|
||||||
c.wrapper,
|
|
||||||
c.iceBind,
|
|
||||||
device.NewLogger(device.LogLevelSilent, "[netbird] "),
|
|
||||||
)
|
|
||||||
err = tunDev.Up()
|
err = tunDev.Up()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tunIface.Close()
|
_ = tunIface.Close()
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ package iface
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
|
||||||
|
|
||||||
"github.com/pion/transport/v2"
|
"github.com/pion/transport/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
"golang.zx2c4.com/wireguard/device"
|
"golang.zx2c4.com/wireguard/device"
|
||||||
"golang.zx2c4.com/wireguard/ipc"
|
"golang.zx2c4.com/wireguard/ipc"
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
@@ -23,7 +21,6 @@ type tunDevice struct {
|
|||||||
iceBind *bind.ICEBind
|
iceBind *bind.ICEBind
|
||||||
mtu int
|
mtu int
|
||||||
uapi net.Listener
|
uapi net.Listener
|
||||||
wrapper *DeviceWrapper
|
|
||||||
close chan struct{}
|
close chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +50,6 @@ func (c *tunDevice) createWithUserspace() (NetInterface, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
c.wrapper = newDeviceWrapper(tunIface)
|
|
||||||
|
|
||||||
// We need to create a wireguard-go device and listen to configuration requests
|
// We need to create a wireguard-go device and listen to configuration requests
|
||||||
tunDev := device.NewDevice(tunIface, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] "))
|
tunDev := device.NewDevice(tunIface, c.iceBind, device.NewLogger(device.LogLevelSilent, "[netbird] "))
|
||||||
@@ -63,22 +59,6 @@ func (c *tunDevice) createWithUserspace() (NetInterface, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
luid := winipcfg.LUID(tunIface.(*tun.NativeTun).LUID())
|
|
||||||
|
|
||||||
nbiface, err := luid.IPInterface(windows.AF_INET)
|
|
||||||
if err != nil {
|
|
||||||
_ = tunIface.Close()
|
|
||||||
return nil, fmt.Errorf("got error when getting ip interface %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nbiface.NLMTU = uint32(c.mtu)
|
|
||||||
|
|
||||||
err = nbiface.Set()
|
|
||||||
if err != nil {
|
|
||||||
_ = tunIface.Close()
|
|
||||||
return nil, fmt.Errorf("got error when getting setting the interface mtu: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.uapi, err = c.getUAPI(c.name)
|
c.uapi, err = c.getUAPI(c.name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tunIface.Close()
|
_ = tunIface.Close()
|
||||||
@@ -162,7 +142,7 @@ func (c *tunDevice) assignAddr() error {
|
|||||||
tunDev := c.netInterface.(*tun.NativeTun)
|
tunDev := c.netInterface.(*tun.NativeTun)
|
||||||
luid := winipcfg.LUID(tunDev.LUID())
|
luid := winipcfg.LUID(tunDev.LUID())
|
||||||
log.Debugf("adding address %s to interface: %s", c.address.IP, c.name)
|
log.Debugf("adding address %s to interface: %s", c.address.IP, c.name)
|
||||||
return luid.SetIPAddresses([]netip.Prefix{netip.MustParsePrefix(c.address.String())})
|
return luid.SetIPAddresses([]net.IPNet{{c.address.IP, c.address.Network.Mask}})
|
||||||
}
|
}
|
||||||
|
|
||||||
// getUAPI returns a Listener
|
// getUAPI returns a Listener
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ NETBIRD_TOKEN_SOURCE=${NETBIRD_TOKEN_SOURCE:-accessToken}
|
|||||||
# exports
|
# exports
|
||||||
export NETBIRD_DOMAIN
|
export NETBIRD_DOMAIN
|
||||||
export NETBIRD_AUTH_CLIENT_ID
|
export NETBIRD_AUTH_CLIENT_ID
|
||||||
export NETBIRD_AUTH_CLIENT_SECRET
|
|
||||||
export NETBIRD_AUTH_AUDIENCE
|
export NETBIRD_AUTH_AUDIENCE
|
||||||
export NETBIRD_AUTH_AUTHORITY
|
export NETBIRD_AUTH_AUTHORITY
|
||||||
export NETBIRD_USE_AUTH0
|
export NETBIRD_USE_AUTH0
|
||||||
|
|||||||
@@ -1,29 +1,34 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
if ! which curl >/dev/null 2>&1; then
|
if ! which curl > /dev/null 2>&1
|
||||||
echo "This script uses curl fetch OpenID configuration from IDP."
|
then
|
||||||
echo "Please install curl and re-run the script https://curl.se/"
|
echo "This script uses curl fetch OpenID configuration from IDP."
|
||||||
echo ""
|
echo "Please install curl and re-run the script https://curl.se/"
|
||||||
exit 1
|
echo ""
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! which jq >/dev/null 2>&1; then
|
if ! which jq > /dev/null 2>&1
|
||||||
echo "This script uses jq to load OpenID configuration from IDP."
|
then
|
||||||
echo "Please install jq and re-run the script https://stedolan.github.io/jq/"
|
echo "This script uses jq to load OpenID configuration from IDP."
|
||||||
echo ""
|
echo "Please install jq and re-run the script https://stedolan.github.io/jq/"
|
||||||
exit 1
|
echo ""
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
source setup.env
|
source setup.env
|
||||||
source base.setup.env
|
source base.setup.env
|
||||||
|
|
||||||
if ! which envsubst >/dev/null 2>&1; then
|
if ! which envsubst > /dev/null 2>&1
|
||||||
|
then
|
||||||
echo "envsubst is needed to run this script"
|
echo "envsubst is needed to run this script"
|
||||||
if [[ $(uname) == "Darwin" ]]; then
|
if [[ $(uname) == "Darwin" ]]
|
||||||
|
then
|
||||||
echo "you can install it with homebrew (https://brew.sh):"
|
echo "you can install it with homebrew (https://brew.sh):"
|
||||||
echo "brew install gettext"
|
echo "brew install gettext"
|
||||||
else
|
else
|
||||||
if which apt-get >/dev/null 2>&1; then
|
if which apt-get > /dev/null 2>&1
|
||||||
|
then
|
||||||
echo "you can install it by running"
|
echo "you can install it by running"
|
||||||
echo "apt-get update && apt-get install gettext-base"
|
echo "apt-get update && apt-get install gettext-base"
|
||||||
else
|
else
|
||||||
@@ -33,7 +38,8 @@ if ! which envsubst >/dev/null 2>&1; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "x-$NETBIRD_DOMAIN" == "x-" ]]; then
|
if [[ "x-$NETBIRD_DOMAIN" == "x-" ]]
|
||||||
|
then
|
||||||
echo NETBIRD_DOMAIN is not set, please update your setup.env file
|
echo NETBIRD_DOMAIN is not set, please update your setup.env file
|
||||||
echo If you are migrating from old versions, you migh need to update your variables prefixes from
|
echo If you are migrating from old versions, you migh need to update your variables prefixes from
|
||||||
echo WIRETRUSTEE_.. TO NETBIRD_
|
echo WIRETRUSTEE_.. TO NETBIRD_
|
||||||
@@ -41,7 +47,8 @@ if [[ "x-$NETBIRD_DOMAIN" == "x-" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# local development or tests
|
# local development or tests
|
||||||
if [[ $NETBIRD_DOMAIN == "localhost" || $NETBIRD_DOMAIN == "127.0.0.1" ]]; then
|
if [[ $NETBIRD_DOMAIN == "localhost" || $NETBIRD_DOMAIN == "127.0.0.1" ]]
|
||||||
|
then
|
||||||
export NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN="netbird.selfhosted"
|
export NETBIRD_MGMT_SINGLE_ACCOUNT_MODE_DOMAIN="netbird.selfhosted"
|
||||||
export NETBIRD_MGMT_API_ENDPOINT=http://$NETBIRD_DOMAIN:$NETBIRD_MGMT_API_PORT
|
export NETBIRD_MGMT_API_ENDPOINT=http://$NETBIRD_DOMAIN:$NETBIRD_MGMT_API_PORT
|
||||||
unset NETBIRD_MGMT_API_CERT_FILE
|
unset NETBIRD_MGMT_API_CERT_FILE
|
||||||
@@ -49,8 +56,9 @@ if [[ $NETBIRD_DOMAIN == "localhost" || $NETBIRD_DOMAIN == "127.0.0.1" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# if not provided, we generate a turn password
|
# if not provided, we generate a turn password
|
||||||
if [[ "x-$TURN_PASSWORD" == "x-" ]]; then
|
if [[ "x-$TURN_PASSWORD" == "x-" ]]
|
||||||
export TURN_PASSWORD=$(openssl rand -base64 32 | sed 's/=//g')
|
then
|
||||||
|
export TURN_PASSWORD=$(openssl rand -base64 32|sed 's/=//g')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
MGMT_VOLUMENAME="${VOLUME_PREFIX}${MGMT_VOLUMESUFFIX}"
|
MGMT_VOLUMENAME="${VOLUME_PREFIX}${MGMT_VOLUMESUFFIX}"
|
||||||
@@ -59,13 +67,13 @@ LETSENCRYPT_VOLUMENAME="${VOLUME_PREFIX}${LETSENCRYPT_VOLUMESUFFIX}"
|
|||||||
# if volume with wiretrustee- prefix already exists, use it, else create new with netbird-
|
# if volume with wiretrustee- prefix already exists, use it, else create new with netbird-
|
||||||
OLD_PREFIX='wiretrustee-'
|
OLD_PREFIX='wiretrustee-'
|
||||||
if docker volume ls | grep -q "${OLD_PREFIX}${MGMT_VOLUMESUFFIX}"; then
|
if docker volume ls | grep -q "${OLD_PREFIX}${MGMT_VOLUMESUFFIX}"; then
|
||||||
MGMT_VOLUMENAME="${OLD_PREFIX}${MGMT_VOLUMESUFFIX}"
|
MGMT_VOLUMENAME="${OLD_PREFIX}${MGMT_VOLUMESUFFIX}"
|
||||||
fi
|
fi
|
||||||
if docker volume ls | grep -q "${OLD_PREFIX}${SIGNAL_VOLUMESUFFIX}"; then
|
if docker volume ls | grep -q "${OLD_PREFIX}${SIGNAL_VOLUMESUFFIX}"; then
|
||||||
SIGNAL_VOLUMENAME="${OLD_PREFIX}${SIGNAL_VOLUMESUFFIX}"
|
SIGNAL_VOLUMENAME="${OLD_PREFIX}${SIGNAL_VOLUMESUFFIX}"
|
||||||
fi
|
fi
|
||||||
if docker volume ls | grep -q "${OLD_PREFIX}${LETSENCRYPT_VOLUMESUFFIX}"; then
|
if docker volume ls | grep -q "${OLD_PREFIX}${LETSENCRYPT_VOLUMESUFFIX}"; then
|
||||||
LETSENCRYPT_VOLUMENAME="${OLD_PREFIX}${LETSENCRYPT_VOLUMESUFFIX}"
|
LETSENCRYPT_VOLUMENAME="${OLD_PREFIX}${LETSENCRYPT_VOLUMESUFFIX}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export MGMT_VOLUMENAME
|
export MGMT_VOLUMENAME
|
||||||
@@ -75,45 +83,47 @@ export LETSENCRYPT_VOLUMENAME
|
|||||||
#backwards compatibility after migrating to generic OIDC with Auth0
|
#backwards compatibility after migrating to generic OIDC with Auth0
|
||||||
if [[ -z "${NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT}" ]]; then
|
if [[ -z "${NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT}" ]]; then
|
||||||
|
|
||||||
if [[ -z "${NETBIRD_AUTH0_DOMAIN}" ]]; then
|
if [[ -z "${NETBIRD_AUTH0_DOMAIN}" ]]; then
|
||||||
# not a backward compatible state
|
# not a backward compatible state
|
||||||
echo "NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT property must be set in the setup.env file"
|
echo "NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT property must be set in the setup.env file"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "It seems like you provided an old setup.env file."
|
echo "It seems like you provided an old setup.env file."
|
||||||
echo "Since the release of v0.8.10, we introduced a new set of properties."
|
echo "Since the release of v0.8.10, we introduced a new set of properties."
|
||||||
echo "The script is backward compatible and will continue automatically."
|
echo "The script is backward compatible and will continue automatically."
|
||||||
echo "In the future versions it will be deprecated. Please refer to the documentation to learn about the changes http://netbird.io/docs/getting-started/self-hosting"
|
echo "In the future versions it will be deprecated. Please refer to the documentation to learn about the changes http://netbird.io/docs/getting-started/self-hosting"
|
||||||
|
|
||||||
export NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="https://${NETBIRD_AUTH0_DOMAIN}/.well-known/openid-configuration"
|
export NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="https://${NETBIRD_AUTH0_DOMAIN}/.well-known/openid-configuration"
|
||||||
export NETBIRD_USE_AUTH0="true"
|
export NETBIRD_USE_AUTH0="true"
|
||||||
export NETBIRD_AUTH_AUDIENCE=${NETBIRD_AUTH0_AUDIENCE}
|
export NETBIRD_AUTH_AUDIENCE=${NETBIRD_AUTH0_AUDIENCE}
|
||||||
export NETBIRD_AUTH_CLIENT_ID=${NETBIRD_AUTH0_CLIENT_ID}
|
export NETBIRD_AUTH_CLIENT_ID=${NETBIRD_AUTH0_CLIENT_ID}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "loading OpenID configuration from ${NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT} to the openid-configuration.json file"
|
echo "loading OpenID configuration from ${NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT} to the openid-configuration.json file"
|
||||||
curl "${NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT}" -q -o openid-configuration.json
|
curl "${NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT}" -q -o openid-configuration.json
|
||||||
|
|
||||||
export NETBIRD_AUTH_AUTHORITY=$(jq -r '.issuer' openid-configuration.json)
|
export NETBIRD_AUTH_AUTHORITY=$( jq -r '.issuer' openid-configuration.json )
|
||||||
export NETBIRD_AUTH_JWT_CERTS=$(jq -r '.jwks_uri' openid-configuration.json)
|
export NETBIRD_AUTH_JWT_CERTS=$( jq -r '.jwks_uri' openid-configuration.json )
|
||||||
export NETBIRD_AUTH_SUPPORTED_SCOPES=$(jq -r '.scopes_supported | join(" ")' openid-configuration.json)
|
export NETBIRD_AUTH_SUPPORTED_SCOPES=$( jq -r '.scopes_supported | join(" ")' openid-configuration.json )
|
||||||
export NETBIRD_AUTH_TOKEN_ENDPOINT=$(jq -r '.token_endpoint' openid-configuration.json)
|
export NETBIRD_AUTH_TOKEN_ENDPOINT=$( jq -r '.token_endpoint' openid-configuration.json )
|
||||||
export NETBIRD_AUTH_DEVICE_AUTH_ENDPOINT=$(jq -r '.device_authorization_endpoint' openid-configuration.json)
|
export NETBIRD_AUTH_DEVICE_AUTH_ENDPOINT=$( jq -r '.device_authorization_endpoint' openid-configuration.json )
|
||||||
|
|
||||||
if [ "$NETBIRD_USE_AUTH0" == "true" ]; then
|
if [ $NETBIRD_USE_AUTH0 == "true" ]
|
||||||
export NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api email_verified"
|
then
|
||||||
|
export NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api email_verified"
|
||||||
else
|
else
|
||||||
export NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api"
|
export NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! -z "${NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID}" ]]; then
|
if [[ ! -z "${NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID}" ]]; then
|
||||||
# user enabled Device Authorization Grant feature
|
# user enabled Device Authorization Grant feature
|
||||||
export NETBIRD_AUTH_DEVICE_AUTH_PROVIDER="hosted"
|
export NETBIRD_AUTH_DEVICE_AUTH_PROVIDER="hosted"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if letsencrypt was disabled
|
# Check if letsencrypt was disabled
|
||||||
if [[ "$NETBIRD_DISABLE_LETSENCRYPT" == "true" ]]; then
|
if [[ "$NETBIRD_DISABLE_LETSENCRYPT" == "true" ]]
|
||||||
|
then
|
||||||
export NETBIRD_DASHBOARD_ENDPOINT="https://$NETBIRD_DOMAIN:443"
|
export NETBIRD_DASHBOARD_ENDPOINT="https://$NETBIRD_DOMAIN:443"
|
||||||
export NETBIRD_SIGNAL_ENDPOINT="https://$NETBIRD_DOMAIN:$NETBIRD_SIGNAL_PORT"
|
export NETBIRD_SIGNAL_ENDPOINT="https://$NETBIRD_DOMAIN:$NETBIRD_SIGNAL_PORT"
|
||||||
|
|
||||||
@@ -135,31 +145,8 @@ if [[ "$NETBIRD_DISABLE_LETSENCRYPT" == "true" ]]; then
|
|||||||
unset NETBIRD_MGMT_API_CERT_KEY_FILE
|
unset NETBIRD_MGMT_API_CERT_KEY_FILE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if management identity provider is set
|
|
||||||
if [ -n "$NETBIRD_MGMT_IDP" ]; then
|
|
||||||
EXTRA_CONFIG={}
|
|
||||||
|
|
||||||
# extract extra config from all env prefixed with NETBIRD_IDP_MGMT_EXTRA_
|
|
||||||
for var in ${!NETBIRD_IDP_MGMT_EXTRA_*}; do
|
|
||||||
# convert key snake case to camel case
|
|
||||||
key=$(
|
|
||||||
echo "${var#NETBIRD_IDP_MGMT_EXTRA_}" | awk -F "_" \
|
|
||||||
'{for (i=1; i<=NF; i++) {output=output substr($i,1,1) tolower(substr($i,2))} print output}'
|
|
||||||
)
|
|
||||||
value="${!var}"
|
|
||||||
|
|
||||||
echo "$var"
|
|
||||||
EXTRA_CONFIG=$(jq --arg k "$key" --arg v "$value" '.[$k] = $v' <<<"$EXTRA_CONFIG")
|
|
||||||
done
|
|
||||||
|
|
||||||
export NETBIRD_MGMT_IDP
|
|
||||||
export NETBIRD_IDP_MGMT_CLIENT_ID
|
|
||||||
export NETBIRD_IDP_MGMT_CLIENT_SECRET
|
|
||||||
export NETBIRD_IDP_MGMT_EXTRA_CONFIG=$EXTRA_CONFIG
|
|
||||||
fi
|
|
||||||
|
|
||||||
env | grep NETBIRD
|
env | grep NETBIRD
|
||||||
|
|
||||||
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 > management.json
|
||||||
envsubst <turnserver.conf.tmpl >turnserver.conf
|
envsubst < turnserver.conf.tmpl > turnserver.conf
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ services:
|
|||||||
# OIDC
|
# OIDC
|
||||||
- AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
- AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
||||||
- AUTH_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
- AUTH_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
||||||
- AUTH_CLIENT_SECRET=$NETBIRD_AUTH_CLIENT_SECRET
|
|
||||||
- AUTH_AUTHORITY=$NETBIRD_AUTH_AUTHORITY
|
- AUTH_AUTHORITY=$NETBIRD_AUTH_AUTHORITY
|
||||||
- USE_AUTH0=$NETBIRD_USE_AUTH0
|
- USE_AUTH0=$NETBIRD_USE_AUTH0
|
||||||
- AUTH_SUPPORTED_SCOPES=$NETBIRD_AUTH_SUPPORTED_SCOPES
|
- AUTH_SUPPORTED_SCOPES=$NETBIRD_AUTH_SUPPORTED_SCOPES
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ services:
|
|||||||
# OIDC
|
# OIDC
|
||||||
- AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
- AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
||||||
- AUTH_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
- AUTH_CLIENT_ID=$NETBIRD_AUTH_CLIENT_ID
|
||||||
- AUTH_CLIENT_SECRET=$NETBIRD_AUTH_CLIENT_SECRET
|
|
||||||
- AUTH_AUTHORITY=$NETBIRD_AUTH_AUTHORITY
|
- AUTH_AUTHORITY=$NETBIRD_AUTH_AUTHORITY
|
||||||
- USE_AUTH0=$NETBIRD_USE_AUTH0
|
- USE_AUTH0=$NETBIRD_USE_AUTH0
|
||||||
- AUTH_SUPPORTED_SCOPES=$NETBIRD_AUTH_SUPPORTED_SCOPES
|
- AUTH_SUPPORTED_SCOPES=$NETBIRD_AUTH_SUPPORTED_SCOPES
|
||||||
|
|||||||
@@ -38,15 +38,7 @@
|
|||||||
"OIDCConfigEndpoint":"$NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT"
|
"OIDCConfigEndpoint":"$NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT"
|
||||||
},
|
},
|
||||||
"IdpManagerConfig": {
|
"IdpManagerConfig": {
|
||||||
"ManagerType": "$NETBIRD_MGMT_IDP",
|
"Manager": "none"
|
||||||
"ClientConfig": {
|
|
||||||
"Issuer": "$NETBIRD_AUTH_AUTHORITY",
|
|
||||||
"TokenEndpoint": "$NETBIRD_AUTH_TOKEN_ENDPOINT",
|
|
||||||
"ClientID": "$NETBIRD_IDP_MGMT_CLIENT_ID",
|
|
||||||
"ClientSecret": "$NETBIRD_IDP_MGMT_CLIENT_SECRET",
|
|
||||||
"GrantType": "client_credentials"
|
|
||||||
},
|
|
||||||
"ExtraConfig": $NETBIRD_IDP_MGMT_EXTRA_CONFIG
|
|
||||||
},
|
},
|
||||||
"DeviceAuthorizationFlow": {
|
"DeviceAuthorizationFlow": {
|
||||||
"Provider": "$NETBIRD_AUTH_DEVICE_AUTH_PROVIDER",
|
"Provider": "$NETBIRD_AUTH_DEVICE_AUTH_PROVIDER",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user