Compare commits
23 Commits
debug-goog
...
feature/pe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd2e549032 | ||
|
|
896599aa57 | ||
|
|
d37af43456 | ||
|
|
544ae0b25f | ||
|
|
a1e9ebb256 | ||
|
|
ce2d14c08e | ||
|
|
52fd9a575a | ||
|
|
9028c3c1f7 | ||
|
|
9357a587e9 | ||
|
|
a47c69c472 | ||
|
|
bbea4c3cc3 | ||
|
|
b7a6cbfaa5 | ||
|
|
e18bf565a2 | ||
|
|
51fa3c92c5 | ||
|
|
d65602f904 | ||
|
|
8d9e1fed5f | ||
|
|
e1eddd1cab | ||
|
|
0fbf72434e | ||
|
|
51f133fdc6 | ||
|
|
d5338c09dc | ||
|
|
8fd4166c53 | ||
|
|
9bc7b9e897 | ||
|
|
db3cba5e0f |
2
.github/ISSUE_TEMPLATE/bug-issue-report.md
vendored
@@ -2,7 +2,7 @@
|
|||||||
name: Bug/Issue report
|
name: Bug/Issue report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ''
|
||||||
labels: ['triage']
|
labels: ['triage-needed']
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
5
.github/workflows/golang-test-darwin.yml
vendored
@@ -35,5 +35,8 @@ jobs:
|
|||||||
- name: Install modules
|
- name: Install modules
|
||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
|
|
||||||
|
- name: check git status
|
||||||
|
run: git --no-pager diff --exit-code
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: NETBIRD_STORE_ENGINE=${{ matrix.store }} go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./...
|
run: NETBIRD_STORE_ENGINE=${{ matrix.store }} go test -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' -timeout 5m -p 1 ./...
|
||||||
|
|||||||
8
.github/workflows/golang-test-linux.yml
vendored
@@ -41,8 +41,11 @@ jobs:
|
|||||||
- name: Install modules
|
- name: Install modules
|
||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
|
|
||||||
|
- name: check git status
|
||||||
|
run: git --no-pager diff --exit-code
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} go test -exec 'sudo --preserve-env=CI' -timeout 5m -p 1 ./...
|
run: CGO_ENABLED=1 GOARCH=${{ matrix.arch }} NETBIRD_STORE_ENGINE=${{ matrix.store }} go test -exec 'sudo --preserve-env=CI,NETBIRD_STORE_ENGINE' -timeout 5m -p 1 ./...
|
||||||
|
|
||||||
test_client_on_docker:
|
test_client_on_docker:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
@@ -69,6 +72,9 @@ jobs:
|
|||||||
- name: Install modules
|
- name: Install modules
|
||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
|
|
||||||
|
- name: check git status
|
||||||
|
run: git --no-pager diff --exit-code
|
||||||
|
|
||||||
- name: Generate Iface Test bin
|
- name: Generate Iface Test bin
|
||||||
run: CGO_ENABLED=0 go test -c -o iface-testing.bin ./iface/
|
run: CGO_ENABLED=0 go test -c -o iface-testing.bin ./iface/
|
||||||
|
|
||||||
|
|||||||
1
.github/workflows/golang-test-windows.yml
vendored
@@ -44,6 +44,7 @@ jobs:
|
|||||||
|
|
||||||
- run: PsExec64 -s -w ${{ github.workspace }} C:\hostedtoolcache\windows\go\${{ steps.go.outputs.go-version }}\x64\bin\go.exe env -w GOMODCACHE=C:\Users\runneradmin\go\pkg\mod
|
- run: PsExec64 -s -w ${{ github.workspace }} C:\hostedtoolcache\windows\go\${{ steps.go.outputs.go-version }}\x64\bin\go.exe env -w GOMODCACHE=C:\Users\runneradmin\go\pkg\mod
|
||||||
- run: PsExec64 -s -w ${{ github.workspace }} C:\hostedtoolcache\windows\go\${{ steps.go.outputs.go-version }}\x64\bin\go.exe env -w GOCACHE=C:\Users\runneradmin\AppData\Local\go-build
|
- run: PsExec64 -s -w ${{ github.workspace }} C:\hostedtoolcache\windows\go\${{ steps.go.outputs.go-version }}\x64\bin\go.exe env -w GOCACHE=C:\Users\runneradmin\AppData\Local\go-build
|
||||||
|
- run: "[Environment]::SetEnvironmentVariable('NETBIRD_STORE_ENGINE', 'jsonfile', 'Machine')"
|
||||||
|
|
||||||
- name: test
|
- name: test
|
||||||
run: PsExec64 -s -w ${{ github.workspace }} cmd.exe /c "C:\hostedtoolcache\windows\go\${{ steps.go.outputs.go-version }}\x64\bin\go.exe test -timeout 5m -p 1 ./... > test-out.txt 2>&1"
|
run: PsExec64 -s -w ${{ github.workspace }} cmd.exe /c "C:\hostedtoolcache\windows\go\${{ steps.go.outputs.go-version }}\x64\bin\go.exe test -timeout 5m -p 1 ./... > test-out.txt 2>&1"
|
||||||
|
|||||||
3
.github/workflows/release.yml
vendored
@@ -190,6 +190,9 @@ jobs:
|
|||||||
-
|
-
|
||||||
name: Install modules
|
name: Install modules
|
||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
|
-
|
||||||
|
name: check git status
|
||||||
|
run: git --no-pager diff --exit-code
|
||||||
-
|
-
|
||||||
name: Run GoReleaser
|
name: Run GoReleaser
|
||||||
id: goreleaser
|
id: goreleaser
|
||||||
|
|||||||
23
.github/workflows/test-infrastructure-files.yml
vendored
@@ -127,6 +127,9 @@ jobs:
|
|||||||
- name: Install modules
|
- name: Install modules
|
||||||
run: go mod tidy
|
run: go mod tidy
|
||||||
|
|
||||||
|
- name: check git status
|
||||||
|
run: git --no-pager diff --exit-code
|
||||||
|
|
||||||
- name: Build management binary
|
- name: Build management binary
|
||||||
working-directory: management
|
working-directory: management
|
||||||
run: CGO_ENABLED=1 go build -o netbird-mgmt main.go
|
run: CGO_ENABLED=1 go build -o netbird-mgmt main.go
|
||||||
@@ -159,6 +162,13 @@ jobs:
|
|||||||
test $count -eq 4
|
test $count -eq 4
|
||||||
working-directory: infrastructure_files/artifacts
|
working-directory: infrastructure_files/artifacts
|
||||||
|
|
||||||
|
- name: test geolocation databases
|
||||||
|
working-directory: infrastructure_files/artifacts
|
||||||
|
run: |
|
||||||
|
sleep 30
|
||||||
|
docker compose exec management ls -l /var/lib/netbird/ | grep -i GeoLite2-City.mmdb
|
||||||
|
docker compose exec management ls -l /var/lib/netbird/ | grep -i geonames.db
|
||||||
|
|
||||||
test-getting-started-script:
|
test-getting-started-script:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@@ -186,3 +196,16 @@ jobs:
|
|||||||
run: test -f zitadel.env
|
run: test -f zitadel.env
|
||||||
- name: test dashboard.env file gen
|
- name: test dashboard.env file gen
|
||||||
run: test -f dashboard.env
|
run: test -f dashboard.env
|
||||||
|
test-download-geolite2-script:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Install jq
|
||||||
|
run: sudo apt-get update && sudo apt-get install -y unzip sqlite3
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: test script
|
||||||
|
run: bash -x infrastructure_files/download-geolite2.sh
|
||||||
|
- name: test mmdb file exists
|
||||||
|
run: test -f GeoLite2-City.mmdb
|
||||||
|
- name: test geonames file exists
|
||||||
|
run: test -f geonames.db
|
||||||
|
|||||||
2
.gitignore
vendored
@@ -29,4 +29,4 @@ infrastructure_files/setup.env
|
|||||||
infrastructure_files/setup-*.env
|
infrastructure_files/setup-*.env
|
||||||
.vscode
|
.vscode
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.db
|
GeoLite2-City*
|
||||||
@@ -63,6 +63,14 @@ linters-settings:
|
|||||||
enable:
|
enable:
|
||||||
- nilness
|
- nilness
|
||||||
|
|
||||||
|
revive:
|
||||||
|
rules:
|
||||||
|
- name: exported
|
||||||
|
severity: warning
|
||||||
|
disabled: false
|
||||||
|
arguments:
|
||||||
|
- "checkPrivateReceivers"
|
||||||
|
- "sayRepetitiveInsteadOfStutters"
|
||||||
tenv:
|
tenv:
|
||||||
# The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.
|
# The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures.
|
||||||
# Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked.
|
# Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked.
|
||||||
@@ -93,6 +101,7 @@ linters:
|
|||||||
- nilerr # finds the code that returns nil even if it checks that the error is not nil
|
- nilerr # finds the code that returns nil even if it checks that the error is not nil
|
||||||
- nilnil # checks that there is no simultaneous return of nil error and an invalid value
|
- nilnil # checks that there is no simultaneous return of nil error and an invalid value
|
||||||
- predeclared # predeclared finds code that shadows one of Go's predeclared identifiers
|
- predeclared # predeclared finds code that shadows one of Go's predeclared identifiers
|
||||||
|
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint.
|
||||||
- sqlclosecheck # checks that sql.Rows and sql.Stmt are closed
|
- sqlclosecheck # checks that sql.Rows and sql.Stmt are closed
|
||||||
- thelper # thelper detects Go test helpers without t.Helper() call and checks the consistency of test helpers.
|
- thelper # thelper detects Go test helpers without t.Helper() call and checks the consistency of test helpers.
|
||||||
- wastedassign # wastedassign finds wasted assignment statements
|
- wastedassign # wastedassign finds wasted assignment statements
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ nfpms:
|
|||||||
contents:
|
contents:
|
||||||
- src: client/ui/netbird.desktop
|
- src: client/ui/netbird.desktop
|
||||||
dst: /usr/share/applications/netbird.desktop
|
dst: /usr/share/applications/netbird.desktop
|
||||||
- src: client/ui/netbird-systemtray-default.png
|
- src: client/ui/netbird-systemtray-connected.png
|
||||||
dst: /usr/share/pixmaps/netbird.png
|
dst: /usr/share/pixmaps/netbird.png
|
||||||
dependencies:
|
dependencies:
|
||||||
- netbird
|
- netbird
|
||||||
@@ -71,7 +71,7 @@ nfpms:
|
|||||||
contents:
|
contents:
|
||||||
- src: client/ui/netbird.desktop
|
- src: client/ui/netbird.desktop
|
||||||
dst: /usr/share/applications/netbird.desktop
|
dst: /usr/share/applications/netbird.desktop
|
||||||
- src: client/ui/netbird-systemtray-default.png
|
- src: client/ui/netbird-systemtray-connected.png
|
||||||
dst: /usr/share/pixmaps/netbird.png
|
dst: /usr/share/pixmaps/netbird.png
|
||||||
dependencies:
|
dependencies:
|
||||||
- netbird
|
- netbird
|
||||||
|
|||||||
31
README.md
@@ -1,6 +1,6 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<strong>:hatching_chick: New Release! Self-hosting in under 5 min.</strong>
|
<strong>:hatching_chick: New Release! Device Posture Checks.</strong>
|
||||||
<a href="https://github.com/netbirdio/netbird#quickstart-with-self-hosted-netbird">
|
<a href="https://docs.netbird.io/how-to/manage-posture-checks">
|
||||||
Learn more
|
Learn more
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@@ -42,25 +42,22 @@
|
|||||||
|
|
||||||
**Secure.** NetBird enables secure remote access by applying granular access policies, while allowing you to manage them intuitively from a single place. Works universally on any infrastructure.
|
**Secure.** NetBird enables secure remote access by applying granular access policies, while allowing you to manage them intuitively from a single place. Works universally on any infrastructure.
|
||||||
|
|
||||||
### Secure peer-to-peer VPN with SSO and MFA in minutes
|
### Open-Source Network Security in a Single Platform
|
||||||
|
|
||||||
https://user-images.githubusercontent.com/700848/197345890-2e2cded5-7b7a-436f-a444-94e80dd24f46.mov
|

|
||||||
|
|
||||||
### Key features
|
### Key features
|
||||||
|
|
||||||
| Connectivity | Management | Automation | Platforms |
|
| Connectivity | Management | Security | Automation | Platforms |
|
||||||
|---------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------|----------------------------------------------------------------------------|---------------------------------------|
|
|------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
|
||||||
| <ul><li> - \[x] Kernel WireGuard </ul></li> | <ul><li> - \[x] [Admin Web UI](https://github.com/netbirdio/dashboard) </ul></li> | <ul><li> - \[x] [Public API](https://docs.netbird.io/api) </ul></li> | <ul><li> - \[x] Linux </ul></li> |
|
| <ul><li> - \[x] Kernel WireGuard </ul></li> | <ul><li> - \[x] [Admin Web UI](https://github.com/netbirdio/dashboard) </ul></li> | <ul><li> - \[x] [SSO & MFA support](https://docs.netbird.io/how-to/installation#running-net-bird-with-sso-login) </ul></li> | <ul><li> - \[x] [Public API](https://docs.netbird.io/api) </ul></li> | <ul><li> - \[x] Linux </ul></li> |
|
||||||
| <ul><li> - \[x] Peer-to-peer connections </ul></li> | <ul><li> - \[x] Auto peer discovery and configuration </ul></li> | <ul><li> - \[x] [Setup keys for bulk network provisioning](https://docs.netbird.io/how-to/register-machines-using-setup-keys) </ul></li> | <ul><li> - \[x] Mac </ul></li> |
|
| <ul><li> - \[x] Peer-to-peer connections </ul></li> | <ul><li> - \[x] Auto peer discovery and configuration </ul></li> | <ul><li> - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access) </ul></li> | <ul><li> - \[x] [Setup keys for bulk network provisioning](https://docs.netbird.io/how-to/register-machines-using-setup-keys) </ul></li> | <ul><li> - \[x] Mac </ul></li> |
|
||||||
| <ul><li> - \[x] Peer-to-peer encryption </ul></li> | <ul><li> - \[x] [IdP integrations](https://docs.netbird.io/selfhosted/identity-providers) </ul></li> | <ul><li> - \[x] [Self-hosting quickstart script](https://docs.netbird.io/selfhosted/selfhosted-quickstart) </ul></li> | <ul><li> - \[x] Windows </ul></li> |
|
| <ul><li> - \[x] Connection relay fallback </ul></li> | <ul><li> - \[x] [IdP integrations](https://docs.netbird.io/selfhosted/identity-providers) </ul></li> | <ul><li> - \[x] [Activity logging](https://docs.netbird.io/how-to/monitor-system-and-network-activity) </ul></li> | <ul><li> - \[x] [Self-hosting quickstart script](https://docs.netbird.io/selfhosted/selfhosted-quickstart) </ul></li> | <ul><li> - \[x] Windows </ul></li> |
|
||||||
| <ul><li> - \[x] Connection relay fallback </ul></li> | <ul><li> - \[x] [SSO & MFA support](https://docs.netbird.io/how-to/installation#running-net-bird-with-sso-login) </ul></li> | <ul><li> - \[x] IdP groups sync with JWT </ul></li> | <ul><li> - \[x] Android </ul></li> |
|
| <ul><li> - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks) </ul></li> | <ul><li> - \[x] [Private DNS](https://docs.netbird.io/how-to/manage-dns-in-your-network) </ul></li> | <ul><li> - \[x] [Device posture checks](https://docs.netbird.io/how-to/manage-posture-checks) </ul></li> | <ul><li> - \[x] IdP groups sync with JWT </ul></li> | <ul><li> - \[x] Android </ul></li> |
|
||||||
| <ul><li> - \[x] [Routes to external networks](https://docs.netbird.io/how-to/routing-traffic-to-private-networks) </ul></li> | <ul><li> - \[x] [Access control - groups & rules](https://docs.netbird.io/how-to/manage-network-access) </ul></li> | | <ul><li> - \[x] iOS </ul></li> |
|
| <ul><li> - \[x] NAT traversal with BPF </ul></li> | <ul><li> - \[x] [Multiuser support](https://docs.netbird.io/how-to/add-users-to-your-network) </ul></li> | <ul><li> - \[x] Peer-to-peer encryption </ul></li> | | <ul><li> - \[x] iOS </ul></li> |
|
||||||
| <ul><li> - \[x] NAT traversal with BPF </ul></li> | <ul><li> - \[x] [Private DNS](https://docs.netbird.io/how-to/manage-dns-in-your-network) </ul></li> | | <ul><li> - \[x] Docker </ul></li> |
|
| | | <ul><li> - \[x] [Quantum-resistance with Rosenpass](https://netbird.io/knowledge-hub/the-first-quantum-resistant-mesh-vpn) </ul></li> | | <ul><li> - \[x] OpenWRT </ul></li> |
|
||||||
| <ul><li> - \[x] Post-quantum-secure connection through [Rosenpass](https://rosenpass.eu) </ul></li> | <ul><li> - \[x] [Multiuser support](https://docs.netbird.io/how-to/add-users-to-your-network) </ul></li> | | <ul><li> - \[x] OpenWRT </ul></li> |
|
| | | <ui><li> - \[x] [Periodic re-authentication](https://docs.netbird.io/how-to/enforce-periodic-user-authentication)</ul></li> | | <ul><li> - \[x] [Serverless](https://docs.netbird.io/how-to/netbird-on-faas) </ul></li> |
|
||||||
| | <ul><li> - \[x] [Activity logging](https://docs.netbird.io/how-to/monitor-system-and-network-activity) </ul></li> | | |
|
| | | | | <ul><li> - \[x] Docker </ul></li> |
|
||||||
| | <ul><li> - \[x] SSH access management </ul></li> | | |
|
|
||||||
|
|
||||||
|
|
||||||
### Quickstart with NetBird Cloud
|
### Quickstart with NetBird Cloud
|
||||||
|
|
||||||
- Download and install NetBird at [https://app.netbird.io/install](https://app.netbird.io/install)
|
- Download and install NetBird at [https://app.netbird.io/install](https://app.netbird.io/install)
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsRead
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
|
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
|
||||||
|
c.recorder.UpdateRosenpass(cfg.RosenpassEnabled, cfg.RosenpassPermissive)
|
||||||
|
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
//nolint
|
//nolint
|
||||||
@@ -109,6 +110,7 @@ func (c *Client) RunWithoutLogin(dns *DNSList, dnsReadyListener DnsReadyListener
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
|
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
|
||||||
|
c.recorder.UpdateRosenpass(cfg.RosenpassEnabled, cfg.RosenpassPermissive)
|
||||||
|
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
//nolint
|
//nolint
|
||||||
|
|||||||
@@ -25,12 +25,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
externalIPMapFlag = "external-ip-map"
|
externalIPMapFlag = "external-ip-map"
|
||||||
dnsResolverAddress = "dns-resolver-address"
|
dnsResolverAddress = "dns-resolver-address"
|
||||||
enableRosenpassFlag = "enable-rosenpass"
|
enableRosenpassFlag = "enable-rosenpass"
|
||||||
preSharedKeyFlag = "preshared-key"
|
rosenpassPermissiveFlag = "rosenpass-permissive"
|
||||||
interfaceNameFlag = "interface-name"
|
preSharedKeyFlag = "preshared-key"
|
||||||
wireguardPortFlag = "wireguard-port"
|
interfaceNameFlag = "interface-name"
|
||||||
|
wireguardPortFlag = "wireguard-port"
|
||||||
|
disableAutoConnectFlag = "disable-auto-connect"
|
||||||
|
serverSSHAllowedFlag = "allow-server-ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -54,8 +57,11 @@ var (
|
|||||||
natExternalIPs []string
|
natExternalIPs []string
|
||||||
customDNSAddress string
|
customDNSAddress string
|
||||||
rosenpassEnabled bool
|
rosenpassEnabled bool
|
||||||
|
rosenpassPermissive bool
|
||||||
|
serverSSHAllowed bool
|
||||||
interfaceName string
|
interfaceName string
|
||||||
wireguardPort uint16
|
wireguardPort uint16
|
||||||
|
autoConnectDisabled bool
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "netbird",
|
Use: "netbird",
|
||||||
Short: "",
|
Short: "",
|
||||||
@@ -126,6 +132,9 @@ func init() {
|
|||||||
`E.g. --dns-resolver-address 127.0.0.1:5053 or --dns-resolver-address ""`,
|
`E.g. --dns-resolver-address 127.0.0.1:5053 or --dns-resolver-address ""`,
|
||||||
)
|
)
|
||||||
upCmd.PersistentFlags().BoolVar(&rosenpassEnabled, enableRosenpassFlag, false, "[Experimental] Enable Rosenpass feature. If enabled, the connection will be post-quantum secured via Rosenpass.")
|
upCmd.PersistentFlags().BoolVar(&rosenpassEnabled, enableRosenpassFlag, false, "[Experimental] Enable Rosenpass feature. If enabled, the connection will be post-quantum secured via Rosenpass.")
|
||||||
|
upCmd.PersistentFlags().BoolVar(&rosenpassPermissive, rosenpassPermissiveFlag, false, "[Experimental] Enable Rosenpass in permissive mode to allow this peer to accept WireGuard connections without requiring Rosenpass functionality from peers that do not have Rosenpass enabled.")
|
||||||
|
upCmd.PersistentFlags().BoolVar(&serverSSHAllowed, serverSSHAllowedFlag, false, "Allow SSH server on peer. If enabled, the SSH server will be permitted")
|
||||||
|
upCmd.PersistentFlags().BoolVar(&autoConnectDisabled, disableAutoConnectFlag, false, "Disables auto-connect feature. If enabled, then the client won't connect automatically when the service starts.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupCloseHandler handles SIGTERM signal and exits with success
|
// SetupCloseHandler handles SIGTERM signal and exits with success
|
||||||
@@ -176,7 +185,7 @@ func FlagNameToEnvVar(cmdFlag string, prefix string) string {
|
|||||||
return prefix + upper
|
return prefix + upper
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialClientGRPCServer returns client connection to the dameno server.
|
// DialClientGRPCServer returns client connection to the daemon server.
|
||||||
func DialClientGRPCServer(ctx context.Context, addr string) (*grpc.ClientConn, error) {
|
func DialClientGRPCServer(ctx context.Context, addr string) (*grpc.ClientConn, error) {
|
||||||
ctx, cancel := context.WithTimeout(ctx, time.Second*3)
|
ctx, cancel := context.WithTimeout(ctx, time.Second*3)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ var installCmd = &cobra.Command{
|
|||||||
cmd.PrintErrln(err)
|
cmd.PrintErrln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Println("Netbird service has been installed")
|
cmd.Println("Netbird service has been installed")
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@@ -106,7 +107,7 @@ var uninstallCmd = &cobra.Command{
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cmd.Println("Netbird has been uninstalled")
|
cmd.Println("Netbird service has been uninstalled")
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type peerStateDetailOutput struct {
|
|||||||
LastWireguardHandshake time.Time `json:"lastWireguardHandshake" yaml:"lastWireguardHandshake"`
|
LastWireguardHandshake time.Time `json:"lastWireguardHandshake" yaml:"lastWireguardHandshake"`
|
||||||
TransferReceived int64 `json:"transferReceived" yaml:"transferReceived"`
|
TransferReceived int64 `json:"transferReceived" yaml:"transferReceived"`
|
||||||
TransferSent int64 `json:"transferSent" yaml:"transferSent"`
|
TransferSent int64 `json:"transferSent" yaml:"transferSent"`
|
||||||
|
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type peersStateOutput struct {
|
type peersStateOutput struct {
|
||||||
@@ -72,16 +73,18 @@ type iceCandidateType struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type statusOutputOverview struct {
|
type statusOutputOverview struct {
|
||||||
Peers peersStateOutput `json:"peers" yaml:"peers"`
|
Peers peersStateOutput `json:"peers" yaml:"peers"`
|
||||||
CliVersion string `json:"cliVersion" yaml:"cliVersion"`
|
CliVersion string `json:"cliVersion" yaml:"cliVersion"`
|
||||||
DaemonVersion string `json:"daemonVersion" yaml:"daemonVersion"`
|
DaemonVersion string `json:"daemonVersion" yaml:"daemonVersion"`
|
||||||
ManagementState managementStateOutput `json:"management" yaml:"management"`
|
ManagementState managementStateOutput `json:"management" yaml:"management"`
|
||||||
SignalState signalStateOutput `json:"signal" yaml:"signal"`
|
SignalState signalStateOutput `json:"signal" yaml:"signal"`
|
||||||
Relays relayStateOutput `json:"relays" yaml:"relays"`
|
Relays relayStateOutput `json:"relays" yaml:"relays"`
|
||||||
IP string `json:"netbirdIp" yaml:"netbirdIp"`
|
IP string `json:"netbirdIp" yaml:"netbirdIp"`
|
||||||
PubKey string `json:"publicKey" yaml:"publicKey"`
|
PubKey string `json:"publicKey" yaml:"publicKey"`
|
||||||
KernelInterface bool `json:"usesKernelInterface" yaml:"usesKernelInterface"`
|
KernelInterface bool `json:"usesKernelInterface" yaml:"usesKernelInterface"`
|
||||||
FQDN string `json:"fqdn" yaml:"fqdn"`
|
FQDN string `json:"fqdn" yaml:"fqdn"`
|
||||||
|
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
|
||||||
|
RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -253,16 +256,18 @@ func convertToStatusOutputOverview(resp *proto.StatusResponse) statusOutputOverv
|
|||||||
peersOverview := mapPeers(resp.GetFullStatus().GetPeers())
|
peersOverview := mapPeers(resp.GetFullStatus().GetPeers())
|
||||||
|
|
||||||
overview := statusOutputOverview{
|
overview := statusOutputOverview{
|
||||||
Peers: peersOverview,
|
Peers: peersOverview,
|
||||||
CliVersion: version.NetbirdVersion(),
|
CliVersion: version.NetbirdVersion(),
|
||||||
DaemonVersion: resp.GetDaemonVersion(),
|
DaemonVersion: resp.GetDaemonVersion(),
|
||||||
ManagementState: managementOverview,
|
ManagementState: managementOverview,
|
||||||
SignalState: signalOverview,
|
SignalState: signalOverview,
|
||||||
Relays: relayOverview,
|
Relays: relayOverview,
|
||||||
IP: pbFullStatus.GetLocalPeerState().GetIP(),
|
IP: pbFullStatus.GetLocalPeerState().GetIP(),
|
||||||
PubKey: pbFullStatus.GetLocalPeerState().GetPubKey(),
|
PubKey: pbFullStatus.GetLocalPeerState().GetPubKey(),
|
||||||
KernelInterface: pbFullStatus.GetLocalPeerState().GetKernelInterface(),
|
KernelInterface: pbFullStatus.GetLocalPeerState().GetKernelInterface(),
|
||||||
FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(),
|
FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(),
|
||||||
|
RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(),
|
||||||
|
RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return overview
|
return overview
|
||||||
@@ -346,6 +351,7 @@ func mapPeers(peers []*proto.PeerState) peersStateOutput {
|
|||||||
LastWireguardHandshake: lastHandshake,
|
LastWireguardHandshake: lastHandshake,
|
||||||
TransferReceived: transferReceived,
|
TransferReceived: transferReceived,
|
||||||
TransferSent: transferSent,
|
TransferSent: transferSent,
|
||||||
|
RosenpassEnabled: pbPeerState.GetRosenpassEnabled(),
|
||||||
}
|
}
|
||||||
|
|
||||||
peersStateDetail = append(peersStateDetail, peerState)
|
peersStateDetail = append(peersStateDetail, peerState)
|
||||||
@@ -451,6 +457,14 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays
|
|||||||
|
|
||||||
peersCountString := fmt.Sprintf("%d/%d Connected", overview.Peers.Connected, overview.Peers.Total)
|
peersCountString := fmt.Sprintf("%d/%d Connected", overview.Peers.Connected, overview.Peers.Total)
|
||||||
|
|
||||||
|
rosenpassEnabledStatus := "false"
|
||||||
|
if overview.RosenpassEnabled {
|
||||||
|
rosenpassEnabledStatus = "true"
|
||||||
|
if overview.RosenpassPermissive {
|
||||||
|
rosenpassEnabledStatus = "true (permissive)" //nolint:gosec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
summary := fmt.Sprintf(
|
summary := fmt.Sprintf(
|
||||||
"Daemon version: %s\n"+
|
"Daemon version: %s\n"+
|
||||||
"CLI version: %s\n"+
|
"CLI version: %s\n"+
|
||||||
@@ -460,6 +474,7 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays
|
|||||||
"FQDN: %s\n"+
|
"FQDN: %s\n"+
|
||||||
"NetBird IP: %s\n"+
|
"NetBird IP: %s\n"+
|
||||||
"Interface type: %s\n"+
|
"Interface type: %s\n"+
|
||||||
|
"Quantum resistance: %s\n"+
|
||||||
"Peers count: %s\n",
|
"Peers count: %s\n",
|
||||||
overview.DaemonVersion,
|
overview.DaemonVersion,
|
||||||
version.NetbirdVersion(),
|
version.NetbirdVersion(),
|
||||||
@@ -469,13 +484,14 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays
|
|||||||
overview.FQDN,
|
overview.FQDN,
|
||||||
interfaceIP,
|
interfaceIP,
|
||||||
interfaceTypeString,
|
interfaceTypeString,
|
||||||
|
rosenpassEnabledStatus,
|
||||||
peersCountString,
|
peersCountString,
|
||||||
)
|
)
|
||||||
return summary
|
return summary
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseToFullDetailSummary(overview statusOutputOverview) string {
|
func parseToFullDetailSummary(overview statusOutputOverview) string {
|
||||||
parsedPeersString := parsePeers(overview.Peers)
|
parsedPeersString := parsePeers(overview.Peers, overview.RosenpassEnabled, overview.RosenpassPermissive)
|
||||||
summary := parseGeneralSummary(overview, true, true)
|
summary := parseGeneralSummary(overview, true, true)
|
||||||
|
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
@@ -487,7 +503,7 @@ func parseToFullDetailSummary(overview statusOutputOverview) string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePeers(peers peersStateOutput) string {
|
func parsePeers(peers peersStateOutput, rosenpassEnabled, rosenpassPermissive bool) string {
|
||||||
var (
|
var (
|
||||||
peersString = ""
|
peersString = ""
|
||||||
)
|
)
|
||||||
@@ -518,9 +534,26 @@ func parsePeers(peers peersStateOutput) string {
|
|||||||
lastStatusUpdate = peerState.LastStatusUpdate.Format("2006-01-02 15:04:05")
|
lastStatusUpdate = peerState.LastStatusUpdate.Format("2006-01-02 15:04:05")
|
||||||
}
|
}
|
||||||
|
|
||||||
lastWireguardHandshake := "-"
|
lastWireGuardHandshake := "-"
|
||||||
if !peerState.LastWireguardHandshake.IsZero() && peerState.LastWireguardHandshake != time.Unix(0, 0) {
|
if !peerState.LastWireguardHandshake.IsZero() && peerState.LastWireguardHandshake != time.Unix(0, 0) {
|
||||||
lastWireguardHandshake = peerState.LastWireguardHandshake.Format("2006-01-02 15:04:05")
|
lastWireGuardHandshake = peerState.LastWireguardHandshake.Format("2006-01-02 15:04:05")
|
||||||
|
}
|
||||||
|
|
||||||
|
rosenpassEnabledStatus := "false"
|
||||||
|
if rosenpassEnabled {
|
||||||
|
if peerState.RosenpassEnabled {
|
||||||
|
rosenpassEnabledStatus = "true"
|
||||||
|
} else {
|
||||||
|
if rosenpassPermissive {
|
||||||
|
rosenpassEnabledStatus = "false (remote didn't enable quantum resistance)"
|
||||||
|
} else {
|
||||||
|
rosenpassEnabledStatus = "false (connection won't work without a permissive mode)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if peerState.RosenpassEnabled {
|
||||||
|
rosenpassEnabledStatus = "false (connection might not work without a remote permissive mode)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
peerString := fmt.Sprintf(
|
peerString := fmt.Sprintf(
|
||||||
@@ -534,8 +567,9 @@ func parsePeers(peers peersStateOutput) string {
|
|||||||
" ICE candidate (Local/Remote): %s/%s\n"+
|
" ICE candidate (Local/Remote): %s/%s\n"+
|
||||||
" ICE candidate endpoints (Local/Remote): %s/%s\n"+
|
" ICE candidate endpoints (Local/Remote): %s/%s\n"+
|
||||||
" Last connection update: %s\n"+
|
" Last connection update: %s\n"+
|
||||||
" Last Wireguard handshake: %s\n"+
|
" Last WireGuard handshake: %s\n"+
|
||||||
" Transfer status (received/sent) %s/%s\n",
|
" Transfer status (received/sent) %s/%s\n"+
|
||||||
|
" Quantum resistance: %s\n",
|
||||||
peerState.FQDN,
|
peerState.FQDN,
|
||||||
peerState.IP,
|
peerState.IP,
|
||||||
peerState.PubKey,
|
peerState.PubKey,
|
||||||
@@ -547,9 +581,10 @@ func parsePeers(peers peersStateOutput) string {
|
|||||||
localICEEndpoint,
|
localICEEndpoint,
|
||||||
remoteICEEndpoint,
|
remoteICEEndpoint,
|
||||||
lastStatusUpdate,
|
lastStatusUpdate,
|
||||||
lastWireguardHandshake,
|
lastWireGuardHandshake,
|
||||||
toIEC(peerState.TransferReceived),
|
toIEC(peerState.TransferReceived),
|
||||||
toIEC(peerState.TransferSent),
|
toIEC(peerState.TransferSent),
|
||||||
|
rosenpassEnabledStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
peersString += peerString
|
peersString += peerString
|
||||||
|
|||||||
@@ -231,7 +231,8 @@ func TestParsingToJSON(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"lastWireguardHandshake": "2001-01-01T01:01:02Z",
|
"lastWireguardHandshake": "2001-01-01T01:01:02Z",
|
||||||
"transferReceived": 200,
|
"transferReceived": 200,
|
||||||
"transferSent": 100
|
"transferSent": 100,
|
||||||
|
"quantumResistance":false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fqdn": "peer-2.awesome-domain.com",
|
"fqdn": "peer-2.awesome-domain.com",
|
||||||
@@ -251,7 +252,8 @@ func TestParsingToJSON(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"lastWireguardHandshake": "2002-02-02T02:02:03Z",
|
"lastWireguardHandshake": "2002-02-02T02:02:03Z",
|
||||||
"transferReceived": 2000,
|
"transferReceived": 2000,
|
||||||
"transferSent": 1000
|
"transferSent": 1000,
|
||||||
|
"quantumResistance":false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -286,7 +288,9 @@ func TestParsingToJSON(t *testing.T) {
|
|||||||
"netbirdIp": "192.168.178.100/16",
|
"netbirdIp": "192.168.178.100/16",
|
||||||
"publicKey": "Some-Pub-Key",
|
"publicKey": "Some-Pub-Key",
|
||||||
"usesKernelInterface": true,
|
"usesKernelInterface": true,
|
||||||
"fqdn": "some-localhost.awesome-domain.com"
|
"fqdn": "some-localhost.awesome-domain.com",
|
||||||
|
"quantumResistance":false,
|
||||||
|
"quantumResistancePermissive":false
|
||||||
}`
|
}`
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
@@ -320,6 +324,7 @@ func TestParsingToYAML(t *testing.T) {
|
|||||||
lastWireguardHandshake: 2001-01-01T01:01:02Z
|
lastWireguardHandshake: 2001-01-01T01:01:02Z
|
||||||
transferReceived: 200
|
transferReceived: 200
|
||||||
transferSent: 100
|
transferSent: 100
|
||||||
|
quantumResistance: false
|
||||||
- fqdn: peer-2.awesome-domain.com
|
- fqdn: peer-2.awesome-domain.com
|
||||||
netbirdIp: 192.168.178.102
|
netbirdIp: 192.168.178.102
|
||||||
publicKey: Pubkey2
|
publicKey: Pubkey2
|
||||||
@@ -336,6 +341,7 @@ func TestParsingToYAML(t *testing.T) {
|
|||||||
lastWireguardHandshake: 2002-02-02T02:02:03Z
|
lastWireguardHandshake: 2002-02-02T02:02:03Z
|
||||||
transferReceived: 2000
|
transferReceived: 2000
|
||||||
transferSent: 1000
|
transferSent: 1000
|
||||||
|
quantumResistance: false
|
||||||
cliVersion: development
|
cliVersion: development
|
||||||
daemonVersion: 0.14.1
|
daemonVersion: 0.14.1
|
||||||
management:
|
management:
|
||||||
@@ -360,6 +366,8 @@ netbirdIp: 192.168.178.100/16
|
|||||||
publicKey: Some-Pub-Key
|
publicKey: Some-Pub-Key
|
||||||
usesKernelInterface: true
|
usesKernelInterface: true
|
||||||
fqdn: some-localhost.awesome-domain.com
|
fqdn: some-localhost.awesome-domain.com
|
||||||
|
quantumResistance: false
|
||||||
|
quantumResistancePermissive: false
|
||||||
`
|
`
|
||||||
|
|
||||||
assert.Equal(t, expectedYAML, yaml)
|
assert.Equal(t, expectedYAML, yaml)
|
||||||
@@ -380,8 +388,9 @@ func TestParsingToDetail(t *testing.T) {
|
|||||||
ICE candidate (Local/Remote): -/-
|
ICE candidate (Local/Remote): -/-
|
||||||
ICE candidate endpoints (Local/Remote): -/-
|
ICE candidate endpoints (Local/Remote): -/-
|
||||||
Last connection update: 2001-01-01 01:01:01
|
Last connection update: 2001-01-01 01:01:01
|
||||||
Last Wireguard handshake: 2001-01-01 01:01:02
|
Last WireGuard handshake: 2001-01-01 01:01:02
|
||||||
Transfer status (received/sent) 200 B/100 B
|
Transfer status (received/sent) 200 B/100 B
|
||||||
|
Quantum resistance: false
|
||||||
|
|
||||||
peer-2.awesome-domain.com:
|
peer-2.awesome-domain.com:
|
||||||
NetBird IP: 192.168.178.102
|
NetBird IP: 192.168.178.102
|
||||||
@@ -393,8 +402,9 @@ func TestParsingToDetail(t *testing.T) {
|
|||||||
ICE candidate (Local/Remote): relay/prflx
|
ICE candidate (Local/Remote): relay/prflx
|
||||||
ICE candidate endpoints (Local/Remote): 10.0.0.1:10001/10.0.10.1:10002
|
ICE candidate endpoints (Local/Remote): 10.0.0.1:10001/10.0.10.1:10002
|
||||||
Last connection update: 2002-02-02 02:02:02
|
Last connection update: 2002-02-02 02:02:02
|
||||||
Last Wireguard handshake: 2002-02-02 02:02:03
|
Last WireGuard handshake: 2002-02-02 02:02:03
|
||||||
Transfer status (received/sent) 2.0 KiB/1000 B
|
Transfer status (received/sent) 2.0 KiB/1000 B
|
||||||
|
Quantum resistance: false
|
||||||
|
|
||||||
Daemon version: 0.14.1
|
Daemon version: 0.14.1
|
||||||
CLI version: development
|
CLI version: development
|
||||||
@@ -406,6 +416,7 @@ Relays:
|
|||||||
FQDN: some-localhost.awesome-domain.com
|
FQDN: some-localhost.awesome-domain.com
|
||||||
NetBird IP: 192.168.178.100/16
|
NetBird IP: 192.168.178.100/16
|
||||||
Interface type: Kernel
|
Interface type: Kernel
|
||||||
|
Quantum resistance: false
|
||||||
Peers count: 2/2 Connected
|
Peers count: 2/2 Connected
|
||||||
`
|
`
|
||||||
|
|
||||||
@@ -424,6 +435,7 @@ Relays: 1/2 Available
|
|||||||
FQDN: some-localhost.awesome-domain.com
|
FQDN: some-localhost.awesome-domain.com
|
||||||
NetBird IP: 192.168.178.100/16
|
NetBird IP: 192.168.178.100/16
|
||||||
Interface type: Kernel
|
Interface type: Kernel
|
||||||
|
Quantum resistance: false
|
||||||
Peers count: 2/2 Connected
|
Peers count: 2/2 Connected
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
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"
|
||||||
@@ -75,11 +76,8 @@ func startManagement(t *testing.T, config *mgmt.Config) (*grpc.Server, net.Liste
|
|||||||
|
|
||||||
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
|
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
|
||||||
eventStore := &activity.InMemoryEventStore{}
|
eventStore := &activity.InMemoryEventStore{}
|
||||||
if err != nil {
|
iv, _ := integrations.NewIntegratedApproval(eventStore)
|
||||||
return nil, nil
|
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false, iv)
|
||||||
}
|
|
||||||
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "",
|
|
||||||
eventStore, false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,14 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
ic.RosenpassEnabled = &rosenpassEnabled
|
ic.RosenpassEnabled = &rosenpassEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(rosenpassPermissiveFlag).Changed {
|
||||||
|
ic.RosenpassPermissive = &rosenpassPermissive
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(serverSSHAllowedFlag).Changed {
|
||||||
|
ic.ServerSSHAllowed = &serverSSHAllowed
|
||||||
|
}
|
||||||
|
|
||||||
if cmd.Flag(interfaceNameFlag).Changed {
|
if cmd.Flag(interfaceNameFlag).Changed {
|
||||||
if err := parseInterfaceName(interfaceName); err != nil {
|
if err := parseInterfaceName(interfaceName); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -110,6 +118,18 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
ic.PreSharedKey = &preSharedKey
|
ic.PreSharedKey = &preSharedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(disableAutoConnectFlag).Changed {
|
||||||
|
ic.DisableAutoConnect = &autoConnectDisabled
|
||||||
|
|
||||||
|
if autoConnectDisabled {
|
||||||
|
cmd.Println("Autoconnect has been disabled. The client won't connect automatically when the service starts.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !autoConnectDisabled {
|
||||||
|
cmd.Println("Autoconnect has been enabled. The client will connect automatically when the service starts.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
config, err := internal.UpdateOrCreateConfig(ic)
|
config, err := internal.UpdateOrCreateConfig(ic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get config file: %v", err)
|
return fmt.Errorf("get config file: %v", err)
|
||||||
@@ -180,6 +200,18 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
loginRequest.RosenpassEnabled = &rosenpassEnabled
|
loginRequest.RosenpassEnabled = &rosenpassEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(rosenpassPermissiveFlag).Changed {
|
||||||
|
loginRequest.RosenpassPermissive = &rosenpassPermissive
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(serverSSHAllowedFlag).Changed {
|
||||||
|
loginRequest.ServerSSHAllowed = &serverSSHAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.Flag(disableAutoConnectFlag).Changed {
|
||||||
|
loginRequest.DisableAutoConnect = &autoConnectDisabled
|
||||||
|
}
|
||||||
|
|
||||||
if cmd.Flag(interfaceNameFlag).Changed {
|
if cmd.Flag(interfaceNameFlag).Changed {
|
||||||
if err := parseInterfaceName(interfaceName); err != nil {
|
if err := parseInterfaceName(interfaceName); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ type HTTPClient interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AuthFlowInfo holds information for the OAuth 2.0 authorization flow
|
// AuthFlowInfo holds information for the OAuth 2.0 authorization flow
|
||||||
type AuthFlowInfo struct {
|
type AuthFlowInfo struct { //nolint:revive
|
||||||
DeviceCode string `json:"device_code"`
|
DeviceCode string `json:"device_code"`
|
||||||
UserCode string `json:"user_code"`
|
UserCode string `json:"user_code"`
|
||||||
VerificationURI string `json:"verification_uri"`
|
VerificationURI string `json:"verification_uri"`
|
||||||
|
|||||||
@@ -35,15 +35,18 @@ var defaultInterfaceBlacklist = []string{iface.WgInterfaceDefault, "wt", "utun",
|
|||||||
|
|
||||||
// ConfigInput carries configuration changes to the client
|
// ConfigInput carries configuration changes to the client
|
||||||
type ConfigInput struct {
|
type ConfigInput struct {
|
||||||
ManagementURL string
|
ManagementURL string
|
||||||
AdminURL string
|
AdminURL string
|
||||||
ConfigPath string
|
ConfigPath string
|
||||||
PreSharedKey *string
|
PreSharedKey *string
|
||||||
NATExternalIPs []string
|
ServerSSHAllowed *bool
|
||||||
CustomDNSAddress []byte
|
NATExternalIPs []string
|
||||||
RosenpassEnabled *bool
|
CustomDNSAddress []byte
|
||||||
InterfaceName *string
|
RosenpassEnabled *bool
|
||||||
WireguardPort *int
|
RosenpassPermissive *bool
|
||||||
|
InterfaceName *string
|
||||||
|
WireguardPort *int
|
||||||
|
DisableAutoConnect *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config Configuration type
|
// Config Configuration type
|
||||||
@@ -58,6 +61,8 @@ type Config struct {
|
|||||||
IFaceBlackList []string
|
IFaceBlackList []string
|
||||||
DisableIPv6Discovery bool
|
DisableIPv6Discovery bool
|
||||||
RosenpassEnabled bool
|
RosenpassEnabled bool
|
||||||
|
RosenpassPermissive bool
|
||||||
|
ServerSSHAllowed *bool
|
||||||
// SSHKey is a private SSH key in a PEM format
|
// SSHKey is a private SSH key in a PEM format
|
||||||
SSHKey string
|
SSHKey string
|
||||||
|
|
||||||
@@ -79,6 +84,10 @@ type Config struct {
|
|||||||
NATExternalIPs []string
|
NATExternalIPs []string
|
||||||
// CustomDNSAddress sets the DNS resolver listening address in format ip:port
|
// CustomDNSAddress sets the DNS resolver listening address in format ip:port
|
||||||
CustomDNSAddress string
|
CustomDNSAddress string
|
||||||
|
|
||||||
|
// DisableAutoConnect determines whether the client should not start with the service
|
||||||
|
// it's set to false by default due to backwards compatibility
|
||||||
|
DisableAutoConnect bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadConfig read config file and return with Config. If it is not exists create a new with default values
|
// ReadConfig read config file and return with Config. If it is not exists create a new with default values
|
||||||
@@ -88,6 +97,7 @@ func ReadConfig(configPath string) (*Config, error) {
|
|||||||
if _, err := util.ReadJson(configPath, config); err != nil {
|
if _, err := util.ReadJson(configPath, config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +162,8 @@ func createNewConfig(input ConfigInput) (*Config, error) {
|
|||||||
DisableIPv6Discovery: false,
|
DisableIPv6Discovery: false,
|
||||||
NATExternalIPs: input.NATExternalIPs,
|
NATExternalIPs: input.NATExternalIPs,
|
||||||
CustomDNSAddress: string(input.CustomDNSAddress),
|
CustomDNSAddress: string(input.CustomDNSAddress),
|
||||||
|
ServerSSHAllowed: util.False(),
|
||||||
|
DisableAutoConnect: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultManagementURL, err := parseURL("Management URL", DefaultManagementURL)
|
defaultManagementURL, err := parseURL("Management URL", DefaultManagementURL)
|
||||||
@@ -186,6 +198,14 @@ func createNewConfig(input ConfigInput) (*Config, error) {
|
|||||||
config.RosenpassEnabled = *input.RosenpassEnabled
|
config.RosenpassEnabled = *input.RosenpassEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.RosenpassPermissive != nil {
|
||||||
|
config.RosenpassPermissive = *input.RosenpassPermissive
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.ServerSSHAllowed != nil {
|
||||||
|
config.ServerSSHAllowed = input.ServerSSHAllowed
|
||||||
|
}
|
||||||
|
|
||||||
defaultAdminURL, err := parseURL("Admin URL", DefaultAdminURL)
|
defaultAdminURL, err := parseURL("Admin URL", DefaultAdminURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -280,6 +300,26 @@ func update(input ConfigInput) (*Config, error) {
|
|||||||
refresh = true
|
refresh = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if input.RosenpassPermissive != nil {
|
||||||
|
config.RosenpassPermissive = *input.RosenpassPermissive
|
||||||
|
refresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.DisableAutoConnect != nil {
|
||||||
|
config.DisableAutoConnect = *input.DisableAutoConnect
|
||||||
|
refresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.ServerSSHAllowed != nil {
|
||||||
|
config.ServerSSHAllowed = input.ServerSSHAllowed
|
||||||
|
refresh = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ServerSSHAllowed == nil {
|
||||||
|
config.ServerSSHAllowed = util.True()
|
||||||
|
refresh = true
|
||||||
|
}
|
||||||
|
|
||||||
if refresh {
|
if refresh {
|
||||||
// since we have new management URL, we need to update config file
|
// since we have new management URL, we need to update config file
|
||||||
if err := util.WriteJson(input.ConfigPath, config); err != nil {
|
if err := util.WriteJson(input.ConfigPath, config); err != nil {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import (
|
|||||||
mgm "github.com/netbirdio/netbird/management/client"
|
mgm "github.com/netbirdio/netbird/management/client"
|
||||||
mgmProto "github.com/netbirdio/netbird/management/proto"
|
mgmProto "github.com/netbirdio/netbird/management/proto"
|
||||||
signal "github.com/netbirdio/netbird/signal/client"
|
signal "github.com/netbirdio/netbird/signal/client"
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -283,6 +284,8 @@ func createEngineConfig(key wgtypes.Key, config *Config, peerConfig *mgmProto.Pe
|
|||||||
NATExternalIPs: config.NATExternalIPs,
|
NATExternalIPs: config.NATExternalIPs,
|
||||||
CustomDNSAddress: config.CustomDNSAddress,
|
CustomDNSAddress: config.CustomDNSAddress,
|
||||||
RosenpassEnabled: config.RosenpassEnabled,
|
RosenpassEnabled: config.RosenpassEnabled,
|
||||||
|
RosenpassPermissive: config.RosenpassPermissive,
|
||||||
|
ServerSSHAllowed: util.ReturnBoolWithDefaultTrue(config.ServerSSHAllowed),
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.PreSharedKey != "" {
|
if config.PreSharedKey != "" {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@@ -23,10 +24,16 @@ const (
|
|||||||
fileMaxNumberOfSearchDomains = 6
|
fileMaxNumberOfSearchDomains = 6
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dnsFailoverTimeout = 4 * time.Second
|
||||||
|
dnsFailoverAttempts = 1
|
||||||
|
)
|
||||||
|
|
||||||
type fileConfigurator struct {
|
type fileConfigurator struct {
|
||||||
repair *repair
|
repair *repair
|
||||||
|
|
||||||
originalPerms os.FileMode
|
originalPerms os.FileMode
|
||||||
|
nbNameserverIP string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFileConfigurator() (hostManager, error) {
|
func newFileConfigurator() (hostManager, error) {
|
||||||
@@ -64,7 +71,7 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nbSearchDomains := searchDomains(config)
|
nbSearchDomains := searchDomains(config)
|
||||||
nbNameserverIP := config.ServerIP
|
f.nbNameserverIP = config.ServerIP
|
||||||
|
|
||||||
resolvConf, err := parseBackupResolvConf()
|
resolvConf, err := parseBackupResolvConf()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -73,11 +80,11 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
|
|
||||||
f.repair.stopWatchFileChanges()
|
f.repair.stopWatchFileChanges()
|
||||||
|
|
||||||
err = f.updateConfig(nbSearchDomains, nbNameserverIP, resolvConf)
|
err = f.updateConfig(nbSearchDomains, f.nbNameserverIP, resolvConf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
f.repair.watchFileChanges(nbSearchDomains, nbNameserverIP)
|
f.repair.watchFileChanges(nbSearchDomains, f.nbNameserverIP)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,10 +92,11 @@ func (f *fileConfigurator) updateConfig(nbSearchDomains []string, nbNameserverIP
|
|||||||
searchDomainList := mergeSearchDomains(nbSearchDomains, cfg.searchDomains)
|
searchDomainList := mergeSearchDomains(nbSearchDomains, cfg.searchDomains)
|
||||||
nameServers := generateNsList(nbNameserverIP, cfg)
|
nameServers := generateNsList(nbNameserverIP, cfg)
|
||||||
|
|
||||||
|
options := prepareOptionsWithTimeout(cfg.others, int(dnsFailoverTimeout.Seconds()), dnsFailoverAttempts)
|
||||||
buf := prepareResolvConfContent(
|
buf := prepareResolvConfContent(
|
||||||
searchDomainList,
|
searchDomainList,
|
||||||
nameServers,
|
nameServers,
|
||||||
cfg.others)
|
options)
|
||||||
|
|
||||||
log.Debugf("creating managed file %s", defaultResolvConfPath)
|
log.Debugf("creating managed file %s", defaultResolvConfPath)
|
||||||
err := os.WriteFile(defaultResolvConfPath, buf.Bytes(), f.originalPerms)
|
err := os.WriteFile(defaultResolvConfPath, buf.Bytes(), f.originalPerms)
|
||||||
@@ -131,7 +139,12 @@ func (f *fileConfigurator) backup() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (f *fileConfigurator) restore() error {
|
func (f *fileConfigurator) restore() error {
|
||||||
err := copyFile(fileDefaultResolvConfBackupLocation, defaultResolvConfPath)
|
err := removeFirstNbNameserver(fileDefaultResolvConfBackupLocation, f.nbNameserverIP)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to remove netbird nameserver from %s on backup restore: %s", fileDefaultResolvConfBackupLocation, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = copyFile(fileDefaultResolvConfBackupLocation, defaultResolvConfPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("restoring %s from %s: %w", defaultResolvConfPath, fileDefaultResolvConfBackupLocation, err)
|
return fmt.Errorf("restoring %s from %s: %w", defaultResolvConfPath, fileDefaultResolvConfBackupLocation, err)
|
||||||
}
|
}
|
||||||
@@ -157,7 +170,7 @@ func (f *fileConfigurator) restoreUncleanShutdownDNS(storedDNSAddress *netip.Add
|
|||||||
currentDNSAddress, err := netip.ParseAddr(resolvConf.nameServers[0])
|
currentDNSAddress, err := netip.ParseAddr(resolvConf.nameServers[0])
|
||||||
// not a valid first nameserver -> restore
|
// not a valid first nameserver -> restore
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("restoring unclean shutdown: parse dns address %s failed: %s", resolvConf.nameServers[1], err)
|
log.Errorf("restoring unclean shutdown: parse dns address %s failed: %s", resolvConf.nameServers[0], err)
|
||||||
return restoreResolvConfFile()
|
return restoreResolvConfFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package dns
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -14,6 +15,9 @@ const (
|
|||||||
defaultResolvConfPath = "/etc/resolv.conf"
|
defaultResolvConfPath = "/etc/resolv.conf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var timeoutRegex = regexp.MustCompile(`timeout:\d+`)
|
||||||
|
var attemptsRegex = regexp.MustCompile(`attempts:\d+`)
|
||||||
|
|
||||||
type resolvConf struct {
|
type resolvConf struct {
|
||||||
nameServers []string
|
nameServers []string
|
||||||
searchDomains []string
|
searchDomains []string
|
||||||
@@ -103,3 +107,62 @@ func parseResolvConfFile(resolvConfFile string) (*resolvConf, error) {
|
|||||||
}
|
}
|
||||||
return rconf, nil
|
return rconf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prepareOptionsWithTimeout appends timeout to existing options if it doesn't exist,
|
||||||
|
// otherwise it adds a new option with timeout and attempts.
|
||||||
|
func prepareOptionsWithTimeout(input []string, timeout int, attempts int) []string {
|
||||||
|
configs := make([]string, len(input))
|
||||||
|
copy(configs, input)
|
||||||
|
|
||||||
|
for i, config := range configs {
|
||||||
|
if strings.HasPrefix(config, "options") {
|
||||||
|
config = strings.ReplaceAll(config, "rotate", "")
|
||||||
|
config = strings.Join(strings.Fields(config), " ")
|
||||||
|
|
||||||
|
if strings.Contains(config, "timeout:") {
|
||||||
|
config = timeoutRegex.ReplaceAllString(config, fmt.Sprintf("timeout:%d", timeout))
|
||||||
|
} else {
|
||||||
|
config = strings.Replace(config, "options ", fmt.Sprintf("options timeout:%d ", timeout), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(config, "attempts:") {
|
||||||
|
config = attemptsRegex.ReplaceAllString(config, fmt.Sprintf("attempts:%d", attempts))
|
||||||
|
} else {
|
||||||
|
config = strings.Replace(config, "options ", fmt.Sprintf("options attempts:%d ", attempts), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
configs[i] = config
|
||||||
|
return configs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(configs, fmt.Sprintf("options timeout:%d attempts:%d", timeout, attempts))
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeFirstNbNameserver removes the given nameserver from the given file if it is in the first position
|
||||||
|
// and writes the file back to the original location
|
||||||
|
func removeFirstNbNameserver(filename, nameserverIP string) error {
|
||||||
|
resolvConf, err := parseResolvConfFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parse backup resolv.conf: %w", err)
|
||||||
|
}
|
||||||
|
content, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("read %s: %w", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resolvConf.nameServers) > 1 && resolvConf.nameServers[0] == nameserverIP {
|
||||||
|
newContent := strings.Replace(string(content), fmt.Sprintf("nameserver %s\n", nameserverIP), "", 1)
|
||||||
|
|
||||||
|
stat, err := os.Stat(filename)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("stat %s: %w", filename, err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filename, []byte(newContent), stat.Mode()); err != nil {
|
||||||
|
return fmt.Errorf("write %s: %w", filename, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_parseResolvConf(t *testing.T) {
|
func Test_parseResolvConf(t *testing.T) {
|
||||||
@@ -172,3 +174,131 @@ nameserver 192.168.0.1
|
|||||||
t.Errorf("unexpected resolv.conf content: %v", cfg)
|
t.Errorf("unexpected resolv.conf content: %v", cfg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrepareOptionsWithTimeout(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
others []string
|
||||||
|
timeout int
|
||||||
|
attempts int
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Append new options with timeout and attempts",
|
||||||
|
others: []string{"some config"},
|
||||||
|
timeout: 2,
|
||||||
|
attempts: 2,
|
||||||
|
expected: []string{"some config", "options timeout:2 attempts:2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Modify existing options to exclude rotate and include timeout and attempts",
|
||||||
|
others: []string{"some config", "options rotate someother"},
|
||||||
|
timeout: 3,
|
||||||
|
attempts: 2,
|
||||||
|
expected: []string{"some config", "options attempts:2 timeout:3 someother"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Existing options with timeout and attempts are updated",
|
||||||
|
others: []string{"some config", "options timeout:4 attempts:3"},
|
||||||
|
timeout: 5,
|
||||||
|
attempts: 4,
|
||||||
|
expected: []string{"some config", "options timeout:5 attempts:4"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Modify existing options, add missing attempts before timeout",
|
||||||
|
others: []string{"some config", "options timeout:4"},
|
||||||
|
timeout: 4,
|
||||||
|
attempts: 3,
|
||||||
|
expected: []string{"some config", "options attempts:3 timeout:4"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := prepareOptionsWithTimeout(tc.others, tc.timeout, tc.attempts)
|
||||||
|
assert.Equal(t, tc.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveFirstNbNameserver(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
content string
|
||||||
|
ipToRemove string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Unrelated nameservers with comments and options",
|
||||||
|
content: `# This is a comment
|
||||||
|
options rotate
|
||||||
|
nameserver 1.1.1.1
|
||||||
|
# Another comment
|
||||||
|
nameserver 8.8.4.4
|
||||||
|
search example.com`,
|
||||||
|
ipToRemove: "9.9.9.9",
|
||||||
|
expected: `# This is a comment
|
||||||
|
options rotate
|
||||||
|
nameserver 1.1.1.1
|
||||||
|
# Another comment
|
||||||
|
nameserver 8.8.4.4
|
||||||
|
search example.com`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "First nameserver matches",
|
||||||
|
content: `search example.com
|
||||||
|
nameserver 9.9.9.9
|
||||||
|
# oof, a comment
|
||||||
|
nameserver 8.8.4.4
|
||||||
|
options attempts:5`,
|
||||||
|
ipToRemove: "9.9.9.9",
|
||||||
|
expected: `search example.com
|
||||||
|
# oof, a comment
|
||||||
|
nameserver 8.8.4.4
|
||||||
|
options attempts:5`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Target IP not the first nameserver",
|
||||||
|
// nolint:dupword
|
||||||
|
content: `# Comment about the first nameserver
|
||||||
|
nameserver 8.8.4.4
|
||||||
|
# Comment before our target
|
||||||
|
nameserver 9.9.9.9
|
||||||
|
options timeout:2`,
|
||||||
|
ipToRemove: "9.9.9.9",
|
||||||
|
// nolint:dupword
|
||||||
|
expected: `# Comment about the first nameserver
|
||||||
|
nameserver 8.8.4.4
|
||||||
|
# Comment before our target
|
||||||
|
nameserver 9.9.9.9
|
||||||
|
options timeout:2`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Only nameserver matches",
|
||||||
|
content: `options debug
|
||||||
|
nameserver 9.9.9.9
|
||||||
|
search localdomain`,
|
||||||
|
ipToRemove: "9.9.9.9",
|
||||||
|
expected: `options debug
|
||||||
|
nameserver 9.9.9.9
|
||||||
|
search localdomain`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
tempFile := filepath.Join(tempDir, "resolv.conf")
|
||||||
|
err := os.WriteFile(tempFile, []byte(tc.content), 0644)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = removeFirstNbNameserver(tempFile, tc.ipToRemove)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
content, err := os.ReadFile(tempFile)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, tc.expected, string(content), "The resulting content should match the expected output.")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func newHostManager(wgInterface string) (hostManager, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("discovered mode is: %s", osManager)
|
log.Infof("System DNS manager discovered: %s", osManager)
|
||||||
return newHostManagerFromType(wgInterface, osManager)
|
return newHostManagerFromType(wgInterface, osManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,10 +53,12 @@ func (r *resolvconf) applyDNSConfig(config HostDNSConfig) error {
|
|||||||
searchDomainList := searchDomains(config)
|
searchDomainList := searchDomains(config)
|
||||||
searchDomainList = mergeSearchDomains(searchDomainList, r.originalSearchDomains)
|
searchDomainList = mergeSearchDomains(searchDomainList, r.originalSearchDomains)
|
||||||
|
|
||||||
|
options := prepareOptionsWithTimeout(r.othersConfigs, int(dnsFailoverTimeout.Seconds()), dnsFailoverAttempts)
|
||||||
|
|
||||||
buf := prepareResolvConfContent(
|
buf := prepareResolvConfContent(
|
||||||
searchDomainList,
|
searchDomainList,
|
||||||
append([]string{config.ServerIP}, r.originalNameServers...),
|
append([]string{config.ServerIP}, r.originalNameServers...),
|
||||||
r.othersConfigs)
|
options)
|
||||||
|
|
||||||
// create a backup for unclean shutdown detection before the resolv.conf is changed
|
// create a backup for unclean shutdown detection before the resolv.conf is changed
|
||||||
if err := createUncleanShutdownIndicator(defaultResolvConfPath, resolvConfManager, config.ServerIP); err != nil {
|
if err := createUncleanShutdownIndicator(defaultResolvConfPath, resolvConfManager, config.ServerIP); err != nil {
|
||||||
|
|||||||
@@ -79,7 +79,10 @@ type EngineConfig struct {
|
|||||||
|
|
||||||
CustomDNSAddress string
|
CustomDNSAddress string
|
||||||
|
|
||||||
RosenpassEnabled bool
|
RosenpassEnabled bool
|
||||||
|
RosenpassPermissive bool
|
||||||
|
|
||||||
|
ServerSSHAllowed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Engine is a mechanism responsible for reacting on Signal and Management stream events and managing connections to the remote peers.
|
// Engine is a mechanism responsible for reacting on Signal and Management stream events and managing connections to the remote peers.
|
||||||
@@ -234,6 +237,11 @@ func (e *Engine) Start() error {
|
|||||||
|
|
||||||
if e.config.RosenpassEnabled {
|
if e.config.RosenpassEnabled {
|
||||||
log.Infof("rosenpass is enabled")
|
log.Infof("rosenpass is enabled")
|
||||||
|
if e.config.RosenpassPermissive {
|
||||||
|
log.Infof("running rosenpass in permissive mode")
|
||||||
|
} else {
|
||||||
|
log.Infof("running rosenpass in strict mode")
|
||||||
|
}
|
||||||
e.rpManager, err = rosenpass.NewManager(e.config.PreSharedKey, e.config.WgIfaceName)
|
e.rpManager, err = rosenpass.NewManager(e.config.PreSharedKey, e.config.WgIfaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -482,44 +490,52 @@ func isNil(server nbssh.Server) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error {
|
func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error {
|
||||||
if sshConf.GetSshEnabled() {
|
|
||||||
if runtime.GOOS == "windows" {
|
if !e.config.ServerSSHAllowed {
|
||||||
log.Warnf("running SSH server on Windows is not supported")
|
log.Warnf("running SSH server is not permitted")
|
||||||
return nil
|
return nil
|
||||||
}
|
} else {
|
||||||
// start SSH server if it wasn't running
|
|
||||||
if isNil(e.sshServer) {
|
if sshConf.GetSshEnabled() {
|
||||||
// nil sshServer means it has not yet been started
|
if runtime.GOOS == "windows" {
|
||||||
var err error
|
log.Warnf("running SSH server on Windows is not supported")
|
||||||
e.sshServer, err = e.sshServerFunc(e.config.SSHKey,
|
return nil
|
||||||
fmt.Sprintf("%s:%d", e.wgInterface.Address().IP.String(), nbssh.DefaultSSHPort))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
go func() {
|
// start SSH server if it wasn't running
|
||||||
// blocking
|
if isNil(e.sshServer) {
|
||||||
err = e.sshServer.Start()
|
// nil sshServer means it has not yet been started
|
||||||
|
var err error
|
||||||
|
e.sshServer, err = e.sshServerFunc(e.config.SSHKey,
|
||||||
|
fmt.Sprintf("%s:%d", e.wgInterface.Address().IP.String(), nbssh.DefaultSSHPort))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// will throw error when we stop it even if it is a graceful stop
|
return err
|
||||||
log.Debugf("stopped SSH server with error %v", err)
|
|
||||||
}
|
}
|
||||||
e.syncMsgMux.Lock()
|
go func() {
|
||||||
defer e.syncMsgMux.Unlock()
|
// blocking
|
||||||
e.sshServer = nil
|
err = e.sshServer.Start()
|
||||||
log.Infof("stopped SSH server")
|
if err != nil {
|
||||||
}()
|
// will throw error when we stop it even if it is a graceful stop
|
||||||
} else {
|
log.Debugf("stopped SSH server with error %v", err)
|
||||||
log.Debugf("SSH server is already running")
|
}
|
||||||
|
e.syncMsgMux.Lock()
|
||||||
|
defer e.syncMsgMux.Unlock()
|
||||||
|
e.sshServer = nil
|
||||||
|
log.Infof("stopped SSH server")
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
log.Debugf("SSH server is already running")
|
||||||
|
}
|
||||||
|
} else if !isNil(e.sshServer) {
|
||||||
|
// Disable SSH server request, so stop it if it was running
|
||||||
|
err := e.sshServer.Stop()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to stop SSH server %v", err)
|
||||||
|
}
|
||||||
|
e.sshServer = nil
|
||||||
}
|
}
|
||||||
} else if !isNil(e.sshServer) {
|
return nil
|
||||||
// Disable SSH server request, so stop it if it was running
|
|
||||||
err := e.sshServer.Stop()
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed to stop SSH server %v", err)
|
|
||||||
}
|
|
||||||
e.sshServer = nil
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error {
|
func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error {
|
||||||
@@ -860,7 +876,7 @@ func (e *Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, e
|
|||||||
PreSharedKey: e.config.PreSharedKey,
|
PreSharedKey: e.config.PreSharedKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.config.RosenpassEnabled {
|
if e.config.RosenpassEnabled && !e.config.RosenpassPermissive {
|
||||||
lk := []byte(e.config.WgPrivateKey.PublicKey().String())
|
lk := []byte(e.config.WgPrivateKey.PublicKey().String())
|
||||||
rk := []byte(wgConfig.RemoteKey)
|
rk := []byte(wgConfig.RemoteKey)
|
||||||
var keyInput []byte
|
var keyInput []byte
|
||||||
@@ -1270,7 +1286,7 @@ func (e *Engine) receiveProbeEvents() {
|
|||||||
log.Debugf("failed to get wg stats for peer %s: %s", key, err)
|
log.Debugf("failed to get wg stats for peer %s: %s", key, err)
|
||||||
}
|
}
|
||||||
// wgStats could be zero value, in which case we just reset the stats
|
// wgStats could be zero value, in which case we just reset the stats
|
||||||
if err := e.statusRecorder.UpdateWireguardPeerState(key, wgStats); err != nil {
|
if err := e.statusRecorder.UpdateWireGuardPeerState(key, wgStats); err != nil {
|
||||||
log.Debugf("failed to update wg stats for peer %s: %s", key, err)
|
log.Debugf("failed to update wg stats for peer %s: %s", key, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
|
|
||||||
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
"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/routemanager"
|
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||||
@@ -70,10 +71,11 @@ func TestEngine_SSH(t *testing.T) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, &EngineConfig{
|
engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, &EngineConfig{
|
||||||
WgIfaceName: "utun101",
|
WgIfaceName: "utun101",
|
||||||
WgAddr: "100.64.0.1/24",
|
WgAddr: "100.64.0.1/24",
|
||||||
WgPrivateKey: key,
|
WgPrivateKey: key,
|
||||||
WgPort: 33100,
|
WgPort: 33100,
|
||||||
|
ServerSSHAllowed: true,
|
||||||
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
|
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
|
||||||
|
|
||||||
engine.dnsServer = &dns.MockServer{
|
engine.dnsServer = &dns.MockServer{
|
||||||
@@ -1046,11 +1048,8 @@ func startManagement(dataDir string) (*grpc.Server, string, error) {
|
|||||||
|
|
||||||
peersUpdateManager := server.NewPeersUpdateManager(nil)
|
peersUpdateManager := server.NewPeersUpdateManager(nil)
|
||||||
eventStore := &activity.InMemoryEventStore{}
|
eventStore := &activity.InMemoryEventStore{}
|
||||||
if err != nil {
|
ia, _ := integrations.NewIntegratedApproval(eventStore)
|
||||||
return nil, "", err
|
accountManager, err := server.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false, ia)
|
||||||
}
|
|
||||||
accountManager, err := server.BuildManager(store, peersUpdateManager, nil, "", "",
|
|
||||||
eventStore, false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -407,6 +407,10 @@ func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int, rem
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn.status = StatusConnected
|
conn.status = StatusConnected
|
||||||
|
rosenpassEnabled := false
|
||||||
|
if remoteRosenpassPubKey != nil {
|
||||||
|
rosenpassEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
peerState := State{
|
peerState := State{
|
||||||
PubKey: conn.config.Key,
|
PubKey: conn.config.Key,
|
||||||
@@ -417,6 +421,7 @@ func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int, rem
|
|||||||
LocalIceCandidateEndpoint: fmt.Sprintf("%s:%d", pair.Local.Address(), pair.Local.Port()),
|
LocalIceCandidateEndpoint: fmt.Sprintf("%s:%d", pair.Local.Address(), pair.Local.Port()),
|
||||||
RemoteIceCandidateEndpoint: fmt.Sprintf("%s:%d", pair.Remote.Address(), pair.Local.Port()),
|
RemoteIceCandidateEndpoint: fmt.Sprintf("%s:%d", pair.Remote.Address(), pair.Local.Port()),
|
||||||
Direct: !isRelayCandidate(pair.Local),
|
Direct: !isRelayCandidate(pair.Local),
|
||||||
|
RosenpassEnabled: rosenpassEnabled,
|
||||||
}
|
}
|
||||||
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
|
||||||
@@ -505,7 +510,7 @@ func (conn *Conn) cleanup() error {
|
|||||||
// todo rethink status updates
|
// todo rethink status updates
|
||||||
log.Debugf("error while updating peer's %s state, err: %v", conn.config.Key, err)
|
log.Debugf("error while updating peer's %s state, err: %v", conn.config.Key, err)
|
||||||
}
|
}
|
||||||
if err := conn.statusRecorder.UpdateWireguardPeerState(conn.config.Key, iface.WGStats{}); err != nil {
|
if err := conn.statusRecorder.UpdateWireGuardPeerState(conn.config.Key, iface.WGStats{}); err != nil {
|
||||||
log.Debugf("failed to reset wireguard stats for peer %s: %s", conn.config.Key, err)
|
log.Debugf("failed to reset wireguard stats for peer %s: %s", conn.config.Key, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type State struct {
|
|||||||
LastWireguardHandshake time.Time
|
LastWireguardHandshake time.Time
|
||||||
BytesTx int64
|
BytesTx int64
|
||||||
BytesRx int64
|
BytesRx int64
|
||||||
|
RosenpassEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalPeerState contains the latest state of the local peer
|
// LocalPeerState contains the latest state of the local peer
|
||||||
@@ -49,30 +50,39 @@ type ManagementState struct {
|
|||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RosenpassState contains the latest state of the Rosenpass configuration
|
||||||
|
type RosenpassState struct {
|
||||||
|
Enabled bool
|
||||||
|
Permissive bool
|
||||||
|
}
|
||||||
|
|
||||||
// FullStatus contains the full state held by the Status instance
|
// FullStatus contains the full state held by the Status instance
|
||||||
type FullStatus struct {
|
type FullStatus struct {
|
||||||
Peers []State
|
Peers []State
|
||||||
ManagementState ManagementState
|
ManagementState ManagementState
|
||||||
SignalState SignalState
|
SignalState SignalState
|
||||||
LocalPeerState LocalPeerState
|
LocalPeerState LocalPeerState
|
||||||
|
RosenpassState RosenpassState
|
||||||
Relays []relay.ProbeResult
|
Relays []relay.ProbeResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status holds a state of peers, signal, management connections and relays
|
// Status holds a state of peers, signal, management connections and relays
|
||||||
type Status struct {
|
type Status struct {
|
||||||
mux sync.Mutex
|
mux sync.Mutex
|
||||||
peers map[string]State
|
peers map[string]State
|
||||||
changeNotify map[string]chan struct{}
|
changeNotify map[string]chan struct{}
|
||||||
signalState bool
|
signalState bool
|
||||||
signalError error
|
signalError error
|
||||||
managementState bool
|
managementState bool
|
||||||
managementError error
|
managementError error
|
||||||
relayStates []relay.ProbeResult
|
relayStates []relay.ProbeResult
|
||||||
localPeer LocalPeerState
|
localPeer LocalPeerState
|
||||||
offlinePeers []State
|
offlinePeers []State
|
||||||
mgmAddress string
|
mgmAddress string
|
||||||
signalAddress string
|
signalAddress string
|
||||||
notifier *notifier
|
notifier *notifier
|
||||||
|
rosenpassEnabled bool
|
||||||
|
rosenpassPermissive bool
|
||||||
|
|
||||||
// To reduce the number of notification invocation this bool will be true when need to call the notification
|
// 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
|
// Some Peer actions mostly used by in a batch when the network map has been synchronized. In these type of events
|
||||||
@@ -172,6 +182,7 @@ func (d *Status) UpdatePeerState(receivedState State) error {
|
|||||||
peerState.RemoteIceCandidateType = receivedState.RemoteIceCandidateType
|
peerState.RemoteIceCandidateType = receivedState.RemoteIceCandidateType
|
||||||
peerState.LocalIceCandidateEndpoint = receivedState.LocalIceCandidateEndpoint
|
peerState.LocalIceCandidateEndpoint = receivedState.LocalIceCandidateEndpoint
|
||||||
peerState.RemoteIceCandidateEndpoint = receivedState.RemoteIceCandidateEndpoint
|
peerState.RemoteIceCandidateEndpoint = receivedState.RemoteIceCandidateEndpoint
|
||||||
|
peerState.RosenpassEnabled = receivedState.RosenpassEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
d.peers[receivedState.PubKey] = peerState
|
d.peers[receivedState.PubKey] = peerState
|
||||||
@@ -190,8 +201,8 @@ func (d *Status) UpdatePeerState(receivedState State) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateWireguardPeerState updates the wireguard bits of the peer state
|
// UpdateWireGuardPeerState updates the WireGuard bits of the peer state
|
||||||
func (d *Status) UpdateWireguardPeerState(pubKey string, wgStats iface.WGStats) error {
|
func (d *Status) UpdateWireGuardPeerState(pubKey string, wgStats iface.WGStats) error {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
defer d.mux.Unlock()
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
@@ -316,6 +327,14 @@ func (d *Status) UpdateManagementAddress(mgmAddress string) {
|
|||||||
d.mgmAddress = mgmAddress
|
d.mgmAddress = mgmAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateRosenpass update the Rosenpass configuration
|
||||||
|
func (d *Status) UpdateRosenpass(rosenpassEnabled, rosenpassPermissive bool) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
d.rosenpassPermissive = rosenpassPermissive
|
||||||
|
d.rosenpassEnabled = rosenpassEnabled
|
||||||
|
}
|
||||||
|
|
||||||
// MarkSignalDisconnected sets SignalState to disconnected
|
// MarkSignalDisconnected sets SignalState to disconnected
|
||||||
func (d *Status) MarkSignalDisconnected(err error) {
|
func (d *Status) MarkSignalDisconnected(err error) {
|
||||||
d.mux.Lock()
|
d.mux.Lock()
|
||||||
@@ -342,6 +361,13 @@ func (d *Status) UpdateRelayStates(relayResults []relay.ProbeResult) {
|
|||||||
d.relayStates = relayResults
|
d.relayStates = relayResults
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Status) GetRosenpassState() RosenpassState {
|
||||||
|
return RosenpassState{
|
||||||
|
d.rosenpassEnabled,
|
||||||
|
d.rosenpassPermissive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Status) GetManagementState() ManagementState {
|
func (d *Status) GetManagementState() ManagementState {
|
||||||
return ManagementState{
|
return ManagementState{
|
||||||
d.mgmAddress,
|
d.mgmAddress,
|
||||||
@@ -372,6 +398,7 @@ func (d *Status) GetFullStatus() FullStatus {
|
|||||||
SignalState: d.GetSignalState(),
|
SignalState: d.GetSignalState(),
|
||||||
LocalPeerState: d.localPeer,
|
LocalPeerState: d.localPeer,
|
||||||
Relays: d.GetRelayStates(),
|
Relays: d.GetRelayStates(),
|
||||||
|
RosenpassState: d.GetRosenpassState(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, status := range d.peers {
|
for _, status := range d.peers {
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ func (c *Client) Run(fd int32, interfaceName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
|
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
|
||||||
|
c.recorder.UpdateRosenpass(cfg.RosenpassEnabled, cfg.RosenpassPermissive)
|
||||||
|
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
//nolint
|
//nolint
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.26.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v3.21.9
|
// protoc v3.12.4
|
||||||
// source: daemon.proto
|
// source: daemon.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||||
|
timestamp "github.com/golang/protobuf/ptypes/timestamp"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
_ "google.golang.org/protobuf/types/descriptorpb"
|
|
||||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
sync "sync"
|
sync "sync"
|
||||||
)
|
)
|
||||||
@@ -51,6 +51,9 @@ type LoginRequest struct {
|
|||||||
InterfaceName *string `protobuf:"bytes,11,opt,name=interfaceName,proto3,oneof" json:"interfaceName,omitempty"`
|
InterfaceName *string `protobuf:"bytes,11,opt,name=interfaceName,proto3,oneof" json:"interfaceName,omitempty"`
|
||||||
WireguardPort *int64 `protobuf:"varint,12,opt,name=wireguardPort,proto3,oneof" json:"wireguardPort,omitempty"`
|
WireguardPort *int64 `protobuf:"varint,12,opt,name=wireguardPort,proto3,oneof" json:"wireguardPort,omitempty"`
|
||||||
OptionalPreSharedKey *string `protobuf:"bytes,13,opt,name=optionalPreSharedKey,proto3,oneof" json:"optionalPreSharedKey,omitempty"`
|
OptionalPreSharedKey *string `protobuf:"bytes,13,opt,name=optionalPreSharedKey,proto3,oneof" json:"optionalPreSharedKey,omitempty"`
|
||||||
|
DisableAutoConnect *bool `protobuf:"varint,14,opt,name=disableAutoConnect,proto3,oneof" json:"disableAutoConnect,omitempty"`
|
||||||
|
ServerSSHAllowed *bool `protobuf:"varint,15,opt,name=serverSSHAllowed,proto3,oneof" json:"serverSSHAllowed,omitempty"`
|
||||||
|
RosenpassPermissive *bool `protobuf:"varint,16,opt,name=rosenpassPermissive,proto3,oneof" json:"rosenpassPermissive,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *LoginRequest) Reset() {
|
func (x *LoginRequest) Reset() {
|
||||||
@@ -177,6 +180,27 @@ func (x *LoginRequest) GetOptionalPreSharedKey() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *LoginRequest) GetDisableAutoConnect() bool {
|
||||||
|
if x != nil && x.DisableAutoConnect != nil {
|
||||||
|
return *x.DisableAutoConnect
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginRequest) GetServerSSHAllowed() bool {
|
||||||
|
if x != nil && x.ServerSSHAllowed != nil {
|
||||||
|
return *x.ServerSSHAllowed
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LoginRequest) GetRosenpassPermissive() bool {
|
||||||
|
if x != nil && x.RosenpassPermissive != nil {
|
||||||
|
return *x.RosenpassPermissive
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type LoginResponse struct {
|
type LoginResponse struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -733,20 +757,21 @@ type PeerState struct {
|
|||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
IP string `protobuf:"bytes,1,opt,name=IP,proto3" json:"IP,omitempty"`
|
IP string `protobuf:"bytes,1,opt,name=IP,proto3" json:"IP,omitempty"`
|
||||||
PubKey string `protobuf:"bytes,2,opt,name=pubKey,proto3" json:"pubKey,omitempty"`
|
PubKey string `protobuf:"bytes,2,opt,name=pubKey,proto3" json:"pubKey,omitempty"`
|
||||||
ConnStatus string `protobuf:"bytes,3,opt,name=connStatus,proto3" json:"connStatus,omitempty"`
|
ConnStatus string `protobuf:"bytes,3,opt,name=connStatus,proto3" json:"connStatus,omitempty"`
|
||||||
ConnStatusUpdate *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=connStatusUpdate,proto3" json:"connStatusUpdate,omitempty"`
|
ConnStatusUpdate *timestamp.Timestamp `protobuf:"bytes,4,opt,name=connStatusUpdate,proto3" json:"connStatusUpdate,omitempty"`
|
||||||
Relayed bool `protobuf:"varint,5,opt,name=relayed,proto3" json:"relayed,omitempty"`
|
Relayed bool `protobuf:"varint,5,opt,name=relayed,proto3" json:"relayed,omitempty"`
|
||||||
Direct bool `protobuf:"varint,6,opt,name=direct,proto3" json:"direct,omitempty"`
|
Direct bool `protobuf:"varint,6,opt,name=direct,proto3" json:"direct,omitempty"`
|
||||||
LocalIceCandidateType string `protobuf:"bytes,7,opt,name=localIceCandidateType,proto3" json:"localIceCandidateType,omitempty"`
|
LocalIceCandidateType string `protobuf:"bytes,7,opt,name=localIceCandidateType,proto3" json:"localIceCandidateType,omitempty"`
|
||||||
RemoteIceCandidateType string `protobuf:"bytes,8,opt,name=remoteIceCandidateType,proto3" json:"remoteIceCandidateType,omitempty"`
|
RemoteIceCandidateType string `protobuf:"bytes,8,opt,name=remoteIceCandidateType,proto3" json:"remoteIceCandidateType,omitempty"`
|
||||||
Fqdn string `protobuf:"bytes,9,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
Fqdn string `protobuf:"bytes,9,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
||||||
LocalIceCandidateEndpoint string `protobuf:"bytes,10,opt,name=localIceCandidateEndpoint,proto3" json:"localIceCandidateEndpoint,omitempty"`
|
LocalIceCandidateEndpoint string `protobuf:"bytes,10,opt,name=localIceCandidateEndpoint,proto3" json:"localIceCandidateEndpoint,omitempty"`
|
||||||
RemoteIceCandidateEndpoint string `protobuf:"bytes,11,opt,name=remoteIceCandidateEndpoint,proto3" json:"remoteIceCandidateEndpoint,omitempty"`
|
RemoteIceCandidateEndpoint string `protobuf:"bytes,11,opt,name=remoteIceCandidateEndpoint,proto3" json:"remoteIceCandidateEndpoint,omitempty"`
|
||||||
LastWireguardHandshake *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=lastWireguardHandshake,proto3" json:"lastWireguardHandshake,omitempty"`
|
LastWireguardHandshake *timestamp.Timestamp `protobuf:"bytes,12,opt,name=lastWireguardHandshake,proto3" json:"lastWireguardHandshake,omitempty"`
|
||||||
BytesRx int64 `protobuf:"varint,13,opt,name=bytesRx,proto3" json:"bytesRx,omitempty"`
|
BytesRx int64 `protobuf:"varint,13,opt,name=bytesRx,proto3" json:"bytesRx,omitempty"`
|
||||||
BytesTx int64 `protobuf:"varint,14,opt,name=bytesTx,proto3" json:"bytesTx,omitempty"`
|
BytesTx int64 `protobuf:"varint,14,opt,name=bytesTx,proto3" json:"bytesTx,omitempty"`
|
||||||
|
RosenpassEnabled bool `protobuf:"varint,15,opt,name=rosenpassEnabled,proto3" json:"rosenpassEnabled,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PeerState) Reset() {
|
func (x *PeerState) Reset() {
|
||||||
@@ -802,7 +827,7 @@ func (x *PeerState) GetConnStatus() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PeerState) GetConnStatusUpdate() *timestamppb.Timestamp {
|
func (x *PeerState) GetConnStatusUpdate() *timestamp.Timestamp {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.ConnStatusUpdate
|
return x.ConnStatusUpdate
|
||||||
}
|
}
|
||||||
@@ -858,7 +883,7 @@ func (x *PeerState) GetRemoteIceCandidateEndpoint() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *PeerState) GetLastWireguardHandshake() *timestamppb.Timestamp {
|
func (x *PeerState) GetLastWireguardHandshake() *timestamp.Timestamp {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.LastWireguardHandshake
|
return x.LastWireguardHandshake
|
||||||
}
|
}
|
||||||
@@ -879,16 +904,25 @@ func (x *PeerState) GetBytesTx() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *PeerState) GetRosenpassEnabled() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.RosenpassEnabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// LocalPeerState contains the latest state of the local peer
|
// LocalPeerState contains the latest state of the local peer
|
||||||
type LocalPeerState struct {
|
type LocalPeerState struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
IP string `protobuf:"bytes,1,opt,name=IP,proto3" json:"IP,omitempty"`
|
IP string `protobuf:"bytes,1,opt,name=IP,proto3" json:"IP,omitempty"`
|
||||||
PubKey string `protobuf:"bytes,2,opt,name=pubKey,proto3" json:"pubKey,omitempty"`
|
PubKey string `protobuf:"bytes,2,opt,name=pubKey,proto3" json:"pubKey,omitempty"`
|
||||||
KernelInterface bool `protobuf:"varint,3,opt,name=kernelInterface,proto3" json:"kernelInterface,omitempty"`
|
KernelInterface bool `protobuf:"varint,3,opt,name=kernelInterface,proto3" json:"kernelInterface,omitempty"`
|
||||||
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
|
||||||
|
RosenpassEnabled bool `protobuf:"varint,5,opt,name=rosenpassEnabled,proto3" json:"rosenpassEnabled,omitempty"`
|
||||||
|
RosenpassPermissive bool `protobuf:"varint,6,opt,name=rosenpassPermissive,proto3" json:"rosenpassPermissive,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *LocalPeerState) Reset() {
|
func (x *LocalPeerState) Reset() {
|
||||||
@@ -951,6 +985,20 @@ func (x *LocalPeerState) GetFqdn() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *LocalPeerState) GetRosenpassEnabled() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.RosenpassEnabled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *LocalPeerState) GetRosenpassPermissive() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.RosenpassPermissive
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// SignalState contains the latest state of a signal connection
|
// SignalState contains the latest state of a signal connection
|
||||||
type SignalState struct {
|
type SignalState struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
@@ -1231,7 +1279,7 @@ var file_daemon_proto_rawDesc = []byte{
|
|||||||
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
|
||||||
0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
||||||
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
|
||||||
0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfc, 0x04, 0x0a, 0x0c, 0x4c, 0x6f,
|
0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdd, 0x06, 0x0a, 0x0c, 0x4c, 0x6f,
|
||||||
0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65,
|
0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65,
|
||||||
0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65,
|
0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65,
|
||||||
0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61,
|
0x74, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61,
|
||||||
@@ -1266,162 +1314,185 @@ var file_daemon_proto_rawDesc = []byte{
|
|||||||
0x14, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72,
|
0x14, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72,
|
||||||
0x65, 0x64, 0x4b, 0x65, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x14, 0x6f,
|
0x65, 0x64, 0x4b, 0x65, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x14, 0x6f,
|
||||||
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64,
|
0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64,
|
||||||
0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x72, 0x6f, 0x73, 0x65, 0x6e,
|
0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
|
||||||
0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f,
|
0x65, 0x41, 0x75, 0x74, 0x6f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x18, 0x0e, 0x20, 0x01,
|
||||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x10, 0x0a,
|
0x28, 0x08, 0x48, 0x04, 0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x75, 0x74,
|
||||||
0x0e, 0x5f, 0x77, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x42,
|
0x6f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x10, 0x73,
|
||||||
0x17, 0x0a, 0x15, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x65, 0x53,
|
0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x53, 0x48, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18,
|
||||||
0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x22, 0xb5, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67,
|
0x0f, 0x20, 0x01, 0x28, 0x08, 0x48, 0x05, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53,
|
||||||
0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x65,
|
0x53, 0x48, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x13,
|
||||||
0x65, 0x64, 0x73, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
|
||||||
0x08, 0x52, 0x0d, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
|
0x69, 0x76, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x48, 0x06, 0x52, 0x13, 0x72, 0x6f, 0x73,
|
||||||
0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01,
|
0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65,
|
||||||
0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x0f,
|
0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73,
|
||||||
0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x18,
|
0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x69, 0x6e, 0x74,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
|
0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x77,
|
||||||
0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x12, 0x38, 0x0a, 0x17, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69,
|
0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x42, 0x17, 0x0a, 0x15,
|
||||||
|
0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x50, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72,
|
||||||
|
0x65, 0x64, 0x4b, 0x65, 0x79, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
|
||||||
|
0x65, 0x41, 0x75, 0x74, 0x6f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x42, 0x13, 0x0a, 0x11,
|
||||||
|
0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x53, 0x48, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65,
|
||||||
|
0x64, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50,
|
||||||
|
0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x22, 0xb5, 0x01, 0x0a, 0x0d, 0x4c, 0x6f,
|
||||||
|
0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x6e,
|
||||||
|
0x65, 0x65, 0x64, 0x73, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
||||||
|
0x28, 0x08, 0x52, 0x0d, 0x6e, 0x65, 0x65, 0x64, 0x73, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69,
|
||||||
|
0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20,
|
||||||
|
0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x28, 0x0a,
|
||||||
|
0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49,
|
||||||
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x12, 0x38, 0x0a, 0x17, 0x76, 0x65, 0x72, 0x69, 0x66,
|
||||||
|
0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
|
||||||
|
0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69,
|
||||||
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
|
0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
|
||||||
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63,
|
0x65, 0x22, 0x4d, 0x0a, 0x13, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69,
|
||||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x52, 0x49, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65,
|
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72,
|
||||||
0x22, 0x4d, 0x0a, 0x13, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
|
0x43, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72,
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43,
|
0x43, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65,
|
||||||
0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65,
|
||||||
0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18,
|
0x22, 0x16, 0x0a, 0x14, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
|
||||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22,
|
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0b, 0x0a, 0x09, 0x55, 0x70, 0x52, 0x65,
|
||||||
0x16, 0x0a, 0x14, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52,
|
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0c, 0x0a, 0x0a, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0b, 0x0a, 0x09, 0x55, 0x70, 0x52, 0x65, 0x71,
|
0x6e, 0x73, 0x65, 0x22, 0x3d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x22, 0x0c, 0x0a, 0x0a, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50,
|
||||||
0x73, 0x65, 0x22, 0x3d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75,
|
0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x65,
|
0x11, 0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74,
|
||||||
0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11,
|
0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73,
|
||||||
0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75,
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
|
||||||
0x73, 0x22, 0x82, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a,
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01,
|
0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x0a,
|
0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x53,
|
||||||
0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75,
|
||||||
0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74,
|
0x73, 0x12, 0x24, 0x0a, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69,
|
||||||
0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
|
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e,
|
||||||
0x12, 0x24, 0x0a, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x0d, 0x0a, 0x0b, 0x44, 0x6f, 0x77, 0x6e, 0x52,
|
||||||
0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x56,
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65,
|
||||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x0d, 0x0a, 0x0b, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65,
|
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e,
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73,
|
0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb3, 0x01, 0x0a, 0x11, 0x47,
|
||||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb3, 0x01, 0x0a, 0x11, 0x47, 0x65,
|
0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x72,
|
||||||
0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
|
||||||
0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x72, 0x6c,
|
0x65, 0x6e, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
0x46, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66,
|
||||||
0x6e, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46,
|
0x69, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c,
|
||||||
0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65,
|
||||||
0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65,
|
0x12, 0x22, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79,
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x12,
|
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65,
|
||||||
0x22, 0x0a, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x18,
|
0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, 0x4c,
|
||||||
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64,
|
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, 0x4c,
|
||||||
0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, 0x4c, 0x18,
|
0x22, 0x81, 0x05, 0x0a, 0x09, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e,
|
||||||
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, 0x4c, 0x22,
|
0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16,
|
||||||
0xd5, 0x04, 0x0a, 0x09, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a,
|
0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
|
||||||
0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a,
|
0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74,
|
||||||
0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70,
|
0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e,
|
||||||
0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61,
|
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74,
|
||||||
0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53,
|
0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
|
||||||
0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61,
|
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x6f,
|
||||||
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18,
|
||||||
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x6f, 0x6e,
|
0x0a, 0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a,
|
0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x72, 0x65,
|
||||||
0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
|
0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
|
||||||
0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63,
|
0x12, 0x34, 0x0a, 0x15, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64,
|
||||||
0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x12,
|
0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x34, 0x0a, 0x15, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69,
|
0x15, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61,
|
||||||
0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15,
|
0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65,
|
||||||
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74,
|
0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65,
|
||||||
0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49,
|
0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63,
|
||||||
0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18,
|
0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12,
|
||||||
0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65,
|
0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71,
|
||||||
0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a,
|
0x64, 0x6e, 0x12, 0x3c, 0x0a, 0x19, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61,
|
||||||
0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64,
|
0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18,
|
||||||
0x6e, 0x12, 0x3c, 0x0a, 0x19, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e,
|
0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43,
|
||||||
0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0a,
|
0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61,
|
0x12, 0x3e, 0x0a, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e,
|
||||||
0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12,
|
0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0b,
|
||||||
0x3e, 0x0a, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65, 0x43,
|
||||||
0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0b, 0x20,
|
0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65, 0x43, 0x61,
|
0x12, 0x52, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72,
|
||||||
0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12,
|
0x64, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b,
|
||||||
0x52, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64,
|
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x16, 0x6c, 0x61,
|
||||||
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
0x73, 0x74, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x73,
|
||||||
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x16, 0x6c, 0x61, 0x73,
|
0x68, 0x61, 0x6b, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x78, 0x18,
|
||||||
0x74, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68,
|
0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x78, 0x12, 0x18,
|
||||||
0x61, 0x6b, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x78, 0x18, 0x0d,
|
0x0a, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52,
|
||||||
0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x78, 0x12, 0x18, 0x0a,
|
0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73, 0x65,
|
||||||
0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07,
|
0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01,
|
||||||
0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x22, 0x76, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x6c,
|
0x28, 0x08, 0x52, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61,
|
||||||
0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18,
|
0x62, 0x6c, 0x65, 0x64, 0x22, 0xd4, 0x01, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65,
|
||||||
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62,
|
0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20,
|
||||||
0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65,
|
0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65,
|
||||||
0x79, 0x12, 0x28, 0x0a, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72,
|
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12,
|
||||||
0x66, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6b, 0x65, 0x72, 0x6e,
|
0x28, 0x0a, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61,
|
||||||
0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66,
|
0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c,
|
||||||
0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22,
|
0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64,
|
||||||
0x53, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10,
|
0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x12, 0x2a, 0x0a,
|
||||||
0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c,
|
0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
||||||
0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20,
|
0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61,
|
||||||
0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14,
|
0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73,
|
||||||
0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
|
0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65,
|
||||||
0x72, 0x72, 0x6f, 0x72, 0x22, 0x57, 0x0a, 0x0f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
|
0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73,
|
||||||
0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01,
|
0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x22, 0x53, 0x0a, 0x0b, 0x53,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e,
|
0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52,
|
||||||
0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f,
|
0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09,
|
||||||
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a,
|
0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72,
|
||||||
0x0a, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55,
|
0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||||
0x52, 0x49, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x49, 0x12, 0x1c, 0x0a,
|
0x22, 0x57, 0x0a, 0x0f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74,
|
||||||
0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
|
0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65,
|
0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
|
||||||
0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f,
|
0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
|
||||||
0x72, 0x22, 0x9b, 0x02, 0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
|
0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01,
|
||||||
0x12, 0x41, 0x0a, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74,
|
0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x0a, 0x52, 0x65, 0x6c,
|
||||||
0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x61, 0x65, 0x6d,
|
0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x49, 0x18, 0x01,
|
||||||
0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x49, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61,
|
||||||
0x74, 0x65, 0x52, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74,
|
0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x76,
|
||||||
0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61,
|
0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||||
0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x9b, 0x02,
|
||||||
0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x73,
|
0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x41, 0x0a, 0x0f,
|
||||||
0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x6c, 0x6f,
|
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18,
|
||||||
0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01,
|
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d,
|
||||||
0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x63, 0x61,
|
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f,
|
||||||
0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61,
|
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
|
||||||
0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x65,
|
0x35, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02,
|
||||||
0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d,
|
0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x69,
|
||||||
0x6f, 0x6e, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x70, 0x65,
|
0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61,
|
||||||
0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x05, 0x20,
|
0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50,
|
||||||
0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x6c,
|
0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
|
||||||
0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x32,
|
0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65,
|
||||||
0xf7, 0x02, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65,
|
||||||
0x65, 0x12, 0x36, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65,
|
0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18,
|
||||||
0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x50,
|
||||||
0x1a, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52,
|
0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12,
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69,
|
0x2a, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d,
|
0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x74,
|
||||||
0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52,
|
0x61, 0x74, 0x65, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x32, 0xf7, 0x02, 0x0a, 0x0d,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a,
|
||||||
0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70,
|
0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
||||||
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64,
|
0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x64,
|
||||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f,
|
||||||
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
|
0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57,
|
||||||
0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52,
|
0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
|
0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74,
|
||||||
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
|
0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
0x12, 0x33, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
||||||
0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e,
|
0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x61,
|
||||||
0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||||
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x15, 0x2e, 0x64, 0x61,
|
||||||
0x69, 0x67, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43,
|
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x64,
|
0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74,
|
||||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
|
0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04,
|
||||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72,
|
0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f,
|
||||||
0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d,
|
||||||
|
0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||||
|
0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18,
|
||||||
|
0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
|
0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
|
||||||
|
0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||||
|
0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||||
|
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -1438,25 +1509,25 @@ func file_daemon_proto_rawDescGZIP() []byte {
|
|||||||
|
|
||||||
var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
|
var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
|
||||||
var file_daemon_proto_goTypes = []interface{}{
|
var file_daemon_proto_goTypes = []interface{}{
|
||||||
(*LoginRequest)(nil), // 0: daemon.LoginRequest
|
(*LoginRequest)(nil), // 0: daemon.LoginRequest
|
||||||
(*LoginResponse)(nil), // 1: daemon.LoginResponse
|
(*LoginResponse)(nil), // 1: daemon.LoginResponse
|
||||||
(*WaitSSOLoginRequest)(nil), // 2: daemon.WaitSSOLoginRequest
|
(*WaitSSOLoginRequest)(nil), // 2: daemon.WaitSSOLoginRequest
|
||||||
(*WaitSSOLoginResponse)(nil), // 3: daemon.WaitSSOLoginResponse
|
(*WaitSSOLoginResponse)(nil), // 3: daemon.WaitSSOLoginResponse
|
||||||
(*UpRequest)(nil), // 4: daemon.UpRequest
|
(*UpRequest)(nil), // 4: daemon.UpRequest
|
||||||
(*UpResponse)(nil), // 5: daemon.UpResponse
|
(*UpResponse)(nil), // 5: daemon.UpResponse
|
||||||
(*StatusRequest)(nil), // 6: daemon.StatusRequest
|
(*StatusRequest)(nil), // 6: daemon.StatusRequest
|
||||||
(*StatusResponse)(nil), // 7: daemon.StatusResponse
|
(*StatusResponse)(nil), // 7: daemon.StatusResponse
|
||||||
(*DownRequest)(nil), // 8: daemon.DownRequest
|
(*DownRequest)(nil), // 8: daemon.DownRequest
|
||||||
(*DownResponse)(nil), // 9: daemon.DownResponse
|
(*DownResponse)(nil), // 9: daemon.DownResponse
|
||||||
(*GetConfigRequest)(nil), // 10: daemon.GetConfigRequest
|
(*GetConfigRequest)(nil), // 10: daemon.GetConfigRequest
|
||||||
(*GetConfigResponse)(nil), // 11: daemon.GetConfigResponse
|
(*GetConfigResponse)(nil), // 11: daemon.GetConfigResponse
|
||||||
(*PeerState)(nil), // 12: daemon.PeerState
|
(*PeerState)(nil), // 12: daemon.PeerState
|
||||||
(*LocalPeerState)(nil), // 13: daemon.LocalPeerState
|
(*LocalPeerState)(nil), // 13: daemon.LocalPeerState
|
||||||
(*SignalState)(nil), // 14: daemon.SignalState
|
(*SignalState)(nil), // 14: daemon.SignalState
|
||||||
(*ManagementState)(nil), // 15: daemon.ManagementState
|
(*ManagementState)(nil), // 15: daemon.ManagementState
|
||||||
(*RelayState)(nil), // 16: daemon.RelayState
|
(*RelayState)(nil), // 16: daemon.RelayState
|
||||||
(*FullStatus)(nil), // 17: daemon.FullStatus
|
(*FullStatus)(nil), // 17: daemon.FullStatus
|
||||||
(*timestamppb.Timestamp)(nil), // 18: google.protobuf.Timestamp
|
(*timestamp.Timestamp)(nil), // 18: google.protobuf.Timestamp
|
||||||
}
|
}
|
||||||
var file_daemon_proto_depIdxs = []int32{
|
var file_daemon_proto_depIdxs = []int32{
|
||||||
17, // 0: daemon.StatusResponse.fullStatus:type_name -> daemon.FullStatus
|
17, // 0: daemon.StatusResponse.fullStatus:type_name -> daemon.FullStatus
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ message LoginRequest {
|
|||||||
|
|
||||||
// This is the old PreSharedKey field which will be deprecated in favor of optionalPreSharedKey field that is defined as optional
|
// This is the old PreSharedKey field which will be deprecated in favor of optionalPreSharedKey field that is defined as optional
|
||||||
// to allow clearing of preshared key while being able to persist in the config file.
|
// to allow clearing of preshared key while being able to persist in the config file.
|
||||||
string preSharedKey = 2 [deprecated=true];
|
string preSharedKey = 2 [deprecated = true];
|
||||||
|
|
||||||
// managementUrl to authenticate.
|
// managementUrl to authenticate.
|
||||||
string managementUrl = 3;
|
string managementUrl = 3;
|
||||||
@@ -63,6 +63,12 @@ message LoginRequest {
|
|||||||
optional int64 wireguardPort = 12;
|
optional int64 wireguardPort = 12;
|
||||||
|
|
||||||
optional string optionalPreSharedKey = 13;
|
optional string optionalPreSharedKey = 13;
|
||||||
|
|
||||||
|
optional bool disableAutoConnect = 14;
|
||||||
|
|
||||||
|
optional bool serverSSHAllowed = 15;
|
||||||
|
|
||||||
|
optional bool rosenpassPermissive = 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LoginResponse {
|
message LoginResponse {
|
||||||
@@ -134,6 +140,7 @@ message PeerState {
|
|||||||
google.protobuf.Timestamp lastWireguardHandshake = 12;
|
google.protobuf.Timestamp lastWireguardHandshake = 12;
|
||||||
int64 bytesRx = 13;
|
int64 bytesRx = 13;
|
||||||
int64 bytesTx = 14;
|
int64 bytesTx = 14;
|
||||||
|
bool rosenpassEnabled = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalPeerState contains the latest state of the local peer
|
// LocalPeerState contains the latest state of the local peer
|
||||||
@@ -142,6 +149,8 @@ message LocalPeerState {
|
|||||||
string pubKey = 2;
|
string pubKey = 2;
|
||||||
bool kernelInterface = 3;
|
bool kernelInterface = 3;
|
||||||
string fqdn = 4;
|
string fqdn = 4;
|
||||||
|
bool rosenpassEnabled = 5;
|
||||||
|
bool rosenpassPermissive = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignalState contains the latest state of a signal connection
|
// SignalState contains the latest state of a signal connection
|
||||||
|
|||||||
@@ -112,15 +112,17 @@ func (s *Server) Start() error {
|
|||||||
|
|
||||||
if s.statusRecorder == nil {
|
if s.statusRecorder == nil {
|
||||||
s.statusRecorder = peer.NewRecorder(config.ManagementURL.String())
|
s.statusRecorder = peer.NewRecorder(config.ManagementURL.String())
|
||||||
} else {
|
|
||||||
s.statusRecorder.UpdateManagementAddress(config.ManagementURL.String())
|
|
||||||
}
|
}
|
||||||
|
s.statusRecorder.UpdateManagementAddress(config.ManagementURL.String())
|
||||||
|
s.statusRecorder.UpdateRosenpass(config.RosenpassEnabled, config.RosenpassPermissive)
|
||||||
|
|
||||||
go func() {
|
if !config.DisableAutoConnect {
|
||||||
if err := internal.RunClientWithProbes(ctx, config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe); err != nil {
|
go func() {
|
||||||
log.Errorf("init connections: %v", err)
|
if err := internal.RunClientWithProbes(ctx, config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe); err != nil {
|
||||||
}
|
log.Errorf("init connections: %v", err)
|
||||||
}()
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -204,6 +206,21 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro
|
|||||||
s.latestConfigInput.RosenpassEnabled = msg.RosenpassEnabled
|
s.latestConfigInput.RosenpassEnabled = msg.RosenpassEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if msg.RosenpassPermissive != nil {
|
||||||
|
inputConfig.RosenpassPermissive = msg.RosenpassPermissive
|
||||||
|
s.latestConfigInput.RosenpassPermissive = msg.RosenpassPermissive
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.ServerSSHAllowed != nil {
|
||||||
|
inputConfig.ServerSSHAllowed = msg.ServerSSHAllowed
|
||||||
|
s.latestConfigInput.ServerSSHAllowed = msg.ServerSSHAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.DisableAutoConnect != nil {
|
||||||
|
inputConfig.DisableAutoConnect = msg.DisableAutoConnect
|
||||||
|
s.latestConfigInput.DisableAutoConnect = msg.DisableAutoConnect
|
||||||
|
}
|
||||||
|
|
||||||
if msg.InterfaceName != nil {
|
if msg.InterfaceName != nil {
|
||||||
inputConfig.InterfaceName = msg.InterfaceName
|
inputConfig.InterfaceName = msg.InterfaceName
|
||||||
s.latestConfigInput.InterfaceName = msg.InterfaceName
|
s.latestConfigInput.InterfaceName = msg.InterfaceName
|
||||||
@@ -416,9 +433,9 @@ func (s *Server) Up(callerCtx context.Context, _ *proto.UpRequest) (*proto.UpRes
|
|||||||
|
|
||||||
if s.statusRecorder == nil {
|
if s.statusRecorder == nil {
|
||||||
s.statusRecorder = peer.NewRecorder(s.config.ManagementURL.String())
|
s.statusRecorder = peer.NewRecorder(s.config.ManagementURL.String())
|
||||||
} else {
|
|
||||||
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
|
||||||
}
|
}
|
||||||
|
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
||||||
|
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := internal.RunClientWithProbes(ctx, s.config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe); err != nil {
|
if err := internal.RunClientWithProbes(ctx, s.config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe); err != nil {
|
||||||
@@ -462,9 +479,9 @@ func (s *Server) Status(
|
|||||||
|
|
||||||
if s.statusRecorder == nil {
|
if s.statusRecorder == nil {
|
||||||
s.statusRecorder = peer.NewRecorder(s.config.ManagementURL.String())
|
s.statusRecorder = peer.NewRecorder(s.config.ManagementURL.String())
|
||||||
} else {
|
|
||||||
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
|
||||||
}
|
}
|
||||||
|
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
|
||||||
|
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
|
||||||
|
|
||||||
if msg.GetFullPeerStatus {
|
if msg.GetFullPeerStatus {
|
||||||
s.runProbes()
|
s.runProbes()
|
||||||
@@ -550,6 +567,8 @@ func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
|
|||||||
pbFullStatus.LocalPeerState.PubKey = fullStatus.LocalPeerState.PubKey
|
pbFullStatus.LocalPeerState.PubKey = fullStatus.LocalPeerState.PubKey
|
||||||
pbFullStatus.LocalPeerState.KernelInterface = fullStatus.LocalPeerState.KernelInterface
|
pbFullStatus.LocalPeerState.KernelInterface = fullStatus.LocalPeerState.KernelInterface
|
||||||
pbFullStatus.LocalPeerState.Fqdn = fullStatus.LocalPeerState.FQDN
|
pbFullStatus.LocalPeerState.Fqdn = fullStatus.LocalPeerState.FQDN
|
||||||
|
pbFullStatus.LocalPeerState.RosenpassPermissive = fullStatus.RosenpassState.Permissive
|
||||||
|
pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled
|
||||||
|
|
||||||
for _, peerState := range fullStatus.Peers {
|
for _, peerState := range fullStatus.Peers {
|
||||||
pbPeerState := &proto.PeerState{
|
pbPeerState := &proto.PeerState{
|
||||||
@@ -567,6 +586,7 @@ func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
|
|||||||
LastWireguardHandshake: timestamppb.New(peerState.LastWireguardHandshake),
|
LastWireguardHandshake: timestamppb.New(peerState.LastWireguardHandshake),
|
||||||
BytesRx: peerState.BytesRx,
|
BytesRx: peerState.BytesRx,
|
||||||
BytesTx: peerState.BytesTx,
|
BytesTx: peerState.BytesTx,
|
||||||
|
RosenpassEnabled: peerState.RosenpassEnabled,
|
||||||
}
|
}
|
||||||
pbFullStatus.Peers = append(pbFullStatus.Peers, pbPeerState)
|
pbFullStatus.Peers = append(pbFullStatus.Peers, pbPeerState)
|
||||||
}
|
}
|
||||||
|
|||||||
24
client/system/detect_cloud/alibabacloud.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package detect_cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectAlibabaCloud(ctx context.Context) string {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://100.100.100.200/latest/", nil)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return "Alibaba Cloud"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
57
client/system/detect_cloud/aws.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package detect_cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectAWS(ctx context.Context) string {
|
||||||
|
v1ResultChan := make(chan bool, 1)
|
||||||
|
v2ResultChan := make(chan bool, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
v1ResultChan <- detectAWSIDMSv1(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
v2ResultChan <- detectAWSIDMSv2(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
v1Result, v2Result := <-v1ResultChan, <-v2ResultChan
|
||||||
|
|
||||||
|
if v1Result || v2Result {
|
||||||
|
return "Amazon Web Services"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectAWSIDMSv1(ctx context.Context) bool {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://169.254.169.254/latest/", nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return resp.StatusCode == http.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectAWSIDMSv2(ctx context.Context) bool {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "PUT", "http://169.254.169.254/latest/api/token", nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
req.Header.Set("X-aws-ec2-metadata-token-ttl-seconds", "21600")
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return resp.StatusCode == http.StatusOK
|
||||||
|
}
|
||||||
25
client/system/detect_cloud/azure.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package detect_cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectAzure(ctx context.Context) string {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://169.254.169.254/metadata/instance?api-version=2021-02-01", nil)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
req.Header.Set("Metadata", "true")
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return "Microsoft Azure"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
65
client/system/detect_cloud/detect.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package detect_cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
This packages is inspired by the work of the original author (https://github.com/perlogix), but it has been modified to fit the needs of the project.
|
||||||
|
Original project: https://github.com/perlogix/libdetectcloud
|
||||||
|
*/
|
||||||
|
|
||||||
|
var hc = &http.Client{Timeout: 300 * time.Millisecond}
|
||||||
|
|
||||||
|
func Detect(ctx context.Context) string {
|
||||||
|
subCtx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
funcs := []func(context.Context) string{
|
||||||
|
detectAlibabaCloud,
|
||||||
|
detectAWS,
|
||||||
|
detectAzure,
|
||||||
|
detectDigitalOcean,
|
||||||
|
detectGCP,
|
||||||
|
detectOracle,
|
||||||
|
detectIBMCloud,
|
||||||
|
detectSoftlayer,
|
||||||
|
detectVultr,
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make(chan string, len(funcs))
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for _, fn := range funcs {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(f func(context.Context) string) {
|
||||||
|
defer wg.Done()
|
||||||
|
select {
|
||||||
|
case <-subCtx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
if result := f(ctx); result != "" {
|
||||||
|
results <- result
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(results)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for result := range results {
|
||||||
|
if result != "" {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
24
client/system/detect_cloud/digitalocean.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package detect_cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectDigitalOcean(ctx context.Context) string {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://169.254.169.254/metadata/v1/", nil)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return "Digital Ocean"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
25
client/system/detect_cloud/gcp.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package detect_cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectGCP(ctx context.Context) string {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://metadata.google.internal", nil)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
req.Header.Add("Metadata-Flavor", "Google")
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return "Google Cloud Platform"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
54
client/system/detect_cloud/ibmcloud.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package detect_cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectIBMCloud(ctx context.Context) string {
|
||||||
|
v1ResultChan := make(chan bool, 1)
|
||||||
|
v2ResultChan := make(chan bool, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
v1ResultChan <- detectIBMSecure(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
v2ResultChan <- detectIBM(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
v1Result, v2Result := <-v1ResultChan, <-v2ResultChan
|
||||||
|
|
||||||
|
if v1Result || v2Result {
|
||||||
|
return "IBM Cloud"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectIBMSecure(ctx context.Context) bool {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "PUT", "https://api.metadata.cloud.ibm.com/instance_identity/v1/token", nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return resp.StatusCode == http.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectIBM(ctx context.Context) bool {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "PUT", "http://api.metadata.cloud.ibm.com/instance_identity/v1/token", nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return resp.StatusCode == http.StatusOK
|
||||||
|
}
|
||||||
56
client/system/detect_cloud/oracle.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package detect_cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectOracle(ctx context.Context) string {
|
||||||
|
v1ResultChan := make(chan bool, 1)
|
||||||
|
v2ResultChan := make(chan bool, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
v1ResultChan <- detectOracleIDMSv1(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
v2ResultChan <- detectOracleIDMSv2(ctx)
|
||||||
|
}()
|
||||||
|
|
||||||
|
v1Result, v2Result := <-v1ResultChan, <-v2ResultChan
|
||||||
|
|
||||||
|
if v1Result || v2Result {
|
||||||
|
return "Oracle"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectOracleIDMSv1(ctx context.Context) bool {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://169.254.169.254/opc/v1/instance/", nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
req.Header.Add("Authorization", "Bearer Oracle")
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return resp.StatusCode == http.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
func detectOracleIDMSv2(ctx context.Context) bool {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://169.254.169.254/opc/v2/instance/", nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
req.Header.Add("Authorization", "Bearer Oracle")
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return resp.StatusCode == http.StatusOK
|
||||||
|
}
|
||||||
25
client/system/detect_cloud/softlayer.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package detect_cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectSoftlayer(ctx context.Context) string {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.service.softlayer.com/rest/v3/SoftLayer_Resource_Metadata/UserMetadata.txt", nil)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
// Since SoftLayer was acquired by IBM, we should return "IBM Cloud"
|
||||||
|
return "IBM Cloud"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
24
client/system/detect_cloud/vultr.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package detect_cloud
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectVultr(ctx context.Context) string {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://169.254.169.254/v1.json", nil)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return "Vultr"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
53
client/system/detect_platform/detect.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package detect_platform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var hc = &http.Client{Timeout: 300 * time.Millisecond}
|
||||||
|
|
||||||
|
func Detect(ctx context.Context) string {
|
||||||
|
subCtx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
funcs := []func(context.Context) string{
|
||||||
|
detectOpenStack,
|
||||||
|
detectContainer,
|
||||||
|
}
|
||||||
|
|
||||||
|
results := make(chan string, len(funcs))
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for _, fn := range funcs {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(f func(context.Context) string) {
|
||||||
|
defer wg.Done()
|
||||||
|
select {
|
||||||
|
case <-subCtx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
if result := f(ctx); result != "" {
|
||||||
|
results <- result
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(results)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for result := range results {
|
||||||
|
if result != "" {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
17
client/system/detect_platform/kube.go
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package detect_platform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectContainer(ctx context.Context) string {
|
||||||
|
if _, exists := os.LookupEnv("KUBERNETES_SERVICE_HOST"); exists {
|
||||||
|
return "Kubernetes"
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat("/.dockerenv"); err == nil {
|
||||||
|
return "Docker"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
24
client/system/detect_platform/openstack.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package detect_platform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func detectOpenStack(ctx context.Context) string {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", "http://169.254.169.254/openstack", nil)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return "OpenStack"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ package system
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
@@ -18,12 +20,21 @@ const OsVersionCtxKey = "OsVersion"
|
|||||||
// OsNameCtxKey context key for operating system name
|
// OsNameCtxKey context key for operating system name
|
||||||
const OsNameCtxKey = "OsName"
|
const OsNameCtxKey = "OsName"
|
||||||
|
|
||||||
|
type NetworkAddress struct {
|
||||||
|
NetIP netip.Prefix
|
||||||
|
Mac string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Environment struct {
|
||||||
|
Cloud string
|
||||||
|
Platform string
|
||||||
|
}
|
||||||
|
|
||||||
// Info is an object that contains machine information
|
// Info is an object that contains machine information
|
||||||
// Most of the code is taken from https://github.com/matishsiao/goInfo
|
// Most of the code is taken from https://github.com/matishsiao/goInfo
|
||||||
type Info struct {
|
type Info struct {
|
||||||
GoOS string
|
GoOS string
|
||||||
Kernel string
|
Kernel string
|
||||||
Core string
|
|
||||||
Platform string
|
Platform string
|
||||||
OS string
|
OS string
|
||||||
OSVersion string
|
OSVersion string
|
||||||
@@ -31,6 +42,12 @@ type Info struct {
|
|||||||
CPUs int
|
CPUs int
|
||||||
WiretrusteeVersion string
|
WiretrusteeVersion string
|
||||||
UIVersion string
|
UIVersion string
|
||||||
|
KernelVersion string
|
||||||
|
NetworkAddresses []NetworkAddress
|
||||||
|
SystemSerialNumber string
|
||||||
|
SystemProductName string
|
||||||
|
SystemManufacturer string
|
||||||
|
Environment Environment
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractUserAgent extracts Netbird's agent (client) name and version from the outgoing context
|
// extractUserAgent extracts Netbird's agent (client) name and version from the outgoing context
|
||||||
@@ -62,3 +79,53 @@ func extractDeviceName(ctx context.Context, defaultName string) string {
|
|||||||
func GetDesktopUIUserAgent() string {
|
func GetDesktopUIUserAgent() string {
|
||||||
return "netbird-desktop-ui/" + version.NetbirdVersion()
|
return "netbird-desktop-ui/" + version.NetbirdVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func networkAddresses() ([]NetworkAddress, error) {
|
||||||
|
interfaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var netAddresses []NetworkAddress
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if iface.HardwareAddr.String() == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, address := range addrs {
|
||||||
|
ipNet, ok := address.(*net.IPNet)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipNet.IP.IsLoopback() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
netAddr := NetworkAddress{
|
||||||
|
NetIP: netip.MustParsePrefix(ipNet.String()),
|
||||||
|
Mac: iface.HardwareAddr.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if isDuplicated(netAddresses, netAddr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
netAddresses = append(netAddresses, netAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return netAddresses, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDuplicated(addresses []NetworkAddress, addr NetworkAddress) bool {
|
||||||
|
for _, duplicated := range addresses {
|
||||||
|
if duplicated.NetIP == addr.NetIP {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,7 +23,12 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
kernel = osInfo[1]
|
kernel = osInfo[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
gio := &Info{Kernel: kernel, Core: osVersion(), Platform: "unknown", OS: "android", OSVersion: osVersion(), GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
var kernelVersion string
|
||||||
|
if len(osInfo) > 2 {
|
||||||
|
kernelVersion = osInfo[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
gio := &Info{Kernel: kernel, Platform: "unknown", OS: "android", OSVersion: osVersion(), GoOS: runtime.GOOS, CPUs: runtime.NumCPU(), KernelVersion: kernelVersion}
|
||||||
gio.Hostname = extractDeviceName(ctx, "android")
|
gio.Hostname = extractDeviceName(ctx, "android")
|
||||||
gio.WiretrusteeVersion = version.NetbirdVersion()
|
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||||
gio.UIVersion = extractUserAgent(ctx)
|
gio.UIVersion = extractUserAgent(ctx)
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import (
|
|||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_cloud"
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_platform"
|
||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,7 +35,34 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
log.Warnf("got an error while retrieving macOS version with sw_vers, error: %s. Using darwin version instead.\n", err)
|
log.Warnf("got an error while retrieving macOS version with sw_vers, error: %s. Using darwin version instead.\n", err)
|
||||||
swVersion = []byte(release)
|
swVersion = []byte(release)
|
||||||
}
|
}
|
||||||
gio := &Info{Kernel: sysName, OSVersion: strings.TrimSpace(string(swVersion)), Core: release, Platform: machine, OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
|
||||||
|
addrs, err := networkAddresses()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to discover network addresses: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serialNum, prodName, manufacturer := sysInfo()
|
||||||
|
|
||||||
|
env := Environment{
|
||||||
|
Cloud: detect_cloud.Detect(ctx),
|
||||||
|
Platform: detect_platform.Detect(ctx),
|
||||||
|
}
|
||||||
|
|
||||||
|
gio := &Info{
|
||||||
|
Kernel: sysName,
|
||||||
|
OSVersion: strings.TrimSpace(string(swVersion)),
|
||||||
|
Platform: machine,
|
||||||
|
OS: sysName,
|
||||||
|
GoOS: runtime.GOOS,
|
||||||
|
CPUs: runtime.NumCPU(),
|
||||||
|
KernelVersion: release,
|
||||||
|
NetworkAddresses: addrs,
|
||||||
|
SystemSerialNumber: serialNum,
|
||||||
|
SystemProductName: prodName,
|
||||||
|
SystemManufacturer: manufacturer,
|
||||||
|
Environment: env,
|
||||||
|
}
|
||||||
|
|
||||||
systemHostname, _ := os.Hostname()
|
systemHostname, _ := os.Hostname()
|
||||||
gio.Hostname = extractDeviceName(ctx, systemHostname)
|
gio.Hostname = extractDeviceName(ctx, systemHostname)
|
||||||
gio.WiretrusteeVersion = version.NetbirdVersion()
|
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||||
@@ -41,3 +70,31 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
|
|
||||||
return gio
|
return gio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sysInfo() (serialNumber string, productName string, manufacturer string) {
|
||||||
|
out, _ := exec.Command("/usr/sbin/ioreg", "-l").Output() // err ignored for brevity
|
||||||
|
for _, l := range strings.Split(string(out), "\n") {
|
||||||
|
if strings.Contains(l, "IOPlatformSerialNumber") {
|
||||||
|
serialNumber = trimIoRegLine(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(l, "ModelNumber") && productName == "" {
|
||||||
|
productName = trimIoRegLine(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(l, "device manufacturer") && manufacturer == "" {
|
||||||
|
manufacturer = trimIoRegLine(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimIoRegLine(l string) string {
|
||||||
|
kv := strings.Split(l, "=")
|
||||||
|
if len(kv) != 2 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
s := strings.TrimSpace(kv[1])
|
||||||
|
return strings.Trim(s, `"`)
|
||||||
|
}
|
||||||
|
|||||||
23
client/system/info_darwin_test.go
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_sysInfo(t *testing.T) {
|
||||||
|
t.Skip("skipping darwin test")
|
||||||
|
serialNum, prodName, manufacturer := sysInfo()
|
||||||
|
if serialNum == "" {
|
||||||
|
t.Errorf("serialNum is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if prodName == "" {
|
||||||
|
t.Errorf("prodName is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if manufacturer == "" {
|
||||||
|
t.Errorf("manufacturer is empty")
|
||||||
|
}
|
||||||
|
log.Infof("Mac sys info: %s, %s, %s", serialNum, prodName, manufacturer)
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_cloud"
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_platform"
|
||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,7 +25,14 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
osStr := strings.Replace(out, "\n", "", -1)
|
osStr := strings.Replace(out, "\n", "", -1)
|
||||||
osStr = strings.Replace(osStr, "\r\n", "", -1)
|
osStr = strings.Replace(osStr, "\r\n", "", -1)
|
||||||
osInfo := strings.Split(osStr, " ")
|
osInfo := strings.Split(osStr, " ")
|
||||||
gio := &Info{Kernel: osInfo[0], Core: osInfo[1], Platform: runtime.GOARCH, OS: osInfo[2], GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
|
||||||
|
env := Environment{
|
||||||
|
Cloud: detect_cloud.Detect(ctx),
|
||||||
|
Platform: detect_platform.Detect(ctx),
|
||||||
|
}
|
||||||
|
|
||||||
|
gio := &Info{Kernel: osInfo[0], Platform: runtime.GOARCH, OS: osInfo[2], GoOS: runtime.GOOS, CPUs: runtime.NumCPU(), KernelVersion: osInfo[1], Environment: env}
|
||||||
|
|
||||||
systemHostname, _ := os.Hostname()
|
systemHostname, _ := os.Hostname()
|
||||||
gio.Hostname = extractDeviceName(ctx, systemHostname)
|
gio.Hostname = extractDeviceName(ctx, systemHostname)
|
||||||
gio.WiretrusteeVersion = version.NetbirdVersion()
|
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
sysName := extractOsName(ctx, "sysName")
|
sysName := extractOsName(ctx, "sysName")
|
||||||
swVersion := extractOsVersion(ctx, "swVersion")
|
swVersion := extractOsVersion(ctx, "swVersion")
|
||||||
|
|
||||||
gio := &Info{Kernel: sysName, OSVersion: swVersion, Core: swVersion, Platform: "unknown", OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
gio := &Info{Kernel: sysName, OSVersion: swVersion, Platform: "unknown", OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU(), KernelVersion: swVersion}
|
||||||
gio.Hostname = extractDeviceName(ctx, "hostname")
|
gio.Hostname = extractDeviceName(ctx, "hostname")
|
||||||
gio.WiretrusteeVersion = version.NetbirdVersion()
|
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||||
gio.UIVersion = extractUserAgent(ctx)
|
gio.UIVersion = extractUserAgent(ctx)
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/zcalusic/sysinfo"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_cloud"
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_platform"
|
||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,11 +53,38 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
if osName == "" {
|
if osName == "" {
|
||||||
osName = osInfo[3]
|
osName = osInfo[3]
|
||||||
}
|
}
|
||||||
gio := &Info{Kernel: osInfo[0], Core: osInfo[1], Platform: osInfo[2], OS: osName, OSVersion: osVer, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
|
||||||
systemHostname, _ := os.Hostname()
|
systemHostname, _ := os.Hostname()
|
||||||
gio.Hostname = extractDeviceName(ctx, systemHostname)
|
|
||||||
gio.WiretrusteeVersion = version.NetbirdVersion()
|
addrs, err := networkAddresses()
|
||||||
gio.UIVersion = extractUserAgent(ctx)
|
if err != nil {
|
||||||
|
log.Warnf("failed to discover network addresses: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serialNum, prodName, manufacturer := sysInfo()
|
||||||
|
|
||||||
|
env := Environment{
|
||||||
|
Cloud: detect_cloud.Detect(ctx),
|
||||||
|
Platform: detect_platform.Detect(ctx),
|
||||||
|
}
|
||||||
|
|
||||||
|
gio := &Info{
|
||||||
|
Kernel: osInfo[0],
|
||||||
|
Platform: osInfo[2],
|
||||||
|
OS: osName,
|
||||||
|
OSVersion: osVer,
|
||||||
|
Hostname: extractDeviceName(ctx, systemHostname),
|
||||||
|
GoOS: runtime.GOOS,
|
||||||
|
CPUs: runtime.NumCPU(),
|
||||||
|
WiretrusteeVersion: version.NetbirdVersion(),
|
||||||
|
UIVersion: extractUserAgent(ctx),
|
||||||
|
KernelVersion: osInfo[1],
|
||||||
|
NetworkAddresses: addrs,
|
||||||
|
SystemSerialNumber: serialNum,
|
||||||
|
SystemProductName: prodName,
|
||||||
|
SystemManufacturer: manufacturer,
|
||||||
|
Environment: env,
|
||||||
|
}
|
||||||
|
|
||||||
return gio
|
return gio
|
||||||
}
|
}
|
||||||
@@ -86,3 +116,9 @@ func _getReleaseInfo() string {
|
|||||||
}
|
}
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sysInfo() (serialNumber string, productName string, manufacturer string) {
|
||||||
|
var si sysinfo.SysInfo
|
||||||
|
si.GetSysInfo()
|
||||||
|
return si.Product.Version, si.Product.Name, si.Product.Vendor
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,3 +33,13 @@ func Test_CustomHostname(t *testing.T) {
|
|||||||
got := GetInfo(ctx)
|
got := GetInfo(ctx)
|
||||||
assert.Equal(t, want, got.Hostname)
|
assert.Equal(t, want, got.Hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_NetAddresses(t *testing.T) {
|
||||||
|
addr, err := networkAddresses()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to discover network addresses: %s", err)
|
||||||
|
}
|
||||||
|
if len(addr) == 0 {
|
||||||
|
t.Errorf("no network addresses found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
"github.com/yusufpapurcu/wmi"
|
"github.com/yusufpapurcu/wmi"
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_cloud"
|
||||||
|
"github.com/netbirdio/netbird/client/system/detect_platform"
|
||||||
"github.com/netbirdio/netbird/version"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,11 +20,63 @@ type Win32_OperatingSystem struct {
|
|||||||
Caption string
|
Caption string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Win32_ComputerSystem struct {
|
||||||
|
Manufacturer string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Win32_ComputerSystemProduct struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Win32_BIOS struct {
|
||||||
|
SerialNumber string
|
||||||
|
}
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
func GetInfo(ctx context.Context) *Info {
|
func GetInfo(ctx context.Context) *Info {
|
||||||
osName, osVersion := getOSNameAndVersion()
|
osName, osVersion := getOSNameAndVersion()
|
||||||
buildVersion := getBuildVersion()
|
buildVersion := getBuildVersion()
|
||||||
gio := &Info{Kernel: "windows", OSVersion: osVersion, Core: buildVersion, Platform: "unknown", OS: osName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
|
||||||
|
addrs, err := networkAddresses()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to discover network addresses: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
serialNum, err := sysNumber()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to get system serial number: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
prodName, err := sysProductName()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to get system product name: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manufacturer, err := sysManufacturer()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to get system manufacturer: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
env := Environment{
|
||||||
|
Cloud: detect_cloud.Detect(ctx),
|
||||||
|
Platform: detect_platform.Detect(ctx),
|
||||||
|
}
|
||||||
|
|
||||||
|
gio := &Info{
|
||||||
|
Kernel: "windows",
|
||||||
|
OSVersion: osVersion,
|
||||||
|
Platform: "unknown",
|
||||||
|
OS: osName,
|
||||||
|
GoOS: runtime.GOOS,
|
||||||
|
CPUs: runtime.NumCPU(),
|
||||||
|
KernelVersion: buildVersion,
|
||||||
|
NetworkAddresses: addrs,
|
||||||
|
SystemSerialNumber: serialNum,
|
||||||
|
SystemProductName: prodName,
|
||||||
|
SystemManufacturer: manufacturer,
|
||||||
|
Environment: env,
|
||||||
|
}
|
||||||
|
|
||||||
systemHostname, _ := os.Hostname()
|
systemHostname, _ := os.Hostname()
|
||||||
gio.Hostname = extractDeviceName(ctx, systemHostname)
|
gio.Hostname = extractDeviceName(ctx, systemHostname)
|
||||||
gio.WiretrusteeVersion = version.NetbirdVersion()
|
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||||
@@ -93,3 +147,33 @@ func getBuildVersion() string {
|
|||||||
ver := fmt.Sprintf("%d.%d.%s.%d", major, minor, build, ubr)
|
ver := fmt.Sprintf("%d.%d.%s.%d", major, minor, build, ubr)
|
||||||
return ver
|
return ver
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sysNumber() (string, error) {
|
||||||
|
var dst []Win32_BIOS
|
||||||
|
query := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(query, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return dst[0].SerialNumber, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysProductName() (string, error) {
|
||||||
|
var dst []Win32_ComputerSystemProduct
|
||||||
|
query := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(query, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return dst[0].Name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sysManufacturer() (string, error) {
|
||||||
|
var dst []Win32_ComputerSystem
|
||||||
|
query := wmi.CreateQuery(&dst, "")
|
||||||
|
err := wmi.Query(query, &dst)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return dst[0].Manufacturer, nil
|
||||||
|
}
|
||||||
|
|||||||
25
client/system/info_windows_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_sysInfo(t *testing.T) {
|
||||||
|
serialNum, err := sysNumber()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to get system serial number: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
prodName, err := sysProductName()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to get system product name: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manufacturer, err := sysManufacturer()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to get system manufacturer: %s", err)
|
||||||
|
}
|
||||||
|
log.Infof("Windows sys info: %s, %s, %s", serialNum, prodName, manufacturer)
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@ func main() {
|
|||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
a := app.New()
|
a := app.NewWithID("NetBird")
|
||||||
a.SetIcon(fyne.NewStaticResource("netbird", iconDisconnectedPNG))
|
a.SetIcon(fyne.NewStaticResource("netbird", iconDisconnectedPNG))
|
||||||
|
|
||||||
client := newServiceClient(daemonAddr, a, showSettings)
|
client := newServiceClient(daemonAddr, a, showSettings)
|
||||||
@@ -82,17 +82,23 @@ var iconConnectedICO []byte
|
|||||||
//go:embed netbird-systemtray-connected.png
|
//go:embed netbird-systemtray-connected.png
|
||||||
var iconConnectedPNG []byte
|
var iconConnectedPNG []byte
|
||||||
|
|
||||||
//go:embed netbird-systemtray-default.ico
|
//go:embed netbird-systemtray-disconnected.ico
|
||||||
var iconDisconnectedICO []byte
|
var iconDisconnectedICO []byte
|
||||||
|
|
||||||
//go:embed netbird-systemtray-default.png
|
//go:embed netbird-systemtray-disconnected.png
|
||||||
var iconDisconnectedPNG []byte
|
var iconDisconnectedPNG []byte
|
||||||
|
|
||||||
//go:embed netbird-systemtray-update.ico
|
//go:embed netbird-systemtray-update-disconnected.ico
|
||||||
var iconUpdateICO []byte
|
var iconUpdateDisconnectedICO []byte
|
||||||
|
|
||||||
//go:embed netbird-systemtray-update.png
|
//go:embed netbird-systemtray-update-disconnected.png
|
||||||
var iconUpdatePNG []byte
|
var iconUpdateDisconnectedPNG []byte
|
||||||
|
|
||||||
|
//go:embed netbird-systemtray-update-connected.ico
|
||||||
|
var iconUpdateConnectedICO []byte
|
||||||
|
|
||||||
|
//go:embed netbird-systemtray-update-connected.png
|
||||||
|
var iconUpdateConnectedPNG []byte
|
||||||
|
|
||||||
//go:embed netbird-systemtray-update-cloud.ico
|
//go:embed netbird-systemtray-update-cloud.ico
|
||||||
var iconUpdateCloudICO []byte
|
var iconUpdateCloudICO []byte
|
||||||
@@ -105,10 +111,11 @@ type serviceClient struct {
|
|||||||
addr string
|
addr string
|
||||||
conn proto.DaemonServiceClient
|
conn proto.DaemonServiceClient
|
||||||
|
|
||||||
icConnected []byte
|
icConnected []byte
|
||||||
icDisconnected []byte
|
icDisconnected []byte
|
||||||
icUpdate []byte
|
icUpdateConnected []byte
|
||||||
icUpdateCloud []byte
|
icUpdateDisconnected []byte
|
||||||
|
icUpdateCloud []byte
|
||||||
|
|
||||||
// systray menu items
|
// systray menu items
|
||||||
mStatus *systray.MenuItem
|
mStatus *systray.MenuItem
|
||||||
@@ -123,9 +130,10 @@ type serviceClient struct {
|
|||||||
mQuit *systray.MenuItem
|
mQuit *systray.MenuItem
|
||||||
|
|
||||||
// application with main windows.
|
// application with main windows.
|
||||||
app fyne.App
|
app fyne.App
|
||||||
wSettings fyne.Window
|
wSettings fyne.Window
|
||||||
showSettings bool
|
showSettings bool
|
||||||
|
sendNotification bool
|
||||||
|
|
||||||
// input elements for settings form
|
// input elements for settings form
|
||||||
iMngURL *widget.Entry
|
iMngURL *widget.Entry
|
||||||
@@ -139,6 +147,7 @@ type serviceClient struct {
|
|||||||
preSharedKey string
|
preSharedKey string
|
||||||
adminURL string
|
adminURL string
|
||||||
|
|
||||||
|
connected bool
|
||||||
update *version.Update
|
update *version.Update
|
||||||
daemonVersion string
|
daemonVersion string
|
||||||
updateIndicationLock sync.Mutex
|
updateIndicationLock sync.Mutex
|
||||||
@@ -150,9 +159,10 @@ type serviceClient struct {
|
|||||||
// This constructor also builds the UI elements for the settings window.
|
// This constructor also builds the UI elements for the settings window.
|
||||||
func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient {
|
func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient {
|
||||||
s := &serviceClient{
|
s := &serviceClient{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
addr: addr,
|
addr: addr,
|
||||||
app: a,
|
app: a,
|
||||||
|
sendNotification: false,
|
||||||
|
|
||||||
showSettings: showSettings,
|
showSettings: showSettings,
|
||||||
update: version.NewUpdate(),
|
update: version.NewUpdate(),
|
||||||
@@ -161,13 +171,15 @@ func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient
|
|||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
s.icConnected = iconConnectedICO
|
s.icConnected = iconConnectedICO
|
||||||
s.icDisconnected = iconDisconnectedICO
|
s.icDisconnected = iconDisconnectedICO
|
||||||
s.icUpdate = iconUpdateICO
|
s.icUpdateConnected = iconUpdateConnectedICO
|
||||||
|
s.icUpdateDisconnected = iconUpdateDisconnectedICO
|
||||||
s.icUpdateCloud = iconUpdateCloudICO
|
s.icUpdateCloud = iconUpdateCloudICO
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
s.icConnected = iconConnectedPNG
|
s.icConnected = iconConnectedPNG
|
||||||
s.icDisconnected = iconDisconnectedPNG
|
s.icDisconnected = iconDisconnectedPNG
|
||||||
s.icUpdate = iconUpdatePNG
|
s.icUpdateConnected = iconUpdateConnectedPNG
|
||||||
|
s.icUpdateDisconnected = iconUpdateDisconnectedPNG
|
||||||
s.icUpdateCloud = iconUpdateCloudPNG
|
s.icUpdateCloud = iconUpdateCloudPNG
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,9 +379,18 @@ func (s *serviceClient) updateStatus() error {
|
|||||||
s.updateIndicationLock.Lock()
|
s.updateIndicationLock.Lock()
|
||||||
defer s.updateIndicationLock.Unlock()
|
defer s.updateIndicationLock.Unlock()
|
||||||
|
|
||||||
|
// notify the user when the session has expired
|
||||||
|
if status.Status == string(internal.StatusNeedsLogin) {
|
||||||
|
s.onSessionExpire()
|
||||||
|
}
|
||||||
|
|
||||||
var systrayIconState bool
|
var systrayIconState bool
|
||||||
if status.Status == string(internal.StatusConnected) && !s.mUp.Disabled() {
|
if status.Status == string(internal.StatusConnected) && !s.mUp.Disabled() {
|
||||||
if !s.isUpdateIconActive {
|
s.connected = true
|
||||||
|
s.sendNotification = true
|
||||||
|
if s.isUpdateIconActive {
|
||||||
|
systray.SetIcon(s.icUpdateConnected)
|
||||||
|
} else {
|
||||||
systray.SetIcon(s.icConnected)
|
systray.SetIcon(s.icConnected)
|
||||||
}
|
}
|
||||||
systray.SetTooltip("NetBird (Connected)")
|
systray.SetTooltip("NetBird (Connected)")
|
||||||
@@ -378,7 +399,10 @@ func (s *serviceClient) updateStatus() error {
|
|||||||
s.mDown.Enable()
|
s.mDown.Enable()
|
||||||
systrayIconState = true
|
systrayIconState = true
|
||||||
} else if status.Status != string(internal.StatusConnected) && s.mUp.Disabled() {
|
} else if status.Status != string(internal.StatusConnected) && s.mUp.Disabled() {
|
||||||
if !s.isUpdateIconActive {
|
s.connected = false
|
||||||
|
if s.isUpdateIconActive {
|
||||||
|
systray.SetIcon(s.icUpdateDisconnected)
|
||||||
|
} else {
|
||||||
systray.SetIcon(s.icDisconnected)
|
systray.SetIcon(s.icDisconnected)
|
||||||
}
|
}
|
||||||
systray.SetTooltip("NetBird (Disconnected)")
|
systray.SetTooltip("NetBird (Disconnected)")
|
||||||
@@ -605,10 +629,30 @@ func (s *serviceClient) onUpdateAvailable() {
|
|||||||
defer s.updateIndicationLock.Unlock()
|
defer s.updateIndicationLock.Unlock()
|
||||||
|
|
||||||
s.mUpdate.Show()
|
s.mUpdate.Show()
|
||||||
s.mAbout.SetIcon(s.icUpdateCloud)
|
|
||||||
|
|
||||||
s.isUpdateIconActive = true
|
s.isUpdateIconActive = true
|
||||||
systray.SetIcon(s.icUpdate)
|
|
||||||
|
if s.connected {
|
||||||
|
systray.SetIcon(s.icUpdateConnected)
|
||||||
|
} else {
|
||||||
|
systray.SetIcon(s.icUpdateDisconnected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// onSessionExpire sends a notification to the user when the session expires.
|
||||||
|
func (s *serviceClient) onSessionExpire() {
|
||||||
|
if s.sendNotification {
|
||||||
|
title := "Connection session expired"
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
title = "NetBird connection session expired"
|
||||||
|
}
|
||||||
|
s.app.SendNotification(
|
||||||
|
fyne.NewNotification(
|
||||||
|
title,
|
||||||
|
"Please re-authenticate to connect to the network",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
s.sendNotification = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func openURL(url string) error {
|
func openURL(url string) error {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
BIN
client/ui/netbird-systemtray-disconnected.ico
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
BIN
client/ui/netbird-systemtray-disconnected.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
client/ui/netbird-systemtray-update-connected.ico
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
client/ui/netbird-systemtray-update-connected.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
client/ui/netbird-systemtray-update-disconnected.ico
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
client/ui/netbird-systemtray-update-disconnected.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
11
go.mod
@@ -46,8 +46,9 @@ require (
|
|||||||
github.com/golang/mock v1.6.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/martian/v3 v3.0.0
|
||||||
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
|
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240202184442-37827591b26c
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240212192251-757544f21357
|
||||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2
|
||||||
github.com/hashicorp/go-version v1.6.0
|
github.com/hashicorp/go-version v1.6.0
|
||||||
github.com/libp2p/go-netroute v0.2.0
|
github.com/libp2p/go-netroute v0.2.0
|
||||||
@@ -57,9 +58,10 @@ require (
|
|||||||
github.com/miekg/dns v1.1.43
|
github.com/miekg/dns v1.1.43
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/nadoo/ipset v0.5.0
|
github.com/nadoo/ipset v0.5.0
|
||||||
github.com/netbirdio/management-integrations/additions v0.0.0-20240118163419-8a7c87accb22
|
github.com/netbirdio/management-integrations/additions v0.0.0-20240226151841-2e4fe2407450
|
||||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20240118163419-8a7c87accb22
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20240305130559-469a80446ac7
|
||||||
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
github.com/okta/okta-sdk-golang/v2 v2.18.0
|
||||||
|
github.com/oschwald/maxminddb-golang v1.12.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/v2 v2.0.0
|
github.com/pion/stun/v2 v2.0.0
|
||||||
@@ -71,6 +73,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/things-go/go-socks5 v0.0.4
|
github.com/things-go/go-socks5 v0.0.4
|
||||||
github.com/yusufpapurcu/wmi v1.2.3
|
github.com/yusufpapurcu/wmi v1.2.3
|
||||||
|
github.com/zcalusic/sysinfo v1.0.2
|
||||||
go.opentelemetry.io/otel v1.11.1
|
go.opentelemetry.io/otel v1.11.1
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.33.0
|
go.opentelemetry.io/otel/exporters/prometheus v0.33.0
|
||||||
go.opentelemetry.io/otel/metric v0.33.0
|
go.opentelemetry.io/otel/metric v0.33.0
|
||||||
@@ -171,5 +174,3 @@ replace github.com/getlantern/systray => github.com/netbirdio/systray v0.0.0-202
|
|||||||
replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20240105182236-6c340dd55aed
|
replace golang.zx2c4.com/wireguard => github.com/netbirdio/wireguard-go v0.0.0-20240105182236-6c340dd55aed
|
||||||
|
|
||||||
replace github.com/cloudflare/circl => github.com/cunicu/circl v0.0.0-20230801113412-fec58fc7b5f6
|
replace github.com/cloudflare/circl => github.com/cunicu/circl v0.0.0-20230801113412-fec58fc7b5f6
|
||||||
|
|
||||||
replace github.com/grpc-ecosystem/go-grpc-middleware/v2 => github.com/surik/go-grpc-middleware/v2 v2.0.0-20240206110057-98a38fc1f86f
|
|
||||||
|
|||||||
17
go.sum
@@ -255,6 +255,7 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
|
|||||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs=
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
github.com/google/nftables v0.0.0-20220808154552-2eca00135732 h1:csc7dT82JiSLvq4aMyQMIQDL7986NH6Wxf/QrvOj55A=
|
github.com/google/nftables v0.0.0-20220808154552-2eca00135732 h1:csc7dT82JiSLvq4aMyQMIQDL7986NH6Wxf/QrvOj55A=
|
||||||
github.com/google/nftables v0.0.0-20220808154552-2eca00135732/go.mod h1:b97ulCCFipUC+kSin+zygkvUVpx0vyIAwxXFdY3PlNc=
|
github.com/google/nftables v0.0.0-20220808154552-2eca00135732/go.mod h1:b97ulCCFipUC+kSin+zygkvUVpx0vyIAwxXFdY3PlNc=
|
||||||
@@ -286,6 +287,8 @@ github.com/gopherjs/gopherjs v0.0.0-20220410123724-9e86199038b0 h1:fWY+zXdWhvWnd
|
|||||||
github.com/gopherjs/gopherjs v0.0.0-20220410123724-9e86199038b0/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
github.com/gopherjs/gopherjs v0.0.0-20220410123724-9e86199038b0/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240212192251-757544f21357 h1:Fkzd8ktnpOR9h47SXHe2AYPwelXLH2GjGsjlAloiWfo=
|
||||||
|
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240212192251-757544f21357/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4=
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng=
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng=
|
||||||
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
|
github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw=
|
||||||
@@ -374,10 +377,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
|
|||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
github.com/nadoo/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
|
github.com/nadoo/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
|
||||||
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
|
github.com/nadoo/ipset v0.5.0/go.mod h1:rYF5DQLRGGoQ8ZSWeK+6eX5amAuPqwFkWjhQlEITGJQ=
|
||||||
github.com/netbirdio/management-integrations/additions v0.0.0-20240118163419-8a7c87accb22 h1:XTiNnVB6OEwung8WIiGJNzOTLVefuSzAA/cu+6Sst8A=
|
github.com/netbirdio/management-integrations/additions v0.0.0-20240226151841-2e4fe2407450 h1:qA4S5YFt6/s0kQ8wKLjq8faLxuBSte1WzjWfmQmyJTU=
|
||||||
github.com/netbirdio/management-integrations/additions v0.0.0-20240118163419-8a7c87accb22/go.mod h1:31FhBNvQ+riHEIu6LSTmqr8IeuSIsGfQffqV4LFmbwA=
|
github.com/netbirdio/management-integrations/additions v0.0.0-20240226151841-2e4fe2407450/go.mod h1:31FhBNvQ+riHEIu6LSTmqr8IeuSIsGfQffqV4LFmbwA=
|
||||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20240118163419-8a7c87accb22 h1:FNc4p8RS/gFm5jlmvUFWC4/5YxPDWejYyqEBVziFZwo=
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20240305130559-469a80446ac7 h1:YYIQJbRhANmNFClkCmjBa0w33RpTzsF2DpbGAWhul6Y=
|
||||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20240118163419-8a7c87accb22/go.mod h1:B0nMS3es77gOvPYhc0K91fAzTkQLi/jRq5TffUN3klM=
|
github.com/netbirdio/management-integrations/integrations v0.0.0-20240305130559-469a80446ac7/go.mod h1:B0nMS3es77gOvPYhc0K91fAzTkQLi/jRq5TffUN3klM=
|
||||||
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0 h1:hirFRfx3grVA/9eEyjME5/z3nxdJlN9kfQpvWWPk32g=
|
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0 h1:hirFRfx3grVA/9eEyjME5/z3nxdJlN9kfQpvWWPk32g=
|
||||||
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||||
github.com/netbirdio/systray v0.0.0-20231030152038-ef1ed2a27949 h1:xbWM9BU6mwZZLHxEjxIX/V8Hv3HurQt4mReIE4mY4DM=
|
github.com/netbirdio/systray v0.0.0-20231030152038-ef1ed2a27949 h1:xbWM9BU6mwZZLHxEjxIX/V8Hv3HurQt4mReIE4mY4DM=
|
||||||
@@ -407,6 +410,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/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=
|
||||||
|
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
|
||||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
@@ -517,8 +522,6 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
|||||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/surik/go-grpc-middleware/v2 v2.0.0-20240206110057-98a38fc1f86f h1:J+egXEDkpg/vOYYzPO5IwF8OufGb7g+KcwEF1AWIzhQ=
|
|
||||||
github.com/surik/go-grpc-middleware/v2 v2.0.0-20240206110057-98a38fc1f86f/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4=
|
|
||||||
github.com/things-go/go-socks5 v0.0.4 h1:jMQjIc+qhD4z9cITOMnBiwo9dDmpGuXmBlkRFrl/qD0=
|
github.com/things-go/go-socks5 v0.0.4 h1:jMQjIc+qhD4z9cITOMnBiwo9dDmpGuXmBlkRFrl/qD0=
|
||||||
github.com/things-go/go-socks5 v0.0.4/go.mod h1:sh4K6WHrmHZpjxLTCHyYtXYH8OUuD+yZun41NomR1IQ=
|
github.com/things-go/go-socks5 v0.0.4/go.mod h1:sh4K6WHrmHZpjxLTCHyYtXYH8OUuD+yZun41NomR1IQ=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
@@ -539,6 +542,8 @@ 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 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
|
github.com/zcalusic/sysinfo v1.0.2 h1:nwTTo2a+WQ0NXwo0BGRojOJvJ/5XKvQih+2RrtWqfxc=
|
||||||
|
github.com/zcalusic/sysinfo v1.0.2/go.mod h1:kluzTYflRWo6/tXVMJPdEjShsbPpsFRyy+p1mBQPC30=
|
||||||
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=
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/tun/netstack"
|
"golang.zx2c4.com/wireguard/tun/netstack"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NetStackTun struct {
|
type NetStackTun struct { //nolint:revive
|
||||||
address string
|
address string
|
||||||
mtu int
|
mtu int
|
||||||
listenAddress string
|
listenAddress string
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ NETBIRD_DASH_AUTH_USE_AUDIENCE=${NETBIRD_DASH_AUTH_USE_AUDIENCE:-true}
|
|||||||
NETBIRD_DASH_AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
NETBIRD_DASH_AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
|
||||||
|
|
||||||
# Store config
|
# Store config
|
||||||
NETBIRD_STORE_CONFIG_ENGINE=${NETBIRD_STORE_CONFIG_ENGINE:-"jsonfile"}
|
NETBIRD_STORE_CONFIG_ENGINE=${NETBIRD_STORE_CONFIG_ENGINE:-"sqlite"}
|
||||||
|
|
||||||
# Image tags
|
# Image tags
|
||||||
NETBIRD_DASHBOARD_TAG=${NETBIRD_DASHBOARD_TAG:-"latest"}
|
NETBIRD_DASHBOARD_TAG=${NETBIRD_DASHBOARD_TAG:-"latest"}
|
||||||
|
|||||||
109
infrastructure_files/download-geolite2.sh
Executable file
@@ -0,0 +1,109 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# to install sha256sum on mac: brew install coreutils
|
||||||
|
if ! command -v sha256sum &> /dev/null
|
||||||
|
then
|
||||||
|
echo "sha256sum is not installed or not in PATH, please install with your package manager. e.g. sudo apt install sha256sum" > /dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v sqlite3 &> /dev/null
|
||||||
|
then
|
||||||
|
echo "sqlite3 is not installed or not in PATH, please install with your package manager. e.g. sudo apt install sqlite3" > /dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v unzip &> /dev/null
|
||||||
|
then
|
||||||
|
echo "unzip is not installed or not in PATH, please install with your package manager. e.g. sudo apt install unzip" > /dev/stderr
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
download_geolite_mmdb() {
|
||||||
|
DATABASE_URL="https://pkgs.netbird.io/geolocation-dbs/GeoLite2-City/download?suffix=tar.gz"
|
||||||
|
SIGNATURE_URL="https://pkgs.netbird.io/geolocation-dbs/GeoLite2-City/download?suffix=tar.gz.sha256"
|
||||||
|
# Download the database and signature files
|
||||||
|
echo "Downloading mmdb signature file..."
|
||||||
|
SIGNATURE_FILE=$(curl -s -L -O -J "$SIGNATURE_URL" -w "%{filename_effective}")
|
||||||
|
echo "Downloading mmdb database file..."
|
||||||
|
DATABASE_FILE=$(curl -s -L -O -J "$DATABASE_URL" -w "%{filename_effective}")
|
||||||
|
|
||||||
|
# Verify the signature
|
||||||
|
echo "Verifying signature..."
|
||||||
|
if sha256sum -c --status "$SIGNATURE_FILE"; then
|
||||||
|
echo "Signature is valid."
|
||||||
|
else
|
||||||
|
echo "Signature is invalid. Aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Unpack the database file
|
||||||
|
EXTRACTION_DIR=$(basename "$DATABASE_FILE" .tar.gz)
|
||||||
|
echo "Unpacking $DATABASE_FILE..."
|
||||||
|
mkdir -p "$EXTRACTION_DIR"
|
||||||
|
tar -xzvf "$DATABASE_FILE" > /dev/null 2>&1
|
||||||
|
|
||||||
|
MMDB_FILE="GeoLite2-City.mmdb"
|
||||||
|
cp "$EXTRACTION_DIR"/"$MMDB_FILE" $MMDB_FILE
|
||||||
|
|
||||||
|
# Remove downloaded files
|
||||||
|
rm -r "$EXTRACTION_DIR"
|
||||||
|
rm "$DATABASE_FILE" "$SIGNATURE_FILE"
|
||||||
|
|
||||||
|
# Done. Print next steps
|
||||||
|
echo ""
|
||||||
|
echo "Process completed successfully."
|
||||||
|
echo "Now you can place $MMDB_FILE to 'datadir' of management service."
|
||||||
|
echo -e "Example:\n\tdocker compose cp $MMDB_FILE management:/var/lib/netbird/"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
download_geolite_csv_and_create_sqlite_db() {
|
||||||
|
DATABASE_URL="https://pkgs.netbird.io/geolocation-dbs/GeoLite2-City-CSV/download?suffix=zip"
|
||||||
|
SIGNATURE_URL="https://pkgs.netbird.io/geolocation-dbs/GeoLite2-City-CSV/download?suffix=zip.sha256"
|
||||||
|
|
||||||
|
|
||||||
|
# Download the database file
|
||||||
|
echo "Downloading csv signature file..."
|
||||||
|
SIGNATURE_FILE=$(curl -s -L -O -J "$SIGNATURE_URL" -w "%{filename_effective}")
|
||||||
|
echo "Downloading csv database file..."
|
||||||
|
DATABASE_FILE=$(curl -s -L -O -J "$DATABASE_URL" -w "%{filename_effective}")
|
||||||
|
|
||||||
|
# Verify the signature
|
||||||
|
echo "Verifying signature..."
|
||||||
|
if sha256sum -c --status "$SIGNATURE_FILE"; then
|
||||||
|
echo "Signature is valid."
|
||||||
|
else
|
||||||
|
echo "Signature is invalid. Aborting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Unpack the database file
|
||||||
|
EXTRACTION_DIR=$(basename "$DATABASE_FILE" .zip)
|
||||||
|
DB_NAME="geonames.db"
|
||||||
|
|
||||||
|
echo "Unpacking $DATABASE_FILE..."
|
||||||
|
unzip "$DATABASE_FILE" > /dev/null 2>&1
|
||||||
|
|
||||||
|
# Create SQLite database and import data from CSV
|
||||||
|
sqlite3 "$DB_NAME" <<EOF
|
||||||
|
.mode csv
|
||||||
|
.import "$EXTRACTION_DIR/GeoLite2-City-Locations-en.csv" geonames
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
# Remove downloaded and extracted files
|
||||||
|
rm -r -r "$EXTRACTION_DIR"
|
||||||
|
rm "$DATABASE_FILE" "$SIGNATURE_FILE"
|
||||||
|
echo ""
|
||||||
|
echo "SQLite database '$DB_NAME' created successfully."
|
||||||
|
echo "Now you can place $DB_NAME to 'datadir' of management service."
|
||||||
|
echo -e "Example:\n\tdocker compose cp $DB_NAME management:/var/lib/netbird/"
|
||||||
|
}
|
||||||
|
|
||||||
|
download_geolite_mmdb
|
||||||
|
echo -e "\n\n"
|
||||||
|
download_geolite_csv_and_create_sqlite_db
|
||||||
|
echo -e "\n\n"
|
||||||
|
echo "After copying the database files to the management service. You can restart the management service with:"
|
||||||
|
echo -e "Example:\n\tdocker compose restart management"
|
||||||
@@ -137,6 +137,13 @@ create_new_application() {
|
|||||||
BASE_REDIRECT_URL2=$5
|
BASE_REDIRECT_URL2=$5
|
||||||
LOGOUT_URL=$6
|
LOGOUT_URL=$6
|
||||||
ZITADEL_DEV_MODE=$7
|
ZITADEL_DEV_MODE=$7
|
||||||
|
DEVICE_CODE=$8
|
||||||
|
|
||||||
|
if [[ $DEVICE_CODE == "true" ]]; then
|
||||||
|
GRANT_TYPES='["OIDC_GRANT_TYPE_AUTHORIZATION_CODE","OIDC_GRANT_TYPE_DEVICE_CODE","OIDC_GRANT_TYPE_REFRESH_TOKEN"]'
|
||||||
|
else
|
||||||
|
GRANT_TYPES='["OIDC_GRANT_TYPE_AUTHORIZATION_CODE","OIDC_GRANT_TYPE_REFRESH_TOKEN"]'
|
||||||
|
fi
|
||||||
|
|
||||||
RESPONSE=$(
|
RESPONSE=$(
|
||||||
curl -sS -X POST "$INSTANCE_URL/management/v1/projects/$PROJECT_ID/apps/oidc" \
|
curl -sS -X POST "$INSTANCE_URL/management/v1/projects/$PROJECT_ID/apps/oidc" \
|
||||||
@@ -154,10 +161,7 @@ create_new_application() {
|
|||||||
"RESPONSETypes": [
|
"RESPONSETypes": [
|
||||||
"OIDC_RESPONSE_TYPE_CODE"
|
"OIDC_RESPONSE_TYPE_CODE"
|
||||||
],
|
],
|
||||||
"grantTypes": [
|
"grantTypes": '"$GRANT_TYPES"',
|
||||||
"OIDC_GRANT_TYPE_AUTHORIZATION_CODE",
|
|
||||||
"OIDC_GRANT_TYPE_REFRESH_TOKEN"
|
|
||||||
],
|
|
||||||
"appType": "OIDC_APP_TYPE_USER_AGENT",
|
"appType": "OIDC_APP_TYPE_USER_AGENT",
|
||||||
"authMethodType": "OIDC_AUTH_METHOD_TYPE_NONE",
|
"authMethodType": "OIDC_AUTH_METHOD_TYPE_NONE",
|
||||||
"version": "OIDC_VERSION_1_0",
|
"version": "OIDC_VERSION_1_0",
|
||||||
@@ -340,10 +344,10 @@ init_zitadel() {
|
|||||||
|
|
||||||
# create zitadel spa applications
|
# create zitadel spa applications
|
||||||
echo "Creating new Zitadel SPA Dashboard application"
|
echo "Creating new Zitadel SPA Dashboard application"
|
||||||
DASHBOARD_APPLICATION_CLIENT_ID=$(create_new_application "$INSTANCE_URL" "$PAT" "Dashboard" "$BASE_REDIRECT_URL/nb-auth" "$BASE_REDIRECT_URL/nb-silent-auth" "$BASE_REDIRECT_URL/" "$ZITADEL_DEV_MODE")
|
DASHBOARD_APPLICATION_CLIENT_ID=$(create_new_application "$INSTANCE_URL" "$PAT" "Dashboard" "$BASE_REDIRECT_URL/nb-auth" "$BASE_REDIRECT_URL/nb-silent-auth" "$BASE_REDIRECT_URL/" "$ZITADEL_DEV_MODE" "false")
|
||||||
|
|
||||||
echo "Creating new Zitadel SPA Cli application"
|
echo "Creating new Zitadel SPA Cli application"
|
||||||
CLI_APPLICATION_CLIENT_ID=$(create_new_application "$INSTANCE_URL" "$PAT" "Cli" "http://localhost:53000/" "http://localhost:54000/" "http://localhost:53000/" "true")
|
CLI_APPLICATION_CLIENT_ID=$(create_new_application "$INSTANCE_URL" "$PAT" "Cli" "http://localhost:53000/" "http://localhost:54000/" "http://localhost:53000/" "true" "true")
|
||||||
|
|
||||||
MACHINE_USER_ID=$(create_service_user "$INSTANCE_URL" "$PAT")
|
MACHINE_USER_ID=$(create_service_user "$INSTANCE_URL" "$PAT")
|
||||||
|
|
||||||
@@ -561,6 +565,8 @@ renderCaddyfile() {
|
|||||||
reverse_proxy /.well-known/openid-configuration h2c://zitadel:8080
|
reverse_proxy /.well-known/openid-configuration h2c://zitadel:8080
|
||||||
reverse_proxy /openapi/* h2c://zitadel:8080
|
reverse_proxy /openapi/* h2c://zitadel:8080
|
||||||
reverse_proxy /debug/* h2c://zitadel:8080
|
reverse_proxy /debug/* h2c://zitadel:8080
|
||||||
|
reverse_proxy /device/* h2c://zitadel:8080
|
||||||
|
reverse_proxy /device h2c://zitadel:8080
|
||||||
# Dashboard
|
# Dashboard
|
||||||
reverse_proxy /* dashboard:80
|
reverse_proxy /* dashboard:80
|
||||||
}
|
}
|
||||||
@@ -629,6 +635,14 @@ renderManagementJson() {
|
|||||||
"ManagementEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/management/v1"
|
"ManagementEndpoint": "$NETBIRD_HTTP_PROTOCOL://$NETBIRD_DOMAIN/management/v1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"DeviceAuthorizationFlow": {
|
||||||
|
"Provider": "hosted",
|
||||||
|
"ProviderConfig": {
|
||||||
|
"Audience": "$NETBIRD_AUTH_CLIENT_ID_CLI",
|
||||||
|
"ClientID": "$NETBIRD_AUTH_CLIENT_ID_CLI",
|
||||||
|
"Scope": "openid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"PKCEAuthorizationFlow": {
|
"PKCEAuthorizationFlow": {
|
||||||
"ProviderConfig": {
|
"ProviderConfig": {
|
||||||
"Audience": "$NETBIRD_AUTH_CLIENT_ID_CLI",
|
"Audience": "$NETBIRD_AUTH_CLIENT_ID_CLI",
|
||||||
|
|||||||
@@ -26,6 +26,13 @@
|
|||||||
"Username": "",
|
"Username": "",
|
||||||
"Password": null
|
"Password": null
|
||||||
},
|
},
|
||||||
|
"ReverseProxy": {
|
||||||
|
"TrustedHTTPProxies": [],
|
||||||
|
"TrustedHTTPProxiesCount": 0,
|
||||||
|
"TrustedPeers": [
|
||||||
|
"0.0.0.0/0"
|
||||||
|
]
|
||||||
|
},
|
||||||
"Datadir": "",
|
"Datadir": "",
|
||||||
"DataStoreEncryptionKey": "$NETBIRD_DATASTORE_ENC_KEY",
|
"DataStoreEncryptionKey": "$NETBIRD_DATASTORE_ENC_KEY",
|
||||||
"StoreConfig": {
|
"StoreConfig": {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ server {
|
|||||||
proxy_set_header X-Scheme $scheme;
|
proxy_set_header X-Scheme $scheme;
|
||||||
proxy_set_header X-Forwarded-Proto https;
|
proxy_set_header X-Forwarded-Proto https;
|
||||||
proxy_set_header X-Forwarded-Host $host;
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
# Proxy dashboard
|
# Proxy dashboard
|
||||||
location / {
|
location / {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
"github.com/netbirdio/netbird/encryption"
|
"github.com/netbirdio/netbird/encryption"
|
||||||
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"
|
||||||
@@ -60,8 +61,8 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
|
|||||||
|
|
||||||
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
|
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
|
||||||
eventStore := &activity.InMemoryEventStore{}
|
eventStore := &activity.InMemoryEventStore{}
|
||||||
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "",
|
ia, _ := integrations.NewIntegratedApproval(eventStore)
|
||||||
eventStore, false)
|
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false, ia)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -344,18 +345,74 @@ func Test_SystemMetaDataFromClient(t *testing.T) {
|
|||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
|
protoNetAddr := make([]*mgmtProto.NetworkAddress, 0, len(info.NetworkAddresses))
|
||||||
|
for _, addr := range info.NetworkAddresses {
|
||||||
|
protoNetAddr = append(protoNetAddr, &mgmtProto.NetworkAddress{
|
||||||
|
NetIP: addr.NetIP.String(),
|
||||||
|
Mac: addr.Mac,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
expectedMeta := &mgmtProto.PeerSystemMeta{
|
expectedMeta := &mgmtProto.PeerSystemMeta{
|
||||||
Hostname: info.Hostname,
|
Hostname: info.Hostname,
|
||||||
GoOS: info.GoOS,
|
GoOS: info.GoOS,
|
||||||
Kernel: info.Kernel,
|
Kernel: info.Kernel,
|
||||||
Core: info.OSVersion,
|
|
||||||
Platform: info.Platform,
|
Platform: info.Platform,
|
||||||
OS: info.OS,
|
OS: info.OS,
|
||||||
|
Core: info.OSVersion,
|
||||||
|
OSVersion: info.OSVersion,
|
||||||
WiretrusteeVersion: info.WiretrusteeVersion,
|
WiretrusteeVersion: info.WiretrusteeVersion,
|
||||||
|
KernelVersion: info.KernelVersion,
|
||||||
|
|
||||||
|
NetworkAddresses: protoNetAddr,
|
||||||
|
SysSerialNumber: info.SystemSerialNumber,
|
||||||
|
SysProductName: info.SystemProductName,
|
||||||
|
SysManufacturer: info.SystemManufacturer,
|
||||||
|
Environment: &mgmtProto.Environment{Cloud: info.Environment.Cloud, Platform: info.Environment.Platform},
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, ValidKey, actualValidKey)
|
assert.Equal(t, ValidKey, actualValidKey)
|
||||||
assert.Equal(t, expectedMeta, actualMeta)
|
if !isEqual(expectedMeta, actualMeta) {
|
||||||
|
t.Errorf("expected and actual meta are not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEqual(a, b *mgmtProto.PeerSystemMeta) bool {
|
||||||
|
if len(a.NetworkAddresses) != len(b.NetworkAddresses) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range a.GetNetworkAddresses() {
|
||||||
|
var found bool
|
||||||
|
for _, oAddr := range b.GetNetworkAddresses() {
|
||||||
|
if addr.GetMac() == oAddr.GetMac() && addr.GetNetIP() == oAddr.GetNetIP() {
|
||||||
|
found = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("------")
|
||||||
|
|
||||||
|
return a.GetHostname() == b.GetHostname() &&
|
||||||
|
a.GetGoOS() == b.GetGoOS() &&
|
||||||
|
a.GetKernel() == b.GetKernel() &&
|
||||||
|
a.GetKernelVersion() == b.GetKernelVersion() &&
|
||||||
|
a.GetCore() == b.GetCore() &&
|
||||||
|
a.GetPlatform() == b.GetPlatform() &&
|
||||||
|
a.GetOS() == b.GetOS() &&
|
||||||
|
a.GetOSVersion() == b.GetOSVersion() &&
|
||||||
|
a.GetWiretrusteeVersion() == b.GetWiretrusteeVersion() &&
|
||||||
|
a.GetUiVersion() == b.GetUiVersion() &&
|
||||||
|
a.GetSysSerialNumber() == b.GetSysSerialNumber() &&
|
||||||
|
a.GetSysProductName() == b.GetSysProductName() &&
|
||||||
|
a.GetSysManufacturer() == b.GetSysManufacturer() &&
|
||||||
|
a.GetEnvironment().Cloud == b.GetEnvironment().Cloud &&
|
||||||
|
a.GetEnvironment().Platform == b.GetEnvironment().Platform
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_GetDeviceAuthorizationFlow(t *testing.T) {
|
func Test_GetDeviceAuthorizationFlow(t *testing.T) {
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import (
|
|||||||
"github.com/netbirdio/netbird/management/proto"
|
"github.com/netbirdio/netbird/management/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const ConnectTimeout = 10 * time.Second
|
||||||
|
|
||||||
// ConnStateNotifier is a wrapper interface of the status recorders
|
// ConnStateNotifier is a wrapper interface of the status recorders
|
||||||
type ConnStateNotifier interface {
|
type ConnStateNotifier interface {
|
||||||
MarkManagementDisconnected(error)
|
MarkManagementDisconnected(error)
|
||||||
@@ -49,7 +51,7 @@ func NewClient(ctx context.Context, addr string, ourPrivateKey wgtypes.Key, tlsE
|
|||||||
transportOption = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))
|
transportOption = grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
mgmCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
mgmCtx, cancel := context.WithTimeout(ctx, ConnectTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
conn, err := grpc.DialContext(
|
conn, err := grpc.DialContext(
|
||||||
mgmCtx,
|
mgmCtx,
|
||||||
@@ -318,7 +320,7 @@ func (c *GrpcClient) login(serverKey wgtypes.Key, req *proto.LoginRequest) (*pro
|
|||||||
log.Errorf("failed to encrypt message: %s", err)
|
log.Errorf("failed to encrypt message: %s", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mgmCtx, cancel := context.WithTimeout(c.ctx, 5*time.Second)
|
mgmCtx, cancel := context.WithTimeout(c.ctx, ConnectTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
resp, err := c.realClient.Login(mgmCtx, &proto.EncryptedMessage{
|
resp, err := c.realClient.Login(mgmCtx, &proto.EncryptedMessage{
|
||||||
WgPubKey: c.key.PublicKey().String(),
|
WgPubKey: c.key.PublicKey().String(),
|
||||||
@@ -450,14 +452,33 @@ func infoToMetaData(info *system.Info) *proto.PeerSystemMeta {
|
|||||||
if info == nil {
|
if info == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addresses := make([]*proto.NetworkAddress, 0, len(info.NetworkAddresses))
|
||||||
|
for _, addr := range info.NetworkAddresses {
|
||||||
|
addresses = append(addresses, &proto.NetworkAddress{
|
||||||
|
NetIP: addr.NetIP.String(),
|
||||||
|
Mac: addr.Mac,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return &proto.PeerSystemMeta{
|
return &proto.PeerSystemMeta{
|
||||||
Hostname: info.Hostname,
|
Hostname: info.Hostname,
|
||||||
GoOS: info.GoOS,
|
GoOS: info.GoOS,
|
||||||
OS: info.OS,
|
OS: info.OS,
|
||||||
Core: info.OSVersion,
|
Core: info.OSVersion,
|
||||||
|
OSVersion: info.OSVersion,
|
||||||
Platform: info.Platform,
|
Platform: info.Platform,
|
||||||
Kernel: info.Kernel,
|
Kernel: info.Kernel,
|
||||||
WiretrusteeVersion: info.WiretrusteeVersion,
|
WiretrusteeVersion: info.WiretrusteeVersion,
|
||||||
UiVersion: info.UIVersion,
|
UiVersion: info.UIVersion,
|
||||||
|
KernelVersion: info.KernelVersion,
|
||||||
|
NetworkAddresses: addresses,
|
||||||
|
SysSerialNumber: info.SystemSerialNumber,
|
||||||
|
SysManufacturer: info.SystemManufacturer,
|
||||||
|
SysProductName: info.SystemProductName,
|
||||||
|
Environment: &proto.Environment{
|
||||||
|
Cloud: info.Environment.Cloud,
|
||||||
|
Platform: info.Environment.Platform,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/netbirdio/management-integrations/integrations"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -33,15 +31,19 @@ import (
|
|||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
|
|
||||||
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/realip"
|
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/realip"
|
||||||
|
"github.com/netbirdio/management-integrations/integrations"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/encryption"
|
"github.com/netbirdio/netbird/encryption"
|
||||||
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
|
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||||
httpapi "github.com/netbirdio/netbird/management/server/http"
|
httpapi "github.com/netbirdio/netbird/management/server/http"
|
||||||
"github.com/netbirdio/netbird/management/server/idp"
|
"github.com/netbirdio/netbird/management/server/idp"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
"github.com/netbirdio/netbird/management/server/metrics"
|
"github.com/netbirdio/netbird/management/server/metrics"
|
||||||
"github.com/netbirdio/netbird/management/server/telemetry"
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ManagementLegacyPort is the port that was used before by the Management gRPC server.
|
// ManagementLegacyPort is the port that was used before by the Management gRPC server.
|
||||||
@@ -163,8 +165,19 @@ var (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
geo, err := geolocation.NewGeolocation(config.Datadir)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("could not initialize geo location service: %v, we proceed without geo support", err)
|
||||||
|
} else {
|
||||||
|
log.Infof("geo location service has been initialized from %s", config.Datadir)
|
||||||
|
}
|
||||||
|
|
||||||
|
integratedPeerApproval, err := integrations.NewIntegratedApproval(eventStore)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to initialize integrated peer approval: %v", err)
|
||||||
|
}
|
||||||
accountManager, err := server.BuildManager(store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain,
|
accountManager, err := server.BuildManager(store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain,
|
||||||
dnsDomain, eventStore, userDeleteFromIDPEnabled)
|
dnsDomain, eventStore, geo, userDeleteFromIDPEnabled, integratedPeerApproval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to build default manager: %v", err)
|
return fmt.Errorf("failed to build default manager: %v", err)
|
||||||
}
|
}
|
||||||
@@ -183,17 +196,17 @@ var (
|
|||||||
log.Warn("TrustedHTTPProxies and TrustedHTTPProxiesCount both are configured. " +
|
log.Warn("TrustedHTTPProxies and TrustedHTTPProxiesCount both are configured. " +
|
||||||
"This is not recommended way to extract X-Forwarded-For. Consider using one of these options.")
|
"This is not recommended way to extract X-Forwarded-For. Consider using one of these options.")
|
||||||
}
|
}
|
||||||
realipOpts := realip.Opts{
|
realipOpts := []realip.Option{
|
||||||
TrustedPeers: trustedPeers,
|
realip.WithTrustedPeers(trustedPeers),
|
||||||
TrustedProxies: trustedHTTPProxies,
|
realip.WithTrustedProxies(trustedHTTPProxies),
|
||||||
TrustedProxiesCount: trustedProxiesCount,
|
realip.WithTrustedProxiesCount(trustedProxiesCount),
|
||||||
Headers: []string{realip.XForwardedFor, realip.XRealIp},
|
realip.WithHeaders([]string{realip.XForwardedFor, realip.XRealIp}),
|
||||||
}
|
}
|
||||||
gRPCOpts := []grpc.ServerOption{
|
gRPCOpts := []grpc.ServerOption{
|
||||||
grpc.KeepaliveEnforcementPolicy(kaep),
|
grpc.KeepaliveEnforcementPolicy(kaep),
|
||||||
grpc.KeepaliveParams(kasp),
|
grpc.KeepaliveParams(kasp),
|
||||||
grpc.ChainUnaryInterceptor(realip.UnaryServerInterceptorOpts(realipOpts)),
|
grpc.ChainUnaryInterceptor(realip.UnaryServerInterceptorOpts(realipOpts...)),
|
||||||
grpc.ChainStreamInterceptor(realip.StreamServerInterceptorOpts(realipOpts)),
|
grpc.ChainStreamInterceptor(realip.StreamServerInterceptorOpts(realipOpts...)),
|
||||||
}
|
}
|
||||||
|
|
||||||
var certManager *autocert.Manager
|
var certManager *autocert.Manager
|
||||||
@@ -234,7 +247,10 @@ var (
|
|||||||
UserIDClaim: config.HttpConfig.AuthUserIDClaim,
|
UserIDClaim: config.HttpConfig.AuthUserIDClaim,
|
||||||
KeysLocation: config.HttpConfig.AuthKeysLocation,
|
KeysLocation: config.HttpConfig.AuthKeysLocation,
|
||||||
}
|
}
|
||||||
httpAPIHandler, err := httpapi.APIHandler(accountManager, *jwtValidator, appMetrics, httpAPIAuthCfg)
|
|
||||||
|
ctx, cancel := context.WithCancel(cmd.Context())
|
||||||
|
defer cancel()
|
||||||
|
httpAPIHandler, err := httpapi.APIHandler(ctx, accountManager, geo, *jwtValidator, appMetrics, httpAPIAuthCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed creating HTTP API handler: %v", err)
|
return fmt.Errorf("failed creating HTTP API handler: %v", err)
|
||||||
}
|
}
|
||||||
@@ -256,8 +272,6 @@ var (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !disableMetrics {
|
if !disableMetrics {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
idpManager := "disabled"
|
idpManager := "disabled"
|
||||||
if config.IdpManagerConfig != nil && config.IdpManagerConfig.ManagerType != "" {
|
if config.IdpManagerConfig != nil && config.IdpManagerConfig.ManagerType != "" {
|
||||||
idpManager = config.IdpManagerConfig.ManagerType
|
idpManager = config.IdpManagerConfig.ManagerType
|
||||||
@@ -306,12 +320,17 @@ var (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Infof("management server version %s", version.NetbirdVersion())
|
||||||
log.Infof("running HTTP server and gRPC server on the same port: %s", listener.Addr().String())
|
log.Infof("running HTTP server and gRPC server on the same port: %s", listener.Addr().String())
|
||||||
serveGRPCWithHTTP(listener, rootHandler, tlsEnabled)
|
serveGRPCWithHTTP(listener, rootHandler, tlsEnabled)
|
||||||
|
|
||||||
SetupCloseHandler()
|
SetupCloseHandler()
|
||||||
|
|
||||||
<-stopCh
|
<-stopCh
|
||||||
|
integratedPeerApproval.Stop()
|
||||||
|
if geo != nil {
|
||||||
|
_ = geo.Stop()
|
||||||
|
}
|
||||||
ephemeralManager.Stop()
|
ephemeralManager.Stop()
|
||||||
_ = appMetrics.Close()
|
_ = appMetrics.Close()
|
||||||
_ = listener.Close()
|
_ = listener.Close()
|
||||||
|
|||||||
@@ -92,6 +92,14 @@ message PeerKeys {
|
|||||||
bytes wgPubKey = 2;
|
bytes wgPubKey = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Environment is part of the PeerSystemMeta and describes the environment the agent is running in.
|
||||||
|
message Environment {
|
||||||
|
// cloud is the cloud provider the agent is running in if applicable.
|
||||||
|
string cloud = 1;
|
||||||
|
// platform is the platform the agent is running on if applicable.
|
||||||
|
string platform = 2;
|
||||||
|
}
|
||||||
|
|
||||||
// PeerSystemMeta is machine meta data like OS and version.
|
// PeerSystemMeta is machine meta data like OS and version.
|
||||||
message PeerSystemMeta {
|
message PeerSystemMeta {
|
||||||
string hostname = 1;
|
string hostname = 1;
|
||||||
@@ -102,6 +110,13 @@ message PeerSystemMeta {
|
|||||||
string OS = 6;
|
string OS = 6;
|
||||||
string wiretrusteeVersion = 7;
|
string wiretrusteeVersion = 7;
|
||||||
string uiVersion = 8;
|
string uiVersion = 8;
|
||||||
|
string kernelVersion = 9;
|
||||||
|
string OSVersion = 10;
|
||||||
|
repeated NetworkAddress networkAddresses = 11;
|
||||||
|
string sysSerialNumber = 12;
|
||||||
|
string sysProductName = 13;
|
||||||
|
string sysManufacturer = 14;
|
||||||
|
Environment environment = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LoginResponse {
|
message LoginResponse {
|
||||||
@@ -351,3 +366,8 @@ message FirewallRule {
|
|||||||
ICMP = 4;
|
ICMP = 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message NetworkAddress {
|
||||||
|
string netIP = 1;
|
||||||
|
string mac = 2;
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,14 +22,16 @@ import (
|
|||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/netbirdio/management-integrations/additions"
|
"github.com/netbirdio/management-integrations/additions"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/base62"
|
"github.com/netbirdio/netbird/base62"
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
"github.com/netbirdio/netbird/management/server/account"
|
"github.com/netbirdio/netbird/management/server/account"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/geolocation"
|
||||||
"github.com/netbirdio/netbird/management/server/idp"
|
"github.com/netbirdio/netbird/management/server/idp"
|
||||||
|
"github.com/netbirdio/netbird/management/server/integrated_approval"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
|
"github.com/netbirdio/netbird/management/server/posture"
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
)
|
)
|
||||||
@@ -70,11 +72,12 @@ type AccountManager interface {
|
|||||||
CheckUserAccessByJWTGroups(claims jwtclaims.AuthorizationClaims) error
|
CheckUserAccessByJWTGroups(claims jwtclaims.AuthorizationClaims) error
|
||||||
GetAccountFromPAT(pat string) (*Account, *User, *PersonalAccessToken, error)
|
GetAccountFromPAT(pat string) (*Account, *User, *PersonalAccessToken, error)
|
||||||
DeleteAccount(accountID, userID string) error
|
DeleteAccount(accountID, userID string) error
|
||||||
|
GetUsage(ctx context.Context, accountID string, start time.Time, end time.Time) (*AccountUsageStats, error)
|
||||||
MarkPATUsed(tokenID string) error
|
MarkPATUsed(tokenID string) error
|
||||||
GetUser(claims jwtclaims.AuthorizationClaims) (*User, error)
|
GetUser(claims jwtclaims.AuthorizationClaims) (*User, error)
|
||||||
ListUsers(accountID string) ([]*User, error)
|
ListUsers(accountID string) ([]*User, error)
|
||||||
GetPeers(accountID, userID string) ([]*nbpeer.Peer, error)
|
GetPeers(accountID, userID string) ([]*nbpeer.Peer, error)
|
||||||
MarkPeerConnected(peerKey string, connected bool) error
|
MarkPeerConnected(peerKey string, connected bool, realIP net.IP) error
|
||||||
DeletePeer(accountID, peerID, userID string) error
|
DeletePeer(accountID, peerID, userID string) error
|
||||||
UpdatePeer(accountID, userID string, peer *nbpeer.Peer) (*nbpeer.Peer, error)
|
UpdatePeer(accountID, userID string, peer *nbpeer.Peer) (*nbpeer.Peer, error)
|
||||||
GetNetworkMap(peerID string) (*NetworkMap, error)
|
GetNetworkMap(peerID string) (*NetworkMap, error)
|
||||||
@@ -108,7 +111,7 @@ type AccountManager interface {
|
|||||||
DeleteNameServerGroup(accountID, nsGroupID, userID string) error
|
DeleteNameServerGroup(accountID, nsGroupID, userID string) error
|
||||||
ListNameServerGroups(accountID string, userID string) ([]*nbdns.NameServerGroup, error)
|
ListNameServerGroups(accountID string, userID string) ([]*nbdns.NameServerGroup, error)
|
||||||
GetDNSDomain() string
|
GetDNSDomain() string
|
||||||
StoreEvent(initiatorID, targetID, accountID string, activityID activity.Activity, meta map[string]any)
|
StoreEvent(initiatorID, targetID, accountID string, activityID activity.ActivityDescriber, meta map[string]any)
|
||||||
GetEvents(accountID, userID string) ([]*activity.Event, error)
|
GetEvents(accountID, userID string) ([]*activity.Event, error)
|
||||||
GetDNSSettings(accountID string, userID string) (*DNSSettings, error)
|
GetDNSSettings(accountID string, userID string) (*DNSSettings, error)
|
||||||
SaveDNSSettings(accountID string, userID string, dnsSettingsToSave *DNSSettings) error
|
SaveDNSSettings(accountID string, userID string, dnsSettingsToSave *DNSSettings) error
|
||||||
@@ -119,6 +122,13 @@ type AccountManager interface {
|
|||||||
GetAllConnectedPeers() (map[string]struct{}, error)
|
GetAllConnectedPeers() (map[string]struct{}, error)
|
||||||
HasConnectedChannel(peerID string) bool
|
HasConnectedChannel(peerID string) bool
|
||||||
GetExternalCacheManager() ExternalCacheManager
|
GetExternalCacheManager() ExternalCacheManager
|
||||||
|
GetPostureChecks(accountID, postureChecksID, userID string) (*posture.Checks, error)
|
||||||
|
SavePostureChecks(accountID, userID string, postureChecks *posture.Checks) error
|
||||||
|
DeletePostureChecks(accountID, postureChecksID, userID string) error
|
||||||
|
ListPostureChecks(accountID, userID string) ([]*posture.Checks, error)
|
||||||
|
GetIdpManager() idp.Manager
|
||||||
|
UpdateIntegratedApprovalGroups(accountID string, userID string, groups []string) error
|
||||||
|
GroupValidation(accountId string, groups []string) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultAccountManager struct {
|
type DefaultAccountManager struct {
|
||||||
@@ -133,6 +143,7 @@ type DefaultAccountManager struct {
|
|||||||
externalCacheManager ExternalCacheManager
|
externalCacheManager ExternalCacheManager
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
eventStore activity.Store
|
eventStore activity.Store
|
||||||
|
geo *geolocation.Geolocation
|
||||||
|
|
||||||
// singleAccountMode indicates whether the instance has a single account.
|
// singleAccountMode indicates whether the instance has a single account.
|
||||||
// If true, then every new user will end up under the same account.
|
// If true, then every new user will end up under the same account.
|
||||||
@@ -146,6 +157,8 @@ type DefaultAccountManager struct {
|
|||||||
|
|
||||||
// userDeleteFromIDPEnabled allows to delete user from IDP when user is deleted from account
|
// userDeleteFromIDPEnabled allows to delete user from IDP when user is deleted from account
|
||||||
userDeleteFromIDPEnabled bool
|
userDeleteFromIDPEnabled bool
|
||||||
|
|
||||||
|
integratedPeerValidator integrated_approval.IntegratedApproval
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings represents Account settings structure that can be modified via API and Dashboard
|
// Settings represents Account settings structure that can be modified via API and Dashboard
|
||||||
@@ -197,6 +210,7 @@ type Account struct {
|
|||||||
|
|
||||||
// User.Id it was created by
|
// User.Id it was created by
|
||||||
CreatedBy string
|
CreatedBy string
|
||||||
|
CreatedAt time.Time
|
||||||
Domain string `gorm:"index"`
|
Domain string `gorm:"index"`
|
||||||
DomainCategory string
|
DomainCategory string
|
||||||
IsDomainPrimaryAccount bool
|
IsDomainPrimaryAccount bool
|
||||||
@@ -209,16 +223,26 @@ type Account struct {
|
|||||||
UsersG []User `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
UsersG []User `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
Groups map[string]*Group `gorm:"-"`
|
Groups map[string]*Group `gorm:"-"`
|
||||||
GroupsG []Group `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
GroupsG []Group `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
Rules map[string]*Rule `gorm:"-"`
|
|
||||||
RulesG []Rule `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
|
||||||
Policies []*Policy `gorm:"foreignKey:AccountID;references:id"`
|
Policies []*Policy `gorm:"foreignKey:AccountID;references:id"`
|
||||||
Routes map[string]*route.Route `gorm:"-"`
|
Routes map[string]*route.Route `gorm:"-"`
|
||||||
RoutesG []route.Route `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
RoutesG []route.Route `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
NameServerGroups map[string]*nbdns.NameServerGroup `gorm:"-"`
|
NameServerGroups map[string]*nbdns.NameServerGroup `gorm:"-"`
|
||||||
NameServerGroupsG []nbdns.NameServerGroup `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
NameServerGroupsG []nbdns.NameServerGroup `json:"-" gorm:"foreignKey:AccountID;references:id"`
|
||||||
DNSSettings DNSSettings `gorm:"embedded;embeddedPrefix:dns_settings_"`
|
DNSSettings DNSSettings `gorm:"embedded;embeddedPrefix:dns_settings_"`
|
||||||
|
PostureChecks []*posture.Checks `gorm:"foreignKey:AccountID;references:id"`
|
||||||
// Settings is a dictionary of Account settings
|
// Settings is a dictionary of Account settings
|
||||||
Settings *Settings `gorm:"embedded;embeddedPrefix:settings_"`
|
Settings *Settings `gorm:"embedded;embeddedPrefix:settings_"`
|
||||||
|
// deprecated on store and api level
|
||||||
|
Rules map[string]*Rule `json:"-" gorm:"-"`
|
||||||
|
RulesG []Rule `json:"-" gorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountUsageStats represents the current usage statistics for an account
|
||||||
|
type AccountUsageStats struct {
|
||||||
|
ActiveUsers int64 `json:"active_users"`
|
||||||
|
TotalUsers int64 `json:"total_users"`
|
||||||
|
ActivePeers int64 `json:"active_peers"`
|
||||||
|
TotalPeers int64 `json:"total_peers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserInfo struct {
|
type UserInfo struct {
|
||||||
@@ -369,12 +393,14 @@ func (a *Account) GetPeerNetworkMap(peerID, dnsDomain string) *NetworkMap {
|
|||||||
Network: a.Network.Copy(),
|
Network: a.Network.Copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
validatedPeers := additions.ValidatePeers([]*nbpeer.Peer{peer})
|
validatedPeers := additions.ValidatePeers([]*nbpeer.Peer{peer})
|
||||||
if len(validatedPeers) == 0 {
|
if len(validatedPeers) == 0 {
|
||||||
return &NetworkMap{
|
return &NetworkMap{
|
||||||
Network: a.Network.Copy(),
|
Network: a.Network.Copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aclPeers, firewallRules := a.getPeerConnectionResources(peerID)
|
aclPeers, firewallRules := a.getPeerConnectionResources(peerID)
|
||||||
// exclude expired peers
|
// exclude expired peers
|
||||||
var peersToConnect []*nbpeer.Peer
|
var peersToConnect []*nbpeer.Peer
|
||||||
@@ -553,6 +579,20 @@ func (a *Account) FindSetupKey(setupKey string) (*SetupKey, error) {
|
|||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPeerGroupsList return with the list of groups ID.
|
||||||
|
func (a *Account) GetPeerGroupsList(peerID string) []string {
|
||||||
|
var grps []string
|
||||||
|
for groupID, group := range a.Groups {
|
||||||
|
for _, id := range group.Peers {
|
||||||
|
if id == peerID {
|
||||||
|
grps = append(grps, groupID)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return grps
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Account) getUserGroups(userID string) ([]string, error) {
|
func (a *Account) getUserGroups(userID string) ([]string, error) {
|
||||||
user, err := a.FindUser(userID)
|
user, err := a.FindUser(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -635,11 +675,6 @@ func (a *Account) Copy() *Account {
|
|||||||
groups[id] = group.Copy()
|
groups[id] = group.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
rules := map[string]*Rule{}
|
|
||||||
for id, rule := range a.Rules {
|
|
||||||
rules[id] = rule.Copy()
|
|
||||||
}
|
|
||||||
|
|
||||||
policies := []*Policy{}
|
policies := []*Policy{}
|
||||||
for _, policy := range a.Policies {
|
for _, policy := range a.Policies {
|
||||||
policies = append(policies, policy.Copy())
|
policies = append(policies, policy.Copy())
|
||||||
@@ -662,9 +697,15 @@ func (a *Account) Copy() *Account {
|
|||||||
settings = a.Settings.Copy()
|
settings = a.Settings.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postureChecks := []*posture.Checks{}
|
||||||
|
for _, postureCheck := range a.PostureChecks {
|
||||||
|
postureChecks = append(postureChecks, postureCheck.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
return &Account{
|
return &Account{
|
||||||
Id: a.Id,
|
Id: a.Id,
|
||||||
CreatedBy: a.CreatedBy,
|
CreatedBy: a.CreatedBy,
|
||||||
|
CreatedAt: a.CreatedAt,
|
||||||
Domain: a.Domain,
|
Domain: a.Domain,
|
||||||
DomainCategory: a.DomainCategory,
|
DomainCategory: a.DomainCategory,
|
||||||
IsDomainPrimaryAccount: a.IsDomainPrimaryAccount,
|
IsDomainPrimaryAccount: a.IsDomainPrimaryAccount,
|
||||||
@@ -673,11 +714,11 @@ func (a *Account) Copy() *Account {
|
|||||||
Peers: peers,
|
Peers: peers,
|
||||||
Users: users,
|
Users: users,
|
||||||
Groups: groups,
|
Groups: groups,
|
||||||
Rules: rules,
|
|
||||||
Policies: policies,
|
Policies: policies,
|
||||||
Routes: routes,
|
Routes: routes,
|
||||||
NameServerGroups: nsGroups,
|
NameServerGroups: nsGroups,
|
||||||
DNSSettings: dnsSettings,
|
DNSSettings: dnsSettings,
|
||||||
|
PostureChecks: postureChecks,
|
||||||
Settings: settings,
|
Settings: settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -804,10 +845,13 @@ func (a *Account) UserGroupsRemoveFromPeers(userID string, groups ...string) {
|
|||||||
|
|
||||||
// BuildManager creates a new DefaultAccountManager with a provided Store
|
// BuildManager creates a new DefaultAccountManager with a provided Store
|
||||||
func BuildManager(store Store, peersUpdateManager *PeersUpdateManager, idpManager idp.Manager,
|
func BuildManager(store Store, peersUpdateManager *PeersUpdateManager, idpManager idp.Manager,
|
||||||
singleAccountModeDomain string, dnsDomain string, eventStore activity.Store, userDeleteFromIDPEnabled bool,
|
singleAccountModeDomain string, dnsDomain string, eventStore activity.Store, geo *geolocation.Geolocation,
|
||||||
|
userDeleteFromIDPEnabled bool,
|
||||||
|
integratedPeerValidator integrated_approval.IntegratedApproval,
|
||||||
) (*DefaultAccountManager, error) {
|
) (*DefaultAccountManager, error) {
|
||||||
am := &DefaultAccountManager{
|
am := &DefaultAccountManager{
|
||||||
Store: store,
|
Store: store,
|
||||||
|
geo: geo,
|
||||||
peersUpdateManager: peersUpdateManager,
|
peersUpdateManager: peersUpdateManager,
|
||||||
idpManager: idpManager,
|
idpManager: idpManager,
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
@@ -817,6 +861,7 @@ func BuildManager(store Store, peersUpdateManager *PeersUpdateManager, idpManage
|
|||||||
eventStore: eventStore,
|
eventStore: eventStore,
|
||||||
peerLoginExpiry: NewDefaultScheduler(),
|
peerLoginExpiry: NewDefaultScheduler(),
|
||||||
userDeleteFromIDPEnabled: userDeleteFromIDPEnabled,
|
userDeleteFromIDPEnabled: userDeleteFromIDPEnabled,
|
||||||
|
integratedPeerValidator: integratedPeerValidator,
|
||||||
}
|
}
|
||||||
allAccounts := store.GetAllAccounts()
|
allAccounts := store.GetAllAccounts()
|
||||||
// enable single account mode only if configured by user and number of existing accounts is not grater than 1
|
// enable single account mode only if configured by user and number of existing accounts is not grater than 1
|
||||||
@@ -880,6 +925,10 @@ func (am *DefaultAccountManager) GetExternalCacheManager() ExternalCacheManager
|
|||||||
return am.externalCacheManager
|
return am.externalCacheManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (am *DefaultAccountManager) GetIdpManager() idp.Manager {
|
||||||
|
return am.idpManager
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateAccountSettings updates Account settings.
|
// UpdateAccountSettings updates Account settings.
|
||||||
// Only users with role UserRoleAdmin can update the account.
|
// Only users with role UserRoleAdmin can update the account.
|
||||||
// User that performs the update has to belong to the account.
|
// User that performs the update has to belong to the account.
|
||||||
@@ -897,12 +946,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(accountID, userID string,
|
|||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
account, err := am.Store.GetAccountByUser(userID)
|
account, err := am.Store.GetAccount(accountID)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = additions.ValidateExtraSettings(newSettings.Extra, account.Settings.Extra, account.Peers, userID, accountID, am.eventStore)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -916,6 +960,11 @@ func (am *DefaultAccountManager) UpdateAccountSettings(accountID, userID string,
|
|||||||
return nil, status.Errorf(status.PermissionDenied, "user is not allowed to update account")
|
return nil, status.Errorf(status.PermissionDenied, "user is not allowed to update account")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = additions.ValidateExtraSettings(newSettings.Extra, account.Settings.Extra, account.Peers, userID, accountID, am.eventStore)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
oldSettings := account.Settings
|
oldSettings := account.Settings
|
||||||
if oldSettings.PeerLoginExpirationEnabled != newSettings.PeerLoginExpirationEnabled {
|
if oldSettings.PeerLoginExpirationEnabled != newSettings.PeerLoginExpirationEnabled {
|
||||||
event := activity.AccountPeerLoginExpirationEnabled
|
event := activity.AccountPeerLoginExpirationEnabled
|
||||||
@@ -1094,8 +1143,20 @@ func (am *DefaultAccountManager) DeleteAccount(accountID, userID string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUsage returns the usage stats for the given account.
|
||||||
|
// This cannot be used to calculate usage stats for a period in the past as it relies on peers' last seen time.
|
||||||
|
func (am *DefaultAccountManager) GetUsage(ctx context.Context, accountID string, start time.Time, end time.Time) (*AccountUsageStats, error) {
|
||||||
|
usageStats, err := am.Store.CalculateUsageStats(ctx, accountID, start, end)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to calculate usage stats: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return usageStats, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetAccountByUserOrAccountID looks for an account by user or accountID, if no account is provided and
|
// GetAccountByUserOrAccountID looks for an account by user or accountID, if no account is provided and
|
||||||
// userID doesn't have an account associated with it, one account is created
|
// userID doesn't have an account associated with it, one account is created
|
||||||
|
// domain is used to create a new account if no account is found
|
||||||
func (am *DefaultAccountManager) GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error) {
|
func (am *DefaultAccountManager) GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error) {
|
||||||
if accountID != "" {
|
if accountID != "" {
|
||||||
return am.Store.GetAccount(accountID)
|
return am.Store.GetAccount(accountID)
|
||||||
@@ -1780,7 +1841,7 @@ func (am *DefaultAccountManager) CheckUserAccessByJWTGroups(claims jwtclaims.Aut
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// addAllGroup to account object if it doesn't exists
|
// addAllGroup to account object if it doesn't exist
|
||||||
func addAllGroup(account *Account) error {
|
func addAllGroup(account *Account) error {
|
||||||
if len(account.Groups) == 0 {
|
if len(account.Groups) == 0 {
|
||||||
allGroup := &Group{
|
allGroup := &Group{
|
||||||
@@ -1793,21 +1854,28 @@ func addAllGroup(account *Account) error {
|
|||||||
}
|
}
|
||||||
account.Groups = map[string]*Group{allGroup.ID: allGroup}
|
account.Groups = map[string]*Group{allGroup.ID: allGroup}
|
||||||
|
|
||||||
defaultRule := &Rule{
|
id := xid.New().String()
|
||||||
ID: xid.New().String(),
|
|
||||||
|
defaultPolicy := &Policy{
|
||||||
|
ID: id,
|
||||||
Name: DefaultRuleName,
|
Name: DefaultRuleName,
|
||||||
Description: DefaultRuleDescription,
|
Description: DefaultRuleDescription,
|
||||||
Disabled: false,
|
Enabled: true,
|
||||||
Source: []string{allGroup.ID},
|
Rules: []*PolicyRule{
|
||||||
Destination: []string{allGroup.ID},
|
{
|
||||||
|
ID: id,
|
||||||
|
Name: DefaultRuleName,
|
||||||
|
Description: DefaultRuleDescription,
|
||||||
|
Enabled: true,
|
||||||
|
Sources: []string{allGroup.ID},
|
||||||
|
Destinations: []string{allGroup.ID},
|
||||||
|
Bidirectional: true,
|
||||||
|
Protocol: PolicyRuleProtocolALL,
|
||||||
|
Action: PolicyTrafficActionAccept,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
account.Rules = map[string]*Rule{defaultRule.ID: defaultRule}
|
|
||||||
|
|
||||||
// TODO: after migration we need to drop rule and create policy directly
|
|
||||||
defaultPolicy, err := RuleToPolicy(defaultRule)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("convert rule to policy: %w", err)
|
|
||||||
}
|
|
||||||
account.Policies = []*Policy{defaultPolicy}
|
account.Policies = []*Policy{defaultPolicy}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -1831,6 +1899,7 @@ func newAccountWithId(accountID, userID, domain string) *Account {
|
|||||||
|
|
||||||
acc := &Account{
|
acc := &Account{
|
||||||
Id: accountID,
|
Id: accountID,
|
||||||
|
CreatedAt: time.Now().UTC(),
|
||||||
SetupKeys: setupKeys,
|
SetupKeys: setupKeys,
|
||||||
Network: network,
|
Network: network,
|
||||||
Peers: peers,
|
Peers: peers,
|
||||||
|
|||||||
@@ -3,11 +3,17 @@ package account
|
|||||||
type ExtraSettings struct {
|
type ExtraSettings struct {
|
||||||
// PeerApprovalEnabled enables or disables the need for peers bo be approved by an administrator
|
// PeerApprovalEnabled enables or disables the need for peers bo be approved by an administrator
|
||||||
PeerApprovalEnabled bool
|
PeerApprovalEnabled bool
|
||||||
|
|
||||||
|
// IntegratedApprovalGroups list of group IDs to be used with integrated approval configurations
|
||||||
|
IntegratedApprovalGroups []string `gorm:"serializer:json"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy copies the ExtraSettings struct
|
// Copy copies the ExtraSettings struct
|
||||||
func (e *ExtraSettings) Copy() *ExtraSettings {
|
func (e *ExtraSettings) Copy() *ExtraSettings {
|
||||||
|
var cpGroup []string
|
||||||
|
|
||||||
return &ExtraSettings{
|
return &ExtraSettings{
|
||||||
PeerApprovalEnabled: e.PeerApprovalEnabled,
|
PeerApprovalEnabled: e.PeerApprovalEnabled,
|
||||||
|
IntegratedApprovalGroups: append(cpGroup, e.IntegratedApprovalGroups...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,19 +12,34 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
|
||||||
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
|
||||||
"github.com/netbirdio/netbird/route"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
|
"github.com/netbirdio/netbird/management/server/account"
|
||||||
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
||||||
|
"github.com/netbirdio/netbird/management/server/posture"
|
||||||
|
"github.com/netbirdio/netbird/route"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type MocIntegratedApproval struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (MocIntegratedApproval) PreparePeer(accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) *nbpeer.Peer {
|
||||||
|
return peer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (MocIntegratedApproval) IsRequiresApproval(accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (MocIntegratedApproval) Stop() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func verifyCanAddPeerToAccount(t *testing.T, manager AccountManager, account *Account, userID string) {
|
func verifyCanAddPeerToAccount(t *testing.T, manager AccountManager, account *Account, userID string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
peer := &nbpeer.Peer{
|
peer := &nbpeer.Peer{
|
||||||
@@ -93,19 +108,13 @@ func verifyNewAccountHasDefaultFields(t *testing.T, account *Account, createdBy
|
|||||||
t.Errorf("expecting newly created account to be created by user %s, got %s", createdBy, account.CreatedBy)
|
t.Errorf("expecting newly created account to be created by user %s, got %s", createdBy, account.CreatedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if account.CreatedAt.IsZero() {
|
||||||
|
t.Errorf("expecting newly created account to have a non-zero creation time")
|
||||||
|
}
|
||||||
|
|
||||||
if account.Domain != domain {
|
if account.Domain != domain {
|
||||||
t.Errorf("expecting newly created account to have domain %s, got %s", domain, account.Domain)
|
t.Errorf("expecting newly created account to have domain %s, got %s", domain, account.Domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(account.Rules) != 1 {
|
|
||||||
t.Errorf("expecting newly created account to have 1 rule, got %d", len(account.Rules))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rule := range account.Rules {
|
|
||||||
if rule.Name != "Default" {
|
|
||||||
t.Errorf("expecting newly created account to have Default rule, got %s", rule.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccount_GetPeerNetworkMap(t *testing.T) {
|
func TestAccount_GetPeerNetworkMap(t *testing.T) {
|
||||||
@@ -1482,6 +1491,7 @@ func TestAccount_Copy(t *testing.T) {
|
|||||||
account := &Account{
|
account := &Account{
|
||||||
Id: "account1",
|
Id: "account1",
|
||||||
CreatedBy: "tester",
|
CreatedBy: "tester",
|
||||||
|
CreatedAt: time.Now().UTC(),
|
||||||
Domain: "test.com",
|
Domain: "test.com",
|
||||||
DomainCategory: "public",
|
DomainCategory: "public",
|
||||||
IsDomainPrimaryAccount: true,
|
IsDomainPrimaryAccount: true,
|
||||||
@@ -1528,18 +1538,12 @@ func TestAccount_Copy(t *testing.T) {
|
|||||||
Peers: []string{"peer1"},
|
Peers: []string{"peer1"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Rules: map[string]*Rule{
|
|
||||||
"rule1": {
|
|
||||||
ID: "rule1",
|
|
||||||
Destination: []string{},
|
|
||||||
Source: []string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Policies: []*Policy{
|
Policies: []*Policy{
|
||||||
{
|
{
|
||||||
ID: "policy1",
|
ID: "policy1",
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Rules: make([]*PolicyRule, 0),
|
Rules: make([]*PolicyRule, 0),
|
||||||
|
SourcePostureChecks: make([]string, 0),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Routes: map[string]*route.Route{
|
Routes: map[string]*route.Route{
|
||||||
@@ -1558,7 +1562,12 @@ func TestAccount_Copy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
DNSSettings: DNSSettings{DisabledManagementGroups: []string{}},
|
DNSSettings: DNSSettings{DisabledManagementGroups: []string{}},
|
||||||
Settings: &Settings{},
|
PostureChecks: []*posture.Checks{
|
||||||
|
{
|
||||||
|
ID: "posture Checks1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Settings: &Settings{},
|
||||||
}
|
}
|
||||||
err := hasNilField(account)
|
err := hasNilField(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1630,7 +1639,7 @@ func TestDefaultAccountManager_UpdatePeer_PeerLoginExpiration(t *testing.T) {
|
|||||||
LoginExpirationEnabled: true,
|
LoginExpirationEnabled: true,
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "unable to add peer")
|
require.NoError(t, err, "unable to add peer")
|
||||||
err = manager.MarkPeerConnected(key.PublicKey().String(), true)
|
err = manager.MarkPeerConnected(key.PublicKey().String(), true, nil)
|
||||||
require.NoError(t, err, "unable to mark peer connected")
|
require.NoError(t, err, "unable to mark peer connected")
|
||||||
account, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
account, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
||||||
PeerLoginExpiration: time.Hour,
|
PeerLoginExpiration: time.Hour,
|
||||||
@@ -1697,7 +1706,7 @@ func TestDefaultAccountManager_MarkPeerConnected_PeerLoginExpiration(t *testing.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when we mark peer as connected, the peer login expiration routine should trigger
|
// when we mark peer as connected, the peer login expiration routine should trigger
|
||||||
err = manager.MarkPeerConnected(key.PublicKey().String(), true)
|
err = manager.MarkPeerConnected(key.PublicKey().String(), true, nil)
|
||||||
require.NoError(t, err, "unable to mark peer connected")
|
require.NoError(t, err, "unable to mark peer connected")
|
||||||
|
|
||||||
failed := waitTimeout(wg, time.Second)
|
failed := waitTimeout(wg, time.Second)
|
||||||
@@ -1720,7 +1729,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_PeerLoginExpiration(t *test
|
|||||||
LoginExpirationEnabled: true,
|
LoginExpirationEnabled: true,
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "unable to add peer")
|
require.NoError(t, err, "unable to add peer")
|
||||||
err = manager.MarkPeerConnected(key.PublicKey().String(), true)
|
err = manager.MarkPeerConnected(key.PublicKey().String(), true, nil)
|
||||||
require.NoError(t, err, "unable to mark peer connected")
|
require.NoError(t, err, "unable to mark peer connected")
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
@@ -2228,7 +2237,7 @@ func createManager(t *testing.T) (*DefaultAccountManager, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
eventStore := &activity.InMemoryEventStore{}
|
eventStore := &activity.InMemoryEventStore{}
|
||||||
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, false)
|
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedApproval{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createStore(t *testing.T) (Store, error) {
|
func createStore(t *testing.T) (Store, error) {
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
package activity
|
package activity
|
||||||
|
|
||||||
|
import "maps"
|
||||||
|
|
||||||
// Activity that triggered an Event
|
// Activity that triggered an Event
|
||||||
type Activity int
|
type Activity int
|
||||||
|
|
||||||
// Code is an activity string representation
|
// Code is an activity string representation
|
||||||
type Code struct {
|
type Code struct {
|
||||||
message string
|
Message string
|
||||||
code string
|
Code string
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -130,6 +132,12 @@ const (
|
|||||||
PeerApprovalRevoked
|
PeerApprovalRevoked
|
||||||
// TransferredOwnerRole indicates that the user transferred the owner role of the account
|
// TransferredOwnerRole indicates that the user transferred the owner role of the account
|
||||||
TransferredOwnerRole
|
TransferredOwnerRole
|
||||||
|
// PostureCheckCreated indicates that the user created a posture check
|
||||||
|
PostureCheckCreated
|
||||||
|
// PostureCheckUpdated indicates that the user updated a posture check
|
||||||
|
PostureCheckUpdated
|
||||||
|
// PostureCheckDeleted indicates that the user deleted a posture check
|
||||||
|
PostureCheckDeleted
|
||||||
)
|
)
|
||||||
|
|
||||||
var activityMap = map[Activity]Code{
|
var activityMap = map[Activity]Code{
|
||||||
@@ -193,12 +201,15 @@ var activityMap = map[Activity]Code{
|
|||||||
PeerApproved: {"Peer approved", "peer.approve"},
|
PeerApproved: {"Peer approved", "peer.approve"},
|
||||||
PeerApprovalRevoked: {"Peer approval revoked", "peer.approval.revoke"},
|
PeerApprovalRevoked: {"Peer approval revoked", "peer.approval.revoke"},
|
||||||
TransferredOwnerRole: {"Transferred owner role", "transferred.owner.role"},
|
TransferredOwnerRole: {"Transferred owner role", "transferred.owner.role"},
|
||||||
|
PostureCheckCreated: {"Posture check created", "posture.check.created"},
|
||||||
|
PostureCheckUpdated: {"Posture check updated", "posture.check.updated"},
|
||||||
|
PostureCheckDeleted: {"Posture check deleted", "posture.check.deleted"},
|
||||||
}
|
}
|
||||||
|
|
||||||
// StringCode returns a string code of the activity
|
// StringCode returns a string code of the activity
|
||||||
func (a Activity) StringCode() string {
|
func (a Activity) StringCode() string {
|
||||||
if code, ok := activityMap[a]; ok {
|
if code, ok := activityMap[a]; ok {
|
||||||
return code.code
|
return code.Code
|
||||||
}
|
}
|
||||||
return "UNKNOWN_ACTIVITY"
|
return "UNKNOWN_ACTIVITY"
|
||||||
}
|
}
|
||||||
@@ -206,7 +217,12 @@ func (a Activity) StringCode() string {
|
|||||||
// Message returns a string representation of an activity
|
// Message returns a string representation of an activity
|
||||||
func (a Activity) Message() string {
|
func (a Activity) Message() string {
|
||||||
if code, ok := activityMap[a]; ok {
|
if code, ok := activityMap[a]; ok {
|
||||||
return code.message
|
return code.Message
|
||||||
}
|
}
|
||||||
return "UNKNOWN_ACTIVITY"
|
return "UNKNOWN_ACTIVITY"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterActivityMap adds new codes to the activity map
|
||||||
|
func RegisterActivityMap(codes map[Activity]Code) {
|
||||||
|
maps.Copy(activityMap, codes)
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,12 +8,18 @@ const (
|
|||||||
SystemInitiator = "sys"
|
SystemInitiator = "sys"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ActivityDescriber is an interface that describes an activity
|
||||||
|
type ActivityDescriber interface { //nolint:revive
|
||||||
|
StringCode() string
|
||||||
|
Message() string
|
||||||
|
}
|
||||||
|
|
||||||
// Event represents a network/system activity event.
|
// Event represents a network/system activity event.
|
||||||
type Event struct {
|
type Event struct {
|
||||||
// Timestamp of the event
|
// Timestamp of the event
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
// Activity that was performed during the event
|
// Activity that was performed during the event
|
||||||
Activity Activity
|
Activity ActivityDescriber
|
||||||
// ID of the event (can be empty, meaning that it wasn't yet generated)
|
// ID of the event (can be empty, meaning that it wasn't yet generated)
|
||||||
ID uint64
|
ID uint64
|
||||||
// InitiatorID is the ID of an object that initiated the event (e.g., a user)
|
// InitiatorID is the ID of an object that initiated the event (e.g., a user)
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
eventStore := &activity.InMemoryEventStore{}
|
eventStore := &activity.InMemoryEventStore{}
|
||||||
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, false)
|
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedApproval{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDNSStore(t *testing.T) (Store, error) {
|
func createDNSStore(t *testing.T) (Store, error) {
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ func (am *DefaultAccountManager) GetEvents(accountID, userID string) ([]*activit
|
|||||||
return filtered, nil
|
return filtered, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *DefaultAccountManager) StoreEvent(initiatorID, targetID, accountID string, activityID activity.Activity,
|
func (am *DefaultAccountManager) StoreEvent(initiatorID, targetID, accountID string, activityID activity.ActivityDescriber, meta map[string]any) {
|
||||||
meta map[string]any) {
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
_, err := am.eventStore.Save(&activity.Event{
|
_, err := am.eventStore.Save(&activity.Event{
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -159,18 +161,6 @@ func restore(file string) (*FileStore, error) {
|
|||||||
if account.Policies == nil {
|
if account.Policies == nil {
|
||||||
account.Policies = make([]*Policy, 0)
|
account.Policies = make([]*Policy, 0)
|
||||||
}
|
}
|
||||||
for _, rule := range account.Rules {
|
|
||||||
policy, err := RuleToPolicy(rule)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("unable to migrate rule to policy: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// don't update policies from rules, rules deprecated,
|
|
||||||
// only append not existed rules as part of the migration process
|
|
||||||
if _, ok := policies[policy.ID]; !ok {
|
|
||||||
account.Policies = append(account.Policies, policy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for data migration. Can be removed once most base will be with labels
|
// for data migration. Can be removed once most base will be with labels
|
||||||
existingLabels := account.getPeerDNSLabels()
|
existingLabels := account.getPeerDNSLabels()
|
||||||
@@ -342,13 +332,6 @@ func (s *FileStore) SaveAccount(account *Account) error {
|
|||||||
s.PrivateDomain2AccountID[accountCopy.Domain] = accountCopy.Id
|
s.PrivateDomain2AccountID[accountCopy.Domain] = accountCopy.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
accountCopy.Rules = make(map[string]*Rule)
|
|
||||||
for _, policy := range accountCopy.Policies {
|
|
||||||
for _, rule := range policy.Rules {
|
|
||||||
accountCopy.Rules[rule.ID] = rule.ToRule()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.persist(s.storeFile)
|
return s.persist(s.storeFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,6 +609,27 @@ func (s *FileStore) SavePeerStatus(accountID, peerID string, peerStatus nbpeer.P
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SavePeerLocation stores the PeerStatus in memory. It doesn't attempt to persist data to speed up things.
|
||||||
|
// Peer.Location will be saved eventually when some other changes occur.
|
||||||
|
func (s *FileStore) SavePeerLocation(accountID string, peerWithLocation *nbpeer.Peer) error {
|
||||||
|
s.mux.Lock()
|
||||||
|
defer s.mux.Unlock()
|
||||||
|
|
||||||
|
account, err := s.getAccount(accountID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peer := account.Peers[peerWithLocation.ID]
|
||||||
|
if peer == nil {
|
||||||
|
return status.Errorf(status.NotFound, "peer %s not found", peerWithLocation.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
peer.Location = peerWithLocation.Location
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SaveUserLastLogin stores the last login time for a user in memory. It doesn't attempt to persist data to speed up things.
|
// SaveUserLastLogin stores the last login time for a user in memory. It doesn't attempt to persist data to speed up things.
|
||||||
func (s *FileStore) SaveUserLastLogin(accountID, userID string, lastLogin time.Time) error {
|
func (s *FileStore) SaveUserLastLogin(accountID, userID string, lastLogin time.Time) error {
|
||||||
s.mux.Lock()
|
s.mux.Lock()
|
||||||
@@ -660,3 +664,40 @@ func (s *FileStore) Close() error {
|
|||||||
func (s *FileStore) GetStoreEngine() StoreEngine {
|
func (s *FileStore) GetStoreEngine() StoreEngine {
|
||||||
return FileStoreEngine
|
return FileStoreEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CalculateUsageStats returns the usage stats for an account
|
||||||
|
// start and end are inclusive.
|
||||||
|
func (s *FileStore) CalculateUsageStats(_ context.Context, accountID string, start time.Time, end time.Time) (*AccountUsageStats, error) {
|
||||||
|
s.mux.Lock()
|
||||||
|
defer s.mux.Unlock()
|
||||||
|
|
||||||
|
account, exists := s.Accounts[accountID]
|
||||||
|
if !exists {
|
||||||
|
return nil, fmt.Errorf("account not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := &AccountUsageStats{
|
||||||
|
TotalUsers: 0,
|
||||||
|
TotalPeers: int64(len(account.Peers)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range account.Users {
|
||||||
|
if !user.IsServiceUser {
|
||||||
|
stats.TotalUsers++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeUsers := make(map[string]bool)
|
||||||
|
for _, peer := range account.Peers {
|
||||||
|
lastSeen := peer.Status.LastSeen
|
||||||
|
if lastSeen.Compare(start) >= 0 && lastSeen.Compare(end) <= 0 {
|
||||||
|
if _, exists := account.Users[peer.UserID]; exists && !activeUsers[peer.UserID] {
|
||||||
|
activeUsers[peer.UserID] = true
|
||||||
|
stats.ActiveUsers++
|
||||||
|
}
|
||||||
|
stats.ActivePeers++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -193,18 +194,18 @@ func TestStore(t *testing.T) {
|
|||||||
Name: "all",
|
Name: "all",
|
||||||
Peers: []string{"testpeer"},
|
Peers: []string{"testpeer"},
|
||||||
}
|
}
|
||||||
account.Rules["all"] = &Rule{
|
|
||||||
ID: "all",
|
|
||||||
Name: "all",
|
|
||||||
Source: []string{"all"},
|
|
||||||
Destination: []string{"all"},
|
|
||||||
Flow: TrafficFlowBidirect,
|
|
||||||
}
|
|
||||||
account.Policies = append(account.Policies, &Policy{
|
account.Policies = append(account.Policies, &Policy{
|
||||||
ID: "all",
|
ID: "all",
|
||||||
Name: "all",
|
Name: "all",
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Rules: []*PolicyRule{account.Rules["all"].ToPolicyRule()},
|
Rules: []*PolicyRule{
|
||||||
|
{
|
||||||
|
ID: "all",
|
||||||
|
Name: "all",
|
||||||
|
Sources: []string{"all"},
|
||||||
|
Destinations: []string{"all"},
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
account.Policies = append(account.Policies, &Policy{
|
account.Policies = append(account.Policies, &Policy{
|
||||||
ID: "dmz",
|
ID: "dmz",
|
||||||
@@ -317,41 +318,6 @@ func TestRestore(t *testing.T) {
|
|||||||
require.Len(t, store.TokenID2UserID, 1, "failed to restore a FileStore wrong TokenID2UserID mapping length")
|
require.Len(t, store.TokenID2UserID, 1, "failed to restore a FileStore wrong TokenID2UserID mapping length")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: outdated, delete this
|
|
||||||
func TestRestorePolicies_Migration(t *testing.T) {
|
|
||||||
storeDir := t.TempDir()
|
|
||||||
|
|
||||||
err := util.CopyFileContents("testdata/store_policy_migrate.json", filepath.Join(storeDir, "store.json"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
store, err := NewFileStore(storeDir, nil)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
account := store.Accounts["bf1c8084-ba50-4ce7-9439-34653001fc3b"]
|
|
||||||
require.Len(t, account.Groups, 1, "failed to restore a FileStore file - missing Account Groups")
|
|
||||||
require.Len(t, account.Rules, 1, "failed to restore a FileStore file - missing Account Rules")
|
|
||||||
require.Len(t, account.Policies, 1, "failed to restore a FileStore file - missing Account Policies")
|
|
||||||
|
|
||||||
policy := account.Policies[0]
|
|
||||||
require.Equal(t, policy.Name, "Default", "failed to restore a FileStore file - missing Account Policies Name")
|
|
||||||
require.Equal(t, policy.Description,
|
|
||||||
"This is a default rule that allows connections between all the resources",
|
|
||||||
"failed to restore a FileStore file - missing Account Policies Description")
|
|
||||||
require.NoError(t, err, "failed to upldate query")
|
|
||||||
require.Len(t, policy.Rules, 1, "failed to restore a FileStore file - missing Account Policy Rules")
|
|
||||||
require.Equal(t, policy.Rules[0].Action, PolicyTrafficActionAccept, "failed to restore a FileStore file - missing Account Policies Action")
|
|
||||||
require.Equal(t, policy.Rules[0].Destinations,
|
|
||||||
[]string{"cfefqs706sqkneg59g3g"},
|
|
||||||
"failed to restore a FileStore file - missing Account Policies Destinations")
|
|
||||||
require.Equal(t, policy.Rules[0].Sources,
|
|
||||||
[]string{"cfefqs706sqkneg59g3g"},
|
|
||||||
"failed to restore a FileStore file - missing Account Policies Sources")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRestoreGroups_Migration(t *testing.T) {
|
func TestRestoreGroups_Migration(t *testing.T) {
|
||||||
storeDir := t.TempDir()
|
storeDir := t.TempDir()
|
||||||
|
|
||||||
@@ -634,6 +600,55 @@ func TestFileStore_SavePeerStatus(t *testing.T) {
|
|||||||
assert.Equal(t, newStatus, *actual)
|
assert.Equal(t, newStatus, *actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFileStore_SavePeerLocation(t *testing.T) {
|
||||||
|
storeDir := t.TempDir()
|
||||||
|
|
||||||
|
err := util.CopyFileContents("testdata/store.json", filepath.Join(storeDir, "store.json"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
store, err := NewFileStore(storeDir, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
account, err := store.GetAccount("bf1c8084-ba50-4ce7-9439-34653001fc3b")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
peer := &nbpeer.Peer{
|
||||||
|
AccountID: account.Id,
|
||||||
|
ID: "testpeer",
|
||||||
|
Location: nbpeer.Location{
|
||||||
|
ConnectionIP: net.ParseIP("10.0.0.0"),
|
||||||
|
CountryCode: "YY",
|
||||||
|
CityName: "City",
|
||||||
|
GeoNameID: 1,
|
||||||
|
},
|
||||||
|
Meta: nbpeer.PeerSystemMeta{},
|
||||||
|
}
|
||||||
|
// error is expected as peer is not in store yet
|
||||||
|
err = store.SavePeerLocation(account.Id, peer)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
account.Peers[peer.ID] = peer
|
||||||
|
err = store.SaveAccount(account)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
peer.Location.ConnectionIP = net.ParseIP("35.1.1.1")
|
||||||
|
peer.Location.CountryCode = "DE"
|
||||||
|
peer.Location.CityName = "Berlin"
|
||||||
|
peer.Location.GeoNameID = 2950159
|
||||||
|
|
||||||
|
err = store.SavePeerLocation(account.Id, account.Peers[peer.ID])
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
account, err = store.GetAccount(account.Id)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
actual := account.Peers[peer.ID].Location
|
||||||
|
assert.Equal(t, peer.Location, actual)
|
||||||
|
}
|
||||||
|
|
||||||
func newStore(t *testing.T) *FileStore {
|
func newStore(t *testing.T) *FileStore {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
store, err := NewFileStore(t.TempDir(), nil)
|
store, err := NewFileStore(t.TempDir(), nil)
|
||||||
@@ -643,3 +658,32 @@ func newStore(t *testing.T) *FileStore {
|
|||||||
|
|
||||||
return store
|
return store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFileStore_CalculateUsageStats(t *testing.T) {
|
||||||
|
storeDir := t.TempDir()
|
||||||
|
|
||||||
|
err := util.CopyFileContents("testdata/store_stats.json", filepath.Join(storeDir, "store.json"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
store, err := NewFileStore(storeDir, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
startDate := time.Date(2024, time.February, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
endDate := startDate.AddDate(0, 1, 0).Add(-time.Nanosecond)
|
||||||
|
|
||||||
|
stats1, err := store.CalculateUsageStats(context.TODO(), "account-1", startDate, endDate)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(2), stats1.ActiveUsers)
|
||||||
|
assert.Equal(t, int64(4), stats1.TotalUsers)
|
||||||
|
assert.Equal(t, int64(3), stats1.ActivePeers)
|
||||||
|
assert.Equal(t, int64(7), stats1.TotalPeers)
|
||||||
|
|
||||||
|
stats2, err := store.CalculateUsageStats(context.TODO(), "account-2", startDate, endDate)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, int64(1), stats2.ActiveUsers)
|
||||||
|
assert.Equal(t, int64(2), stats2.TotalUsers)
|
||||||
|
assert.Equal(t, int64(1), stats2.ActivePeers)
|
||||||
|
assert.Equal(t, int64(2), stats2.TotalPeers)
|
||||||
|
}
|
||||||
|
|||||||
210
management/server/geolocation/database.go
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
package geolocation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/csv"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
geoLiteCityTarGZURL = "https://pkgs.netbird.io/geolocation-dbs/GeoLite2-City/download?suffix=tar.gz"
|
||||||
|
geoLiteCityZipURL = "https://pkgs.netbird.io/geolocation-dbs/GeoLite2-City-CSV/download?suffix=zip"
|
||||||
|
geoLiteCitySha256TarURL = "https://pkgs.netbird.io/geolocation-dbs/GeoLite2-City/download?suffix=tar.gz.sha256"
|
||||||
|
geoLiteCitySha256ZipURL = "https://pkgs.netbird.io/geolocation-dbs/GeoLite2-City-CSV/download?suffix=zip.sha256"
|
||||||
|
)
|
||||||
|
|
||||||
|
// loadGeolocationDatabases loads the MaxMind databases.
|
||||||
|
func loadGeolocationDatabases(dataDir string) error {
|
||||||
|
files := []string{MMDBFileName, GeoSqliteDBFile}
|
||||||
|
for _, file := range files {
|
||||||
|
exists, _ := fileExists(path.Join(dataDir, file))
|
||||||
|
if exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch file {
|
||||||
|
case MMDBFileName:
|
||||||
|
extractFunc := func(src string, dst string) error {
|
||||||
|
if err := decompressTarGzFile(src, dst); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return copyFile(path.Join(dst, MMDBFileName), path.Join(dataDir, MMDBFileName))
|
||||||
|
}
|
||||||
|
if err := loadDatabase(
|
||||||
|
geoLiteCitySha256TarURL,
|
||||||
|
geoLiteCityTarGZURL,
|
||||||
|
extractFunc,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case GeoSqliteDBFile:
|
||||||
|
extractFunc := func(src string, dst string) error {
|
||||||
|
if err := decompressZipFile(src, dst); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
extractedCsvFile := path.Join(dst, "GeoLite2-City-Locations-en.csv")
|
||||||
|
return importCsvToSqlite(dataDir, extractedCsvFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := loadDatabase(
|
||||||
|
geoLiteCitySha256ZipURL,
|
||||||
|
geoLiteCityZipURL,
|
||||||
|
extractFunc,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadDatabase downloads a file from the specified URL and verifies its checksum.
|
||||||
|
// It then calls the extract function to perform additional processing on the extracted files.
|
||||||
|
func loadDatabase(checksumURL string, fileURL string, extractFunc func(src string, dst string) error) error {
|
||||||
|
temp, err := os.MkdirTemp(os.TempDir(), "geolite")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(temp)
|
||||||
|
|
||||||
|
checksumFile := path.Join(temp, getDatabaseFileName(checksumURL))
|
||||||
|
err = downloadFile(checksumURL, checksumFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256sum, err := loadChecksumFromFile(checksumFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
dbFile := path.Join(temp, getDatabaseFileName(fileURL))
|
||||||
|
err = downloadFile(fileURL, dbFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := verifyChecksum(dbFile, sha256sum); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return extractFunc(dbFile, temp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// importCsvToSqlite imports a CSV file into a SQLite database.
|
||||||
|
func importCsvToSqlite(dataDir string, csvFile string) error {
|
||||||
|
geonames, err := loadGeonamesCsv(csvFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := gorm.Open(sqlite.Open(path.Join(dataDir, GeoSqliteDBFile)), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Silent),
|
||||||
|
CreateBatchSize: 1000,
|
||||||
|
PrepareStmt: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
sql, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sql.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := db.AutoMigrate(&GeoNames{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.Create(geonames).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadGeonamesCsv(filepath string) ([]GeoNames, error) {
|
||||||
|
f, err := os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
reader := csv.NewReader(f)
|
||||||
|
records, err := reader.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var geoNames []GeoNames
|
||||||
|
for index, record := range records {
|
||||||
|
if index == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
geoNameID, err := strconv.Atoi(record[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
geoName := GeoNames{
|
||||||
|
GeoNameID: geoNameID,
|
||||||
|
LocaleCode: record[1],
|
||||||
|
ContinentCode: record[2],
|
||||||
|
ContinentName: record[3],
|
||||||
|
CountryIsoCode: record[4],
|
||||||
|
CountryName: record[5],
|
||||||
|
Subdivision1IsoCode: record[6],
|
||||||
|
Subdivision1Name: record[7],
|
||||||
|
Subdivision2IsoCode: record[8],
|
||||||
|
Subdivision2Name: record[9],
|
||||||
|
CityName: record[10],
|
||||||
|
MetroCode: record[11],
|
||||||
|
TimeZone: record[12],
|
||||||
|
IsInEuropeanUnion: record[13],
|
||||||
|
}
|
||||||
|
geoNames = append(geoNames, geoName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return geoNames, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDatabaseFileName extracts the file name from a given URL string.
|
||||||
|
func getDatabaseFileName(urlStr string) string {
|
||||||
|
u, err := url.Parse(urlStr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := u.Query().Get("suffix")
|
||||||
|
fileName := fmt.Sprintf("%s.%s", path.Base(u.Path), ext)
|
||||||
|
return fileName
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyFile performs a file copy operation from the source file to the destination.
|
||||||
|
func copyFile(src string, dst string) error {
|
||||||
|
srcFile, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer srcFile.Close()
|
||||||
|
|
||||||
|
dstFile, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer dstFile.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(dstFile, srcFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
241
management/server/geolocation/geolocation.go
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
package geolocation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/oschwald/maxminddb-golang"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MMDBFileName = "GeoLite2-City.mmdb"
|
||||||
|
|
||||||
|
type Geolocation struct {
|
||||||
|
mmdbPath string
|
||||||
|
mux sync.RWMutex
|
||||||
|
sha256sum []byte
|
||||||
|
db *maxminddb.Reader
|
||||||
|
locationDB *SqliteStore
|
||||||
|
stopCh chan struct{}
|
||||||
|
reloadCheckInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type Record struct {
|
||||||
|
City struct {
|
||||||
|
GeonameID uint `maxminddb:"geoname_id"`
|
||||||
|
Names struct {
|
||||||
|
En string `maxminddb:"en"`
|
||||||
|
} `maxminddb:"names"`
|
||||||
|
} `maxminddb:"city"`
|
||||||
|
Continent struct {
|
||||||
|
GeonameID uint `maxminddb:"geoname_id"`
|
||||||
|
Code string `maxminddb:"code"`
|
||||||
|
} `maxminddb:"continent"`
|
||||||
|
Country struct {
|
||||||
|
GeonameID uint `maxminddb:"geoname_id"`
|
||||||
|
ISOCode string `maxminddb:"iso_code"`
|
||||||
|
} `maxminddb:"country"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type City struct {
|
||||||
|
GeoNameID int `gorm:"column:geoname_id"`
|
||||||
|
CityName string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Country struct {
|
||||||
|
CountryISOCode string `gorm:"column:country_iso_code"`
|
||||||
|
CountryName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGeolocation(dataDir string) (*Geolocation, error) {
|
||||||
|
if err := loadGeolocationDatabases(dataDir); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load MaxMind databases: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mmdbPath := path.Join(dataDir, MMDBFileName)
|
||||||
|
db, err := openDB(mmdbPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256sum, err := calculateFileSHA256(mmdbPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
locationDB, err := NewSqliteStore(dataDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
geo := &Geolocation{
|
||||||
|
mmdbPath: mmdbPath,
|
||||||
|
mux: sync.RWMutex{},
|
||||||
|
sha256sum: sha256sum,
|
||||||
|
db: db,
|
||||||
|
locationDB: locationDB,
|
||||||
|
reloadCheckInterval: 60 * time.Second, // TODO: make configurable
|
||||||
|
stopCh: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
go geo.reloader()
|
||||||
|
|
||||||
|
return geo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func openDB(mmdbPath string) (*maxminddb.Reader, error) {
|
||||||
|
_, err := os.Stat(mmdbPath)
|
||||||
|
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("%v does not exist", mmdbPath)
|
||||||
|
} else if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := maxminddb.Open(mmdbPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%v could not be opened: %w", mmdbPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gl *Geolocation) Lookup(ip net.IP) (*Record, error) {
|
||||||
|
gl.mux.RLock()
|
||||||
|
defer gl.mux.RUnlock()
|
||||||
|
|
||||||
|
var record Record
|
||||||
|
err := gl.db.Lookup(ip, &record)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &record, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllCountries retrieves a list of all countries.
|
||||||
|
func (gl *Geolocation) GetAllCountries() ([]Country, error) {
|
||||||
|
allCountries, err := gl.locationDB.GetAllCountries()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
countries := make([]Country, 0)
|
||||||
|
for _, country := range allCountries {
|
||||||
|
if country.CountryName != "" {
|
||||||
|
countries = append(countries, country)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return countries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCitiesByCountry retrieves a list of cities in a specific country based on the country's ISO code.
|
||||||
|
func (gl *Geolocation) GetCitiesByCountry(countryISOCode string) ([]City, error) {
|
||||||
|
allCities, err := gl.locationDB.GetCitiesByCountry(countryISOCode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cities := make([]City, 0)
|
||||||
|
for _, city := range allCities {
|
||||||
|
if city.CityName != "" {
|
||||||
|
cities = append(cities, city)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cities, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gl *Geolocation) Stop() error {
|
||||||
|
close(gl.stopCh)
|
||||||
|
if gl.db != nil {
|
||||||
|
if err := gl.db.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if gl.locationDB != nil {
|
||||||
|
if err := gl.locationDB.close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gl *Geolocation) reloader() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-gl.stopCh:
|
||||||
|
return
|
||||||
|
case <-time.After(gl.reloadCheckInterval):
|
||||||
|
if err := gl.locationDB.reload(); err != nil {
|
||||||
|
log.Errorf("geonames db reload failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newSha256sum1, err := calculateFileSHA256(gl.mmdbPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to calculate sha256 sum for '%s': %s", gl.mmdbPath, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(gl.sha256sum, newSha256sum1) {
|
||||||
|
// we check sum twice just to avoid possible case when we reload during update of the file
|
||||||
|
// considering the frequency of file update (few times a week) checking sum twice should be enough
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
newSha256sum2, err := calculateFileSHA256(gl.mmdbPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to calculate sha256 sum for '%s': %s", gl.mmdbPath, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !bytes.Equal(newSha256sum1, newSha256sum2) {
|
||||||
|
log.Errorf("sha256 sum changed during reloading of '%s'", gl.mmdbPath)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = gl.reload(newSha256sum2)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("mmdb reload failed: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debugf("No changes in '%s', no need to reload. Next check is in %.0f seconds.",
|
||||||
|
gl.mmdbPath, gl.reloadCheckInterval.Seconds())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gl *Geolocation) reload(newSha256sum []byte) error {
|
||||||
|
gl.mux.Lock()
|
||||||
|
defer gl.mux.Unlock()
|
||||||
|
|
||||||
|
log.Infof("Reloading '%s'", gl.mmdbPath)
|
||||||
|
|
||||||
|
err := gl.db.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := openDB(gl.mmdbPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.db = db
|
||||||
|
gl.sha256sum = newSha256sum
|
||||||
|
|
||||||
|
log.Infof("Successfully reloaded '%s'", gl.mmdbPath)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileExists(filePath string) (bool, error) {
|
||||||
|
_, err := os.Stat(filePath)
|
||||||
|
if err == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, fmt.Errorf("%v does not exist", filePath)
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
55
management/server/geolocation/geolocation_test.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package geolocation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// from https://github.com/maxmind/MaxMind-DB/blob/main/test-data/GeoLite2-City-Test.mmdb
|
||||||
|
var mmdbPath = "../testdata/GeoLite2-City-Test.mmdb"
|
||||||
|
|
||||||
|
func TestGeoLite_Lookup(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
filename := path.Join(tempDir, MMDBFileName)
|
||||||
|
err := util.CopyFileContents(mmdbPath, filename)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer func() {
|
||||||
|
err := os.Remove(filename)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("os.Remove: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
db, err := openDB(mmdbPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
geo := &Geolocation{
|
||||||
|
mux: sync.RWMutex{},
|
||||||
|
db: db,
|
||||||
|
stopCh: make(chan struct{}),
|
||||||
|
}
|
||||||
|
assert.NotNil(t, geo)
|
||||||
|
defer func() {
|
||||||
|
err = geo.Stop()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("geo.Stop: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
record, err := geo.Lookup(net.ParseIP("89.160.20.128"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, record)
|
||||||
|
assert.Equal(t, "SE", record.Country.ISOCode)
|
||||||
|
assert.Equal(t, uint(2661886), record.Country.GeonameID)
|
||||||
|
assert.Equal(t, "Linköping", record.City.Names.En)
|
||||||
|
assert.Equal(t, uint(2694762), record.City.GeonameID)
|
||||||
|
assert.Equal(t, "EU", record.Continent.Code)
|
||||||
|
assert.Equal(t, uint(6255148), record.Continent.GeonameID)
|
||||||
|
}
|
||||||
243
management/server/geolocation/store.go
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
package geolocation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/logger"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
GeoSqliteDBFile = "geonames.db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GeoNames struct {
|
||||||
|
GeoNameID int `gorm:"column:geoname_id"`
|
||||||
|
LocaleCode string `gorm:"column:locale_code"`
|
||||||
|
ContinentCode string `gorm:"column:continent_code"`
|
||||||
|
ContinentName string `gorm:"column:continent_name"`
|
||||||
|
CountryIsoCode string `gorm:"column:country_iso_code"`
|
||||||
|
CountryName string `gorm:"column:country_name"`
|
||||||
|
Subdivision1IsoCode string `gorm:"column:subdivision_1_iso_code"`
|
||||||
|
Subdivision1Name string `gorm:"column:subdivision_1_name"`
|
||||||
|
Subdivision2IsoCode string `gorm:"column:subdivision_2_iso_code"`
|
||||||
|
Subdivision2Name string `gorm:"column:subdivision_2_name"`
|
||||||
|
CityName string `gorm:"column:city_name"`
|
||||||
|
MetroCode string `gorm:"column:metro_code"`
|
||||||
|
TimeZone string `gorm:"column:time_zone"`
|
||||||
|
IsInEuropeanUnion string `gorm:"column:is_in_european_union"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*GeoNames) TableName() string {
|
||||||
|
return "geonames"
|
||||||
|
}
|
||||||
|
|
||||||
|
// SqliteStore represents a location storage backed by a Sqlite DB.
|
||||||
|
type SqliteStore struct {
|
||||||
|
db *gorm.DB
|
||||||
|
filePath string
|
||||||
|
mux sync.RWMutex
|
||||||
|
closed bool
|
||||||
|
sha256sum []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqliteStore(dataDir string) (*SqliteStore, error) {
|
||||||
|
file := filepath.Join(dataDir, GeoSqliteDBFile)
|
||||||
|
|
||||||
|
db, err := connectDB(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256sum, err := calculateFileSHA256(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &SqliteStore{
|
||||||
|
db: db,
|
||||||
|
filePath: file,
|
||||||
|
mux: sync.RWMutex{},
|
||||||
|
sha256sum: sha256sum,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllCountries returns a list of all countries in the store.
|
||||||
|
func (s *SqliteStore) GetAllCountries() ([]Country, error) {
|
||||||
|
s.mux.RLock()
|
||||||
|
defer s.mux.RUnlock()
|
||||||
|
|
||||||
|
if s.closed {
|
||||||
|
return nil, status.Errorf(status.PreconditionFailed, "geo location database is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
var countries []Country
|
||||||
|
result := s.db.Model(&GeoNames{}).
|
||||||
|
Select("country_iso_code", "country_name").
|
||||||
|
Group("country_name").
|
||||||
|
Scan(&countries)
|
||||||
|
if result.Error != nil {
|
||||||
|
return nil, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return countries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCitiesByCountry retrieves a list of cities from the store based on the given country ISO code.
|
||||||
|
func (s *SqliteStore) GetCitiesByCountry(countryISOCode string) ([]City, error) {
|
||||||
|
s.mux.RLock()
|
||||||
|
defer s.mux.RUnlock()
|
||||||
|
|
||||||
|
if s.closed {
|
||||||
|
return nil, status.Errorf(status.PreconditionFailed, "geo location database is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
var cities []City
|
||||||
|
result := s.db.Model(&GeoNames{}).
|
||||||
|
Select("geoname_id", "city_name").
|
||||||
|
Where("country_iso_code = ?", countryISOCode).
|
||||||
|
Group("city_name").
|
||||||
|
Scan(&cities)
|
||||||
|
if result.Error != nil {
|
||||||
|
return nil, result.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
return cities, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reload attempts to reload the SqliteStore's database if the database file has changed.
|
||||||
|
func (s *SqliteStore) reload() error {
|
||||||
|
s.mux.Lock()
|
||||||
|
defer s.mux.Unlock()
|
||||||
|
|
||||||
|
newSha256sum1, err := calculateFileSHA256(s.filePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to calculate sha256 sum for '%s': %s", s.filePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(s.sha256sum, newSha256sum1) {
|
||||||
|
// we check sum twice just to avoid possible case when we reload during update of the file
|
||||||
|
// considering the frequency of file update (few times a week) checking sum twice should be enough
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
newSha256sum2, err := calculateFileSHA256(s.filePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to calculate sha256 sum for '%s': %s", s.filePath, err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(newSha256sum1, newSha256sum2) {
|
||||||
|
return fmt.Errorf("sha256 sum changed during reloading of '%s'", s.filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Reloading '%s'", s.filePath)
|
||||||
|
_ = s.close()
|
||||||
|
s.closed = true
|
||||||
|
|
||||||
|
newDb, err := connectDB(s.filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.closed = false
|
||||||
|
s.db = newDb
|
||||||
|
|
||||||
|
log.Infof("Successfully reloaded '%s'", s.filePath)
|
||||||
|
} else {
|
||||||
|
log.Debugf("No changes in '%s', no need to reload", s.filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// close closes the database connection.
|
||||||
|
// It retrieves the underlying *sql.DB object from the *gorm.DB object
|
||||||
|
// and calls the Close() method on it.
|
||||||
|
func (s *SqliteStore) close() error {
|
||||||
|
sqlDB, err := s.db.DB()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return sqlDB.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// connectDB connects to an SQLite database and prepares it by setting up an in-memory database.
|
||||||
|
func connectDB(filePath string) (*gorm.DB, error) {
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
log.Debugf("took %v to setup geoname db", time.Since(start))
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err := fileExists(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
storeStr := "file::memory:?cache=shared"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
storeStr = "file::memory:"
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := gorm.Open(sqlite.Open(storeStr), &gorm.Config{
|
||||||
|
Logger: logger.Default.LogMode(logger.Silent),
|
||||||
|
PrepareStmt: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := setupInMemoryDBFromFile(db, filePath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sql, err := db.DB()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conns := runtime.NumCPU()
|
||||||
|
sql.SetMaxOpenConns(conns)
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupInMemoryDBFromFile prepares an in-memory DB by attaching a file database and,
|
||||||
|
// copies the data from the attached database to the in-memory database.
|
||||||
|
func setupInMemoryDBFromFile(db *gorm.DB, source string) error {
|
||||||
|
// Attach the on-disk database to the in-memory database
|
||||||
|
attachStmt := fmt.Sprintf("ATTACH DATABASE '%s' AS source;", source)
|
||||||
|
if err := db.Exec(attachStmt).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.Exec(`
|
||||||
|
CREATE TABLE geonames AS SELECT * FROM source.geonames;
|
||||||
|
`).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach the on-disk database from the in-memory database
|
||||||
|
err = db.Exec("DETACH DATABASE source;").Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// index geoname_id and country_iso_code field
|
||||||
|
err = db.Exec("CREATE INDEX idx_geonames_country_iso_code ON geonames(country_iso_code);").Error
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Exec("CREATE INDEX idx_geonames_geoname_id ON geonames(geoname_id);").Error
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
176
management/server/geolocation/utils.go
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package geolocation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"archive/zip"
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// decompressTarGzFile decompresses a .tar.gz file.
|
||||||
|
func decompressTarGzFile(filepath, destDir string) error {
|
||||||
|
file, err := os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
gzipReader, err := gzip.NewReader(file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer gzipReader.Close()
|
||||||
|
|
||||||
|
tarReader := tar.NewReader(gzipReader)
|
||||||
|
|
||||||
|
for {
|
||||||
|
header, err := tarReader.Next()
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.Typeflag == tar.TypeReg {
|
||||||
|
outFile, err := os.Create(path.Join(destDir, path.Base(header.Name)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(outFile, tarReader) // #nosec G110
|
||||||
|
outFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decompressZipFile decompresses a .zip file.
|
||||||
|
func decompressZipFile(filepath, destDir string) error {
|
||||||
|
r, err := zip.OpenReader(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
for _, f := range r.File {
|
||||||
|
if f.FileInfo().IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile, err := os.Create(path.Join(destDir, path.Base(f.Name)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rc, err := f.Open()
|
||||||
|
if err != nil {
|
||||||
|
outFile.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(outFile, rc) // #nosec G110
|
||||||
|
outFile.Close()
|
||||||
|
rc.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateFileSHA256 calculates the SHA256 checksum of a file.
|
||||||
|
func calculateFileSHA256(filepath string) ([]byte, error) {
|
||||||
|
file, err := os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
if _, err := io.Copy(h, file); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadChecksumFromFile loads the first checksum from a file.
|
||||||
|
func loadChecksumFromFile(filepath string) (string, error) {
|
||||||
|
file, err := os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
if scanner.Scan() {
|
||||||
|
parts := strings.Fields(scanner.Text())
|
||||||
|
if len(parts) > 0 {
|
||||||
|
return parts[0], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyChecksum compares the calculated SHA256 checksum of a file against the expected checksum.
|
||||||
|
func verifyChecksum(filepath, expectedChecksum string) error {
|
||||||
|
calculatedChecksum, err := calculateFileSHA256(filepath)
|
||||||
|
|
||||||
|
fileCheckSum := fmt.Sprintf("%x", calculatedChecksum)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileCheckSum != expectedChecksum {
|
||||||
|
return fmt.Errorf("checksum mismatch: expected %s, got %s", expectedChecksum, fileCheckSum)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadFile downloads a file from a URL and saves it to a local file path.
|
||||||
|
func downloadFile(url, filepath string) error {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
bodyBytes, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("unexpected error occurred while downloading the file: %s", string(bodyBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.Create(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, bytes.NewBuffer(bodyBytes))
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -274,6 +274,15 @@ func (am *DefaultAccountManager) DeleteGroup(accountId, userId, groupID string)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check integrated peer approval
|
||||||
|
if account.Settings.Extra != nil {
|
||||||
|
for _, integratedPeerApprovalGroups := range account.Settings.Extra.IntegratedApprovalGroups {
|
||||||
|
if groupID == integratedPeerApprovalGroups {
|
||||||
|
return &GroupLinkError{"integrated approval", g.Name}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delete(account.Groups, groupID)
|
delete(account.Groups, groupID)
|
||||||
|
|
||||||
account.Network.IncSerial()
|
account.Network.IncSerial()
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package server
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -109,11 +111,11 @@ func (s *GRPCServer) GetServerKey(ctx context.Context, req *proto.Empty) (*proto
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRealIP(ctx context.Context) string {
|
func getRealIP(ctx context.Context) net.IP {
|
||||||
if ip, ok := realip.FromContext(ctx); ok {
|
if addr, ok := realip.FromContext(ctx); ok {
|
||||||
return ip.String()
|
return net.IP(addr.AsSlice())
|
||||||
}
|
}
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync validates the existence of a connecting peer, sends an initial state (all available for the connecting peers) and
|
// Sync validates the existence of a connecting peer, sends an initial state (all available for the connecting peers) and
|
||||||
@@ -124,7 +126,7 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
|||||||
s.appMetrics.GRPCMetrics().CountSyncRequest()
|
s.appMetrics.GRPCMetrics().CountSyncRequest()
|
||||||
}
|
}
|
||||||
realIP := getRealIP(srv.Context())
|
realIP := getRealIP(srv.Context())
|
||||||
log.Debugf("Sync request from peer [%s] [%s]", req.WgPubKey, realIP)
|
log.Debugf("Sync request from peer [%s] [%s]", req.WgPubKey, realIP.String())
|
||||||
|
|
||||||
syncReq := &proto.SyncRequest{}
|
syncReq := &proto.SyncRequest{}
|
||||||
peerKey, err := s.parseRequest(req, syncReq)
|
peerKey, err := s.parseRequest(req, syncReq)
|
||||||
@@ -147,7 +149,7 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
|||||||
|
|
||||||
s.ephemeralManager.OnPeerConnected(peer)
|
s.ephemeralManager.OnPeerConnected(peer)
|
||||||
|
|
||||||
err = s.accountManager.MarkPeerConnected(peerKey.String(), true)
|
err = s.accountManager.MarkPeerConnected(peerKey.String(), true, realIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("failed marking peer as connected %s %v", peerKey, err)
|
log.Warnf("failed marking peer as connected %s %v", peerKey, err)
|
||||||
}
|
}
|
||||||
@@ -205,7 +207,7 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
|||||||
func (s *GRPCServer) cancelPeerRoutines(peer *nbpeer.Peer) {
|
func (s *GRPCServer) cancelPeerRoutines(peer *nbpeer.Peer) {
|
||||||
s.peersUpdateManager.CloseChannel(peer.ID)
|
s.peersUpdateManager.CloseChannel(peer.ID)
|
||||||
s.turnCredentialsManager.CancelRefresh(peer.ID)
|
s.turnCredentialsManager.CancelRefresh(peer.ID)
|
||||||
_ = s.accountManager.MarkPeerConnected(peer.Key, false)
|
_ = s.accountManager.MarkPeerConnected(peer.Key, false, nil)
|
||||||
s.ephemeralManager.OnPeerDisconnected(peer)
|
s.ephemeralManager.OnPeerDisconnected(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,15 +256,42 @@ func mapError(err error) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func extractPeerMeta(loginReq *proto.LoginRequest) nbpeer.PeerSystemMeta {
|
func extractPeerMeta(loginReq *proto.LoginRequest) nbpeer.PeerSystemMeta {
|
||||||
|
osVersion := loginReq.GetMeta().GetOSVersion()
|
||||||
|
if osVersion == "" {
|
||||||
|
osVersion = loginReq.GetMeta().GetCore()
|
||||||
|
}
|
||||||
|
|
||||||
|
networkAddresses := make([]nbpeer.NetworkAddress, 0, len(loginReq.GetMeta().GetNetworkAddresses()))
|
||||||
|
for _, addr := range loginReq.GetMeta().GetNetworkAddresses() {
|
||||||
|
netAddr, err := netip.ParsePrefix(addr.GetNetIP())
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to parse netip address, %s: %v", addr.GetNetIP(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
networkAddresses = append(networkAddresses, nbpeer.NetworkAddress{
|
||||||
|
NetIP: netAddr,
|
||||||
|
Mac: addr.GetMac(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return nbpeer.PeerSystemMeta{
|
return nbpeer.PeerSystemMeta{
|
||||||
Hostname: loginReq.GetMeta().GetHostname(),
|
Hostname: loginReq.GetMeta().GetHostname(),
|
||||||
GoOS: loginReq.GetMeta().GetGoOS(),
|
GoOS: loginReq.GetMeta().GetGoOS(),
|
||||||
Kernel: loginReq.GetMeta().GetKernel(),
|
Kernel: loginReq.GetMeta().GetKernel(),
|
||||||
Core: loginReq.GetMeta().GetCore(),
|
Platform: loginReq.GetMeta().GetPlatform(),
|
||||||
Platform: loginReq.GetMeta().GetPlatform(),
|
OS: loginReq.GetMeta().GetOS(),
|
||||||
OS: loginReq.GetMeta().GetOS(),
|
OSVersion: osVersion,
|
||||||
WtVersion: loginReq.GetMeta().GetWiretrusteeVersion(),
|
WtVersion: loginReq.GetMeta().GetWiretrusteeVersion(),
|
||||||
UIVersion: loginReq.GetMeta().GetUiVersion(),
|
UIVersion: loginReq.GetMeta().GetUiVersion(),
|
||||||
|
KernelVersion: loginReq.GetMeta().GetKernelVersion(),
|
||||||
|
NetworkAddresses: networkAddresses,
|
||||||
|
SystemSerialNumber: loginReq.GetMeta().GetSysSerialNumber(),
|
||||||
|
SystemProductName: loginReq.GetMeta().GetSysProductName(),
|
||||||
|
SystemManufacturer: loginReq.GetMeta().GetSysManufacturer(),
|
||||||
|
Environment: nbpeer.Environment{
|
||||||
|
Cloud: loginReq.GetMeta().GetEnvironment().GetCloud(),
|
||||||
|
Platform: loginReq.GetMeta().GetEnvironment().GetPlatform(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,7 +325,7 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p
|
|||||||
s.appMetrics.GRPCMetrics().CountLoginRequest()
|
s.appMetrics.GRPCMetrics().CountLoginRequest()
|
||||||
}
|
}
|
||||||
realIP := getRealIP(ctx)
|
realIP := getRealIP(ctx)
|
||||||
log.Debugf("Login request from peer [%s] [%s]", req.WgPubKey, realIP)
|
log.Debugf("Login request from peer [%s] [%s]", req.WgPubKey, realIP.String())
|
||||||
|
|
||||||
loginReq := &proto.LoginRequest{}
|
loginReq := &proto.LoginRequest{}
|
||||||
peerKey, err := s.parseRequest(req, loginReq)
|
peerKey, err := s.parseRequest(req, loginReq)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
openapi: 3.0.1
|
openapi: 3.1.0
|
||||||
servers:
|
servers:
|
||||||
- url: https://api.netbird.io
|
- url: https://api.netbird.io
|
||||||
description: Default server
|
description: Default server
|
||||||
@@ -21,6 +21,8 @@ tags:
|
|||||||
description: Interact with and view information about rules.
|
description: Interact with and view information about rules.
|
||||||
- name: Policies
|
- name: Policies
|
||||||
description: Interact with and view information about policies.
|
description: Interact with and view information about policies.
|
||||||
|
- name: Posture Checks
|
||||||
|
description: Interact with and view information about posture checks.
|
||||||
- name: Routes
|
- name: Routes
|
||||||
description: Interact with and view information about routes.
|
description: Interact with and view information about routes.
|
||||||
- name: DNS
|
- name: DNS
|
||||||
@@ -119,7 +121,7 @@ components:
|
|||||||
description: Last time this user performed a login to the dashboard
|
description: Last time this user performed a login to the dashboard
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
example: 2023-05-05T09:00:35.477782Z
|
example: "2023-05-05T09:00:35.477782Z"
|
||||||
auto_groups:
|
auto_groups:
|
||||||
description: Group IDs to auto-assign to peers registered by this user
|
description: Group IDs to auto-assign to peers registered by this user
|
||||||
type: array
|
type: array
|
||||||
@@ -245,6 +247,10 @@ components:
|
|||||||
description: Peer's IP address
|
description: Peer's IP address
|
||||||
type: string
|
type: string
|
||||||
example: 10.64.0.1
|
example: 10.64.0.1
|
||||||
|
connection_ip:
|
||||||
|
description: Peer's public connection IP address
|
||||||
|
type: string
|
||||||
|
example: 35.64.0.1
|
||||||
connected:
|
connected:
|
||||||
description: Peer to Management connection status
|
description: Peer to Management connection status
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -253,11 +259,19 @@ components:
|
|||||||
description: Last time peer connected to Netbird's management service
|
description: Last time peer connected to Netbird's management service
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
example: 2023-05-05T10:05:26.420578Z
|
example: "2023-05-05T10:05:26.420578Z"
|
||||||
os:
|
os:
|
||||||
description: Peer's operating system and version
|
description: Peer's operating system and version
|
||||||
type: string
|
type: string
|
||||||
example: Darwin 13.2.1
|
example: Darwin 13.2.1
|
||||||
|
kernel_version:
|
||||||
|
description: Peer's operating system kernel version
|
||||||
|
type: string
|
||||||
|
example: 23.2.0
|
||||||
|
geoname_id:
|
||||||
|
description: Unique identifier from the GeoNames database for a specific geographical location.
|
||||||
|
type: integer
|
||||||
|
example: 2643743
|
||||||
version:
|
version:
|
||||||
description: Peer's daemon or cli version
|
description: Peer's daemon or cli version
|
||||||
type: string
|
type: string
|
||||||
@@ -299,24 +313,35 @@ components:
|
|||||||
description: Last time this peer performed log in (authentication). E.g., user authenticated.
|
description: Last time this peer performed log in (authentication). E.g., user authenticated.
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
example: 2023-05-05T09:00:35.477782Z
|
example: "2023-05-05T09:00:35.477782Z"
|
||||||
approval_required:
|
approval_required:
|
||||||
description: (Cloud only) Indicates whether peer needs approval
|
description: (Cloud only) Indicates whether peer needs approval
|
||||||
type: boolean
|
type: boolean
|
||||||
example: true
|
example: true
|
||||||
|
country_code:
|
||||||
|
$ref: '#/components/schemas/CountryCode'
|
||||||
|
city_name:
|
||||||
|
$ref: '#/components/schemas/CityName'
|
||||||
required:
|
required:
|
||||||
- ip
|
- city_name
|
||||||
- connected
|
- connected
|
||||||
- last_seen
|
- connection_ip
|
||||||
- os
|
- country_code
|
||||||
- version
|
|
||||||
- groups
|
|
||||||
- ssh_enabled
|
|
||||||
- hostname
|
|
||||||
- dns_label
|
- dns_label
|
||||||
|
- geoname_id
|
||||||
|
- groups
|
||||||
|
- hostname
|
||||||
|
- ip
|
||||||
|
- kernel_version
|
||||||
|
- last_login
|
||||||
|
- last_seen
|
||||||
- login_expiration_enabled
|
- login_expiration_enabled
|
||||||
- login_expired
|
- login_expired
|
||||||
- last_login
|
- os
|
||||||
|
- ssh_enabled
|
||||||
|
- user_id
|
||||||
|
- version
|
||||||
|
- ui_version
|
||||||
AccessiblePeer:
|
AccessiblePeer:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/PeerMinimum'
|
- $ref: '#/components/schemas/PeerMinimum'
|
||||||
@@ -380,7 +405,7 @@ components:
|
|||||||
description: Setup Key expiration date
|
description: Setup Key expiration date
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
example: 2023-06-01T14:47:22.291057Z
|
example: "2023-06-01T14:47:22.291057Z"
|
||||||
type:
|
type:
|
||||||
description: Setup key type, one-off for single time usage and reusable
|
description: Setup key type, one-off for single time usage and reusable
|
||||||
type: string
|
type: string
|
||||||
@@ -401,7 +426,7 @@ components:
|
|||||||
description: Setup key last usage date
|
description: Setup key last usage date
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
example: 2023-05-05T09:00:35.477782Z
|
example: "2023-05-05T09:00:35.477782Z"
|
||||||
state:
|
state:
|
||||||
description: Setup key status, "valid", "overused","expired" or "revoked"
|
description: Setup key status, "valid", "overused","expired" or "revoked"
|
||||||
type: string
|
type: string
|
||||||
@@ -416,7 +441,7 @@ components:
|
|||||||
description: Setup key last update date
|
description: Setup key last update date
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
example: 2023-05-05T09:00:35.477782Z
|
example: "2023-05-05T09:00:35.477782Z"
|
||||||
usage_limit:
|
usage_limit:
|
||||||
description: A number of times this key can be used. The value of 0 indicates the unlimited usage.
|
description: A number of times this key can be used. The value of 0 indicates the unlimited usage.
|
||||||
type: integer
|
type: integer
|
||||||
@@ -497,7 +522,7 @@ components:
|
|||||||
description: Date the token expires
|
description: Date the token expires
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
example: 2023-05-05T14:38:28.977616Z
|
example: "2023-05-05T14:38:28.977616Z"
|
||||||
created_by:
|
created_by:
|
||||||
description: User ID of the user who created the token
|
description: User ID of the user who created the token
|
||||||
type: string
|
type: string
|
||||||
@@ -506,12 +531,12 @@ components:
|
|||||||
description: Date the token was created
|
description: Date the token was created
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
example: 2023-05-02T14:48:20.465209Z
|
example: "2023-05-02T14:48:20.465209Z"
|
||||||
last_used:
|
last_used:
|
||||||
description: Date the token was last used
|
description: Date the token was last used
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
example: 2023-05-04T12:45:25.9723616Z
|
example: "2023-05-04T12:45:25.9723616Z"
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- name
|
- name
|
||||||
@@ -774,6 +799,12 @@ components:
|
|||||||
- $ref: '#/components/schemas/PolicyMinimum'
|
- $ref: '#/components/schemas/PolicyMinimum'
|
||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
|
source_posture_checks:
|
||||||
|
description: Posture checks ID's applied to policy source groups
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: "chacdk86lnnboviihd70"
|
||||||
rules:
|
rules:
|
||||||
description: Policy rule object for policy UI editor
|
description: Policy rule object for policy UI editor
|
||||||
type: array
|
type: array
|
||||||
@@ -786,6 +817,12 @@ components:
|
|||||||
- $ref: '#/components/schemas/PolicyMinimum'
|
- $ref: '#/components/schemas/PolicyMinimum'
|
||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
|
source_posture_checks:
|
||||||
|
description: Posture checks ID's applied to policy source groups
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: "chacdk86lnnboviihd70"
|
||||||
rules:
|
rules:
|
||||||
description: Policy rule object for policy UI editor
|
description: Policy rule object for policy UI editor
|
||||||
type: array
|
type: array
|
||||||
@@ -793,6 +830,190 @@ components:
|
|||||||
$ref: '#/components/schemas/PolicyRule'
|
$ref: '#/components/schemas/PolicyRule'
|
||||||
required:
|
required:
|
||||||
- rules
|
- rules
|
||||||
|
- source_posture_checks
|
||||||
|
PostureCheck:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: Posture check ID
|
||||||
|
type: string
|
||||||
|
example: ch8i4ug6lnn4g9hqv7mg
|
||||||
|
name:
|
||||||
|
description: Posture check unique name identifier
|
||||||
|
type: string
|
||||||
|
example: Default
|
||||||
|
description:
|
||||||
|
description: Posture check friendly description
|
||||||
|
type: string
|
||||||
|
example: This checks if the peer is running required NetBird's version
|
||||||
|
checks:
|
||||||
|
$ref: '#/components/schemas/Checks'
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
- checks
|
||||||
|
Checks:
|
||||||
|
description: List of objects that perform the actual checks
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
nb_version_check:
|
||||||
|
$ref: '#/components/schemas/NBVersionCheck'
|
||||||
|
os_version_check:
|
||||||
|
$ref: '#/components/schemas/OSVersionCheck'
|
||||||
|
geo_location_check:
|
||||||
|
$ref: '#/components/schemas/GeoLocationCheck'
|
||||||
|
peer_network_range_check:
|
||||||
|
$ref: '#/components/schemas/PeerNetworkRangeCheck'
|
||||||
|
NBVersionCheck:
|
||||||
|
description: Posture check for the version of NetBird
|
||||||
|
type: object
|
||||||
|
$ref: '#/components/schemas/MinVersionCheck'
|
||||||
|
OSVersionCheck:
|
||||||
|
description: Posture check for the version of operating system
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
android:
|
||||||
|
description: Minimum version of Android
|
||||||
|
$ref: '#/components/schemas/MinVersionCheck'
|
||||||
|
darwin:
|
||||||
|
$ref: '#/components/schemas/MinVersionCheck'
|
||||||
|
ios:
|
||||||
|
description: Minimum version of iOS
|
||||||
|
$ref: '#/components/schemas/MinVersionCheck'
|
||||||
|
linux:
|
||||||
|
description: Minimum Linux kernel version
|
||||||
|
$ref: '#/components/schemas/MinKernelVersionCheck'
|
||||||
|
windows:
|
||||||
|
description: Minimum Windows kernel build version
|
||||||
|
$ref: '#/components/schemas/MinKernelVersionCheck'
|
||||||
|
example:
|
||||||
|
android:
|
||||||
|
min_version: "13"
|
||||||
|
ios:
|
||||||
|
min_version: "17.3.1"
|
||||||
|
darwin:
|
||||||
|
min_version: "14.2.1"
|
||||||
|
linux:
|
||||||
|
min_kernel_version: "5.3.3"
|
||||||
|
windows:
|
||||||
|
min_kernel_version: "10.0.1234"
|
||||||
|
MinVersionCheck:
|
||||||
|
description: Posture check for the version of operating system
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
min_version:
|
||||||
|
description: Minimum acceptable version
|
||||||
|
type: string
|
||||||
|
example: "14.3"
|
||||||
|
required:
|
||||||
|
- min_version
|
||||||
|
MinKernelVersionCheck:
|
||||||
|
description: Posture check with the kernel version
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
min_kernel_version:
|
||||||
|
description: Minimum acceptable version
|
||||||
|
type: string
|
||||||
|
example: "6.6.12"
|
||||||
|
required:
|
||||||
|
- min_kernel_version
|
||||||
|
GeoLocationCheck:
|
||||||
|
description: Posture check for geo location
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
locations:
|
||||||
|
description: List of geo locations to which the policy applies
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Location'
|
||||||
|
action:
|
||||||
|
description: Action to take upon policy match
|
||||||
|
type: string
|
||||||
|
enum: [ "allow", "deny" ]
|
||||||
|
example: "allow"
|
||||||
|
required:
|
||||||
|
- locations
|
||||||
|
- action
|
||||||
|
PeerNetworkRangeCheck:
|
||||||
|
description: Posture check for allow or deny access based on peer local network addresses
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
ranges:
|
||||||
|
description: List of peer network ranges in CIDR notation
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: ["192.168.1.0/24", "10.0.0.0/8", "2001:db8:1234:1a00::/56"]
|
||||||
|
action:
|
||||||
|
description: Action to take upon policy match
|
||||||
|
type: string
|
||||||
|
enum: [ "allow", "deny" ]
|
||||||
|
example: "allow"
|
||||||
|
required:
|
||||||
|
- ranges
|
||||||
|
- action
|
||||||
|
Location:
|
||||||
|
description: Describe geographical location information
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
country_code:
|
||||||
|
$ref: '#/components/schemas/CountryCode'
|
||||||
|
city_name:
|
||||||
|
$ref: '#/components/schemas/CityName'
|
||||||
|
required:
|
||||||
|
- country_code
|
||||||
|
CountryCode:
|
||||||
|
description: 2-letter ISO 3166-1 alpha-2 code that represents the country
|
||||||
|
type: string
|
||||||
|
example: "DE"
|
||||||
|
CityName:
|
||||||
|
description: Commonly used English name of the city
|
||||||
|
type: string
|
||||||
|
example: "Berlin"
|
||||||
|
Country:
|
||||||
|
description: Describe country geographical location information
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
country_name:
|
||||||
|
description: Commonly used English name of the country
|
||||||
|
type: string
|
||||||
|
example: "Germany"
|
||||||
|
country_code:
|
||||||
|
$ref: '#/components/schemas/CountryCode'
|
||||||
|
required:
|
||||||
|
- country_name
|
||||||
|
- country_code
|
||||||
|
City:
|
||||||
|
description: Describe city geographical location information
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
geoname_id:
|
||||||
|
description: Integer ID of the record in GeoNames database
|
||||||
|
type: integer
|
||||||
|
example: 2950158
|
||||||
|
city_name:
|
||||||
|
description: Commonly used English name of the city
|
||||||
|
type: string
|
||||||
|
example: "Berlin"
|
||||||
|
required:
|
||||||
|
- geoname_id
|
||||||
|
- city_name
|
||||||
|
PostureCheckUpdate:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
description: Posture check name identifier
|
||||||
|
type: string
|
||||||
|
example: Default
|
||||||
|
description:
|
||||||
|
description: Posture check friendly description
|
||||||
|
type: string
|
||||||
|
example: This checks if the peer is running required NetBird's version
|
||||||
|
checks:
|
||||||
|
$ref: '#/components/schemas/Checks'
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- description
|
||||||
RouteRequest:
|
RouteRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -976,7 +1197,7 @@ components:
|
|||||||
description: The date and time when the event occurred
|
description: The date and time when the event occurred
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
example: 2023-05-05T10:04:37.473542Z
|
example: "2023-05-05T10:04:37.473542Z"
|
||||||
activity:
|
activity:
|
||||||
description: The activity that occurred during the event
|
description: The activity that occurred during the event
|
||||||
type: string
|
type: string
|
||||||
@@ -2144,7 +2365,6 @@ paths:
|
|||||||
"$ref": "#/components/responses/forbidden"
|
"$ref": "#/components/responses/forbidden"
|
||||||
'500':
|
'500':
|
||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
|
||||||
/api/routes/{routeId}:
|
/api/routes/{routeId}:
|
||||||
get:
|
get:
|
||||||
summary: Retrieve a Route
|
summary: Retrieve a Route
|
||||||
@@ -2289,7 +2509,6 @@ paths:
|
|||||||
"$ref": "#/components/responses/forbidden"
|
"$ref": "#/components/responses/forbidden"
|
||||||
'500':
|
'500':
|
||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
|
||||||
/api/dns/nameservers/{nsgroupId}:
|
/api/dns/nameservers/{nsgroupId}:
|
||||||
get:
|
get:
|
||||||
summary: Retrieve a Nameserver Group
|
summary: Retrieve a Nameserver Group
|
||||||
@@ -2381,7 +2600,6 @@ paths:
|
|||||||
"$ref": "#/components/responses/forbidden"
|
"$ref": "#/components/responses/forbidden"
|
||||||
'500':
|
'500':
|
||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
|
||||||
/api/dns/settings:
|
/api/dns/settings:
|
||||||
get:
|
get:
|
||||||
summary: Retrieve DNS settings
|
summary: Retrieve DNS settings
|
||||||
@@ -2459,3 +2677,194 @@ paths:
|
|||||||
"$ref": "#/components/responses/forbidden"
|
"$ref": "#/components/responses/forbidden"
|
||||||
'500':
|
'500':
|
||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/posture-checks:
|
||||||
|
get:
|
||||||
|
summary: List all Posture Checks
|
||||||
|
description: Returns a list of all posture checks
|
||||||
|
tags: [ "Posture Checks" ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A JSON Array of posture checks
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/PostureCheck'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
post:
|
||||||
|
summary: Create a Posture Check
|
||||||
|
description: Creates a posture check
|
||||||
|
tags: [ "Posture Checks" ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
requestBody:
|
||||||
|
description: New posture check request
|
||||||
|
content:
|
||||||
|
'application/json':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PostureCheckUpdate'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A posture check Object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PostureCheck'
|
||||||
|
/api/posture-checks/{postureCheckId}:
|
||||||
|
get:
|
||||||
|
summary: Retrieve a Posture Check
|
||||||
|
description: Get information about a posture check
|
||||||
|
tags: [ "Posture Checks" ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: postureCheckId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a posture check
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A posture check object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PostureCheck'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
put:
|
||||||
|
summary: Update a Posture Check
|
||||||
|
description: Update/Replace a posture check
|
||||||
|
tags: [ "Posture Checks" ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: postureCheckId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a posture check
|
||||||
|
requestBody:
|
||||||
|
description: Update Rule request
|
||||||
|
content:
|
||||||
|
'application/json':
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PostureCheckUpdate'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A posture check object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PostureCheck'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
delete:
|
||||||
|
summary: Delete a Posture Check
|
||||||
|
description: Delete a posture check
|
||||||
|
tags: [ "Posture Checks" ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: postureCheckId
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The unique identifier of a posture check
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Delete status code
|
||||||
|
content: { }
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/locations/countries:
|
||||||
|
get:
|
||||||
|
summary: List all country codes
|
||||||
|
description: Get list of all country in 2-letter ISO 3166-1 alpha-2 codes
|
||||||
|
tags: [ "Geo Locations" ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: List of country codes
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
example: "DE"
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/locations/countries/{country}/cities:
|
||||||
|
get:
|
||||||
|
summary: List all city names by country
|
||||||
|
description: Get a list of all English city names for a given country code
|
||||||
|
tags: [ "Geo Locations" ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
- TokenAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: country
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Country'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: List of city names
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/City'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
@@ -63,11 +63,23 @@ const (
|
|||||||
EventActivityCodeUserUnblock EventActivityCode = "user.unblock"
|
EventActivityCodeUserUnblock EventActivityCode = "user.unblock"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Defines values for GeoLocationCheckAction.
|
||||||
|
const (
|
||||||
|
GeoLocationCheckActionAllow GeoLocationCheckAction = "allow"
|
||||||
|
GeoLocationCheckActionDeny GeoLocationCheckAction = "deny"
|
||||||
|
)
|
||||||
|
|
||||||
// Defines values for NameserverNsType.
|
// Defines values for NameserverNsType.
|
||||||
const (
|
const (
|
||||||
NameserverNsTypeUdp NameserverNsType = "udp"
|
NameserverNsTypeUdp NameserverNsType = "udp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Defines values for PeerNetworkRangeCheckAction.
|
||||||
|
const (
|
||||||
|
PeerNetworkRangeCheckActionAllow PeerNetworkRangeCheckAction = "allow"
|
||||||
|
PeerNetworkRangeCheckActionDeny PeerNetworkRangeCheckAction = "deny"
|
||||||
|
)
|
||||||
|
|
||||||
// Defines values for PolicyRuleAction.
|
// Defines values for PolicyRuleAction.
|
||||||
const (
|
const (
|
||||||
PolicyRuleActionAccept PolicyRuleAction = "accept"
|
PolicyRuleActionAccept PolicyRuleAction = "accept"
|
||||||
@@ -176,6 +188,45 @@ type AccountSettings struct {
|
|||||||
PeerLoginExpirationEnabled bool `json:"peer_login_expiration_enabled"`
|
PeerLoginExpirationEnabled bool `json:"peer_login_expiration_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks List of objects that perform the actual checks
|
||||||
|
type Checks struct {
|
||||||
|
// GeoLocationCheck Posture check for geo location
|
||||||
|
GeoLocationCheck *GeoLocationCheck `json:"geo_location_check,omitempty"`
|
||||||
|
|
||||||
|
// NbVersionCheck Posture check for the version of operating system
|
||||||
|
NbVersionCheck *NBVersionCheck `json:"nb_version_check,omitempty"`
|
||||||
|
|
||||||
|
// OsVersionCheck Posture check for the version of operating system
|
||||||
|
OsVersionCheck *OSVersionCheck `json:"os_version_check,omitempty"`
|
||||||
|
|
||||||
|
// PeerNetworkRangeCheck Posture check for allow or deny access based on peer local network addresses
|
||||||
|
PeerNetworkRangeCheck *PeerNetworkRangeCheck `json:"peer_network_range_check,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// City Describe city geographical location information
|
||||||
|
type City struct {
|
||||||
|
// CityName Commonly used English name of the city
|
||||||
|
CityName string `json:"city_name"`
|
||||||
|
|
||||||
|
// GeonameId Integer ID of the record in GeoNames database
|
||||||
|
GeonameId int `json:"geoname_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CityName Commonly used English name of the city
|
||||||
|
type CityName = string
|
||||||
|
|
||||||
|
// Country Describe country geographical location information
|
||||||
|
type Country struct {
|
||||||
|
// CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country
|
||||||
|
CountryCode CountryCode `json:"country_code"`
|
||||||
|
|
||||||
|
// CountryName Commonly used English name of the country
|
||||||
|
CountryName string `json:"country_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country
|
||||||
|
type CountryCode = string
|
||||||
|
|
||||||
// DNSSettings defines model for DNSSettings.
|
// DNSSettings defines model for DNSSettings.
|
||||||
type DNSSettings struct {
|
type DNSSettings struct {
|
||||||
// DisabledManagementGroups Groups whose DNS management is disabled
|
// DisabledManagementGroups Groups whose DNS management is disabled
|
||||||
@@ -215,6 +266,18 @@ type Event struct {
|
|||||||
// EventActivityCode The string code of the activity that occurred during the event
|
// EventActivityCode The string code of the activity that occurred during the event
|
||||||
type EventActivityCode string
|
type EventActivityCode string
|
||||||
|
|
||||||
|
// GeoLocationCheck Posture check for geo location
|
||||||
|
type GeoLocationCheck struct {
|
||||||
|
// Action Action to take upon policy match
|
||||||
|
Action GeoLocationCheckAction `json:"action"`
|
||||||
|
|
||||||
|
// Locations List of geo locations to which the policy applies
|
||||||
|
Locations []Location `json:"locations"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GeoLocationCheckAction Action to take upon policy match
|
||||||
|
type GeoLocationCheckAction string
|
||||||
|
|
||||||
// Group defines model for Group.
|
// Group defines model for Group.
|
||||||
type Group struct {
|
type Group struct {
|
||||||
// Id Group ID
|
// Id Group ID
|
||||||
@@ -257,6 +320,30 @@ type GroupRequest struct {
|
|||||||
Peers *[]string `json:"peers,omitempty"`
|
Peers *[]string `json:"peers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Location Describe geographical location information
|
||||||
|
type Location struct {
|
||||||
|
// CityName Commonly used English name of the city
|
||||||
|
CityName *CityName `json:"city_name,omitempty"`
|
||||||
|
|
||||||
|
// CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country
|
||||||
|
CountryCode CountryCode `json:"country_code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinKernelVersionCheck Posture check with the kernel version
|
||||||
|
type MinKernelVersionCheck struct {
|
||||||
|
// MinKernelVersion Minimum acceptable version
|
||||||
|
MinKernelVersion string `json:"min_kernel_version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinVersionCheck Posture check for the version of operating system
|
||||||
|
type MinVersionCheck struct {
|
||||||
|
// MinVersion Minimum acceptable version
|
||||||
|
MinVersion string `json:"min_version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NBVersionCheck Posture check for the version of operating system
|
||||||
|
type NBVersionCheck = MinVersionCheck
|
||||||
|
|
||||||
// Nameserver defines model for Nameserver.
|
// Nameserver defines model for Nameserver.
|
||||||
type Nameserver struct {
|
type Nameserver struct {
|
||||||
// Ip Nameserver IP
|
// Ip Nameserver IP
|
||||||
@@ -329,6 +416,24 @@ type NameserverGroupRequest struct {
|
|||||||
SearchDomainsEnabled bool `json:"search_domains_enabled"`
|
SearchDomainsEnabled bool `json:"search_domains_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OSVersionCheck Posture check for the version of operating system
|
||||||
|
type OSVersionCheck struct {
|
||||||
|
// Android Posture check for the version of operating system
|
||||||
|
Android *MinVersionCheck `json:"android,omitempty"`
|
||||||
|
|
||||||
|
// Darwin Posture check for the version of operating system
|
||||||
|
Darwin *MinVersionCheck `json:"darwin,omitempty"`
|
||||||
|
|
||||||
|
// Ios Posture check for the version of operating system
|
||||||
|
Ios *MinVersionCheck `json:"ios,omitempty"`
|
||||||
|
|
||||||
|
// Linux Posture check with the kernel version
|
||||||
|
Linux *MinKernelVersionCheck `json:"linux,omitempty"`
|
||||||
|
|
||||||
|
// Windows Posture check with the kernel version
|
||||||
|
Windows *MinKernelVersionCheck `json:"windows,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Peer defines model for Peer.
|
// Peer defines model for Peer.
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
// AccessiblePeers List of accessible peers
|
// AccessiblePeers List of accessible peers
|
||||||
@@ -337,12 +442,24 @@ type Peer struct {
|
|||||||
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
|
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
|
||||||
ApprovalRequired *bool `json:"approval_required,omitempty"`
|
ApprovalRequired *bool `json:"approval_required,omitempty"`
|
||||||
|
|
||||||
|
// CityName Commonly used English name of the city
|
||||||
|
CityName CityName `json:"city_name"`
|
||||||
|
|
||||||
// Connected Peer to Management connection status
|
// Connected Peer to Management connection status
|
||||||
Connected bool `json:"connected"`
|
Connected bool `json:"connected"`
|
||||||
|
|
||||||
|
// ConnectionIp Peer's public connection IP address
|
||||||
|
ConnectionIp string `json:"connection_ip"`
|
||||||
|
|
||||||
|
// CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country
|
||||||
|
CountryCode CountryCode `json:"country_code"`
|
||||||
|
|
||||||
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
|
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
|
||||||
DnsLabel string `json:"dns_label"`
|
DnsLabel string `json:"dns_label"`
|
||||||
|
|
||||||
|
// GeonameId Unique identifier from the GeoNames database for a specific geographical location.
|
||||||
|
GeonameId int `json:"geoname_id"`
|
||||||
|
|
||||||
// Groups Groups that the peer belongs to
|
// Groups Groups that the peer belongs to
|
||||||
Groups []GroupMinimum `json:"groups"`
|
Groups []GroupMinimum `json:"groups"`
|
||||||
|
|
||||||
@@ -355,6 +472,9 @@ type Peer struct {
|
|||||||
// Ip Peer's IP address
|
// Ip Peer's IP address
|
||||||
Ip string `json:"ip"`
|
Ip string `json:"ip"`
|
||||||
|
|
||||||
|
// KernelVersion Peer's operating system kernel version
|
||||||
|
KernelVersion string `json:"kernel_version"`
|
||||||
|
|
||||||
// LastLogin Last time this peer performed log in (authentication). E.g., user authenticated.
|
// LastLogin Last time this peer performed log in (authentication). E.g., user authenticated.
|
||||||
LastLogin time.Time `json:"last_login"`
|
LastLogin time.Time `json:"last_login"`
|
||||||
|
|
||||||
@@ -377,10 +497,10 @@ type Peer struct {
|
|||||||
SshEnabled bool `json:"ssh_enabled"`
|
SshEnabled bool `json:"ssh_enabled"`
|
||||||
|
|
||||||
// UiVersion Peer's desktop UI version
|
// UiVersion Peer's desktop UI version
|
||||||
UiVersion *string `json:"ui_version,omitempty"`
|
UiVersion string `json:"ui_version"`
|
||||||
|
|
||||||
// UserId User ID of the user that enrolled this peer
|
// UserId User ID of the user that enrolled this peer
|
||||||
UserId *string `json:"user_id,omitempty"`
|
UserId string `json:"user_id"`
|
||||||
|
|
||||||
// Version Peer's daemon or cli version
|
// Version Peer's daemon or cli version
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
@@ -391,12 +511,24 @@ type PeerBase struct {
|
|||||||
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
|
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
|
||||||
ApprovalRequired *bool `json:"approval_required,omitempty"`
|
ApprovalRequired *bool `json:"approval_required,omitempty"`
|
||||||
|
|
||||||
|
// CityName Commonly used English name of the city
|
||||||
|
CityName CityName `json:"city_name"`
|
||||||
|
|
||||||
// Connected Peer to Management connection status
|
// Connected Peer to Management connection status
|
||||||
Connected bool `json:"connected"`
|
Connected bool `json:"connected"`
|
||||||
|
|
||||||
|
// ConnectionIp Peer's public connection IP address
|
||||||
|
ConnectionIp string `json:"connection_ip"`
|
||||||
|
|
||||||
|
// CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country
|
||||||
|
CountryCode CountryCode `json:"country_code"`
|
||||||
|
|
||||||
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
|
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
|
||||||
DnsLabel string `json:"dns_label"`
|
DnsLabel string `json:"dns_label"`
|
||||||
|
|
||||||
|
// GeonameId Unique identifier from the GeoNames database for a specific geographical location.
|
||||||
|
GeonameId int `json:"geoname_id"`
|
||||||
|
|
||||||
// Groups Groups that the peer belongs to
|
// Groups Groups that the peer belongs to
|
||||||
Groups []GroupMinimum `json:"groups"`
|
Groups []GroupMinimum `json:"groups"`
|
||||||
|
|
||||||
@@ -409,6 +541,9 @@ type PeerBase struct {
|
|||||||
// Ip Peer's IP address
|
// Ip Peer's IP address
|
||||||
Ip string `json:"ip"`
|
Ip string `json:"ip"`
|
||||||
|
|
||||||
|
// KernelVersion Peer's operating system kernel version
|
||||||
|
KernelVersion string `json:"kernel_version"`
|
||||||
|
|
||||||
// LastLogin Last time this peer performed log in (authentication). E.g., user authenticated.
|
// LastLogin Last time this peer performed log in (authentication). E.g., user authenticated.
|
||||||
LastLogin time.Time `json:"last_login"`
|
LastLogin time.Time `json:"last_login"`
|
||||||
|
|
||||||
@@ -431,10 +566,10 @@ type PeerBase struct {
|
|||||||
SshEnabled bool `json:"ssh_enabled"`
|
SshEnabled bool `json:"ssh_enabled"`
|
||||||
|
|
||||||
// UiVersion Peer's desktop UI version
|
// UiVersion Peer's desktop UI version
|
||||||
UiVersion *string `json:"ui_version,omitempty"`
|
UiVersion string `json:"ui_version"`
|
||||||
|
|
||||||
// UserId User ID of the user that enrolled this peer
|
// UserId User ID of the user that enrolled this peer
|
||||||
UserId *string `json:"user_id,omitempty"`
|
UserId string `json:"user_id"`
|
||||||
|
|
||||||
// Version Peer's daemon or cli version
|
// Version Peer's daemon or cli version
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
@@ -448,12 +583,24 @@ type PeerBatch struct {
|
|||||||
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
|
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
|
||||||
ApprovalRequired *bool `json:"approval_required,omitempty"`
|
ApprovalRequired *bool `json:"approval_required,omitempty"`
|
||||||
|
|
||||||
|
// CityName Commonly used English name of the city
|
||||||
|
CityName CityName `json:"city_name"`
|
||||||
|
|
||||||
// Connected Peer to Management connection status
|
// Connected Peer to Management connection status
|
||||||
Connected bool `json:"connected"`
|
Connected bool `json:"connected"`
|
||||||
|
|
||||||
|
// ConnectionIp Peer's public connection IP address
|
||||||
|
ConnectionIp string `json:"connection_ip"`
|
||||||
|
|
||||||
|
// CountryCode 2-letter ISO 3166-1 alpha-2 code that represents the country
|
||||||
|
CountryCode CountryCode `json:"country_code"`
|
||||||
|
|
||||||
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
|
// DnsLabel Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud
|
||||||
DnsLabel string `json:"dns_label"`
|
DnsLabel string `json:"dns_label"`
|
||||||
|
|
||||||
|
// GeonameId Unique identifier from the GeoNames database for a specific geographical location.
|
||||||
|
GeonameId int `json:"geoname_id"`
|
||||||
|
|
||||||
// Groups Groups that the peer belongs to
|
// Groups Groups that the peer belongs to
|
||||||
Groups []GroupMinimum `json:"groups"`
|
Groups []GroupMinimum `json:"groups"`
|
||||||
|
|
||||||
@@ -466,6 +613,9 @@ type PeerBatch struct {
|
|||||||
// Ip Peer's IP address
|
// Ip Peer's IP address
|
||||||
Ip string `json:"ip"`
|
Ip string `json:"ip"`
|
||||||
|
|
||||||
|
// KernelVersion Peer's operating system kernel version
|
||||||
|
KernelVersion string `json:"kernel_version"`
|
||||||
|
|
||||||
// LastLogin Last time this peer performed log in (authentication). E.g., user authenticated.
|
// LastLogin Last time this peer performed log in (authentication). E.g., user authenticated.
|
||||||
LastLogin time.Time `json:"last_login"`
|
LastLogin time.Time `json:"last_login"`
|
||||||
|
|
||||||
@@ -488,10 +638,10 @@ type PeerBatch struct {
|
|||||||
SshEnabled bool `json:"ssh_enabled"`
|
SshEnabled bool `json:"ssh_enabled"`
|
||||||
|
|
||||||
// UiVersion Peer's desktop UI version
|
// UiVersion Peer's desktop UI version
|
||||||
UiVersion *string `json:"ui_version,omitempty"`
|
UiVersion string `json:"ui_version"`
|
||||||
|
|
||||||
// UserId User ID of the user that enrolled this peer
|
// UserId User ID of the user that enrolled this peer
|
||||||
UserId *string `json:"user_id,omitempty"`
|
UserId string `json:"user_id"`
|
||||||
|
|
||||||
// Version Peer's daemon or cli version
|
// Version Peer's daemon or cli version
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
@@ -506,6 +656,18 @@ type PeerMinimum struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PeerNetworkRangeCheck Posture check for allow or deny access based on peer local network addresses
|
||||||
|
type PeerNetworkRangeCheck struct {
|
||||||
|
// Action Action to take upon policy match
|
||||||
|
Action PeerNetworkRangeCheckAction `json:"action"`
|
||||||
|
|
||||||
|
// Ranges List of peer network ranges in CIDR notation
|
||||||
|
Ranges []string `json:"ranges"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerNetworkRangeCheckAction Action to take upon policy match
|
||||||
|
type PeerNetworkRangeCheckAction string
|
||||||
|
|
||||||
// PeerRequest defines model for PeerRequest.
|
// PeerRequest defines model for PeerRequest.
|
||||||
type PeerRequest struct {
|
type PeerRequest struct {
|
||||||
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
|
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
|
||||||
@@ -569,6 +731,9 @@ type Policy struct {
|
|||||||
|
|
||||||
// Rules Policy rule object for policy UI editor
|
// Rules Policy rule object for policy UI editor
|
||||||
Rules []PolicyRule `json:"rules"`
|
Rules []PolicyRule `json:"rules"`
|
||||||
|
|
||||||
|
// SourcePostureChecks Posture checks ID's applied to policy source groups
|
||||||
|
SourcePostureChecks []string `json:"source_posture_checks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PolicyMinimum defines model for PolicyMinimum.
|
// PolicyMinimum defines model for PolicyMinimum.
|
||||||
@@ -713,6 +878,36 @@ type PolicyUpdate struct {
|
|||||||
|
|
||||||
// Rules Policy rule object for policy UI editor
|
// Rules Policy rule object for policy UI editor
|
||||||
Rules []PolicyRuleUpdate `json:"rules"`
|
Rules []PolicyRuleUpdate `json:"rules"`
|
||||||
|
|
||||||
|
// SourcePostureChecks Posture checks ID's applied to policy source groups
|
||||||
|
SourcePostureChecks *[]string `json:"source_posture_checks,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostureCheck defines model for PostureCheck.
|
||||||
|
type PostureCheck struct {
|
||||||
|
// Checks List of objects that perform the actual checks
|
||||||
|
Checks Checks `json:"checks"`
|
||||||
|
|
||||||
|
// Description Posture check friendly description
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// Id Posture check ID
|
||||||
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// Name Posture check unique name identifier
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostureCheckUpdate defines model for PostureCheckUpdate.
|
||||||
|
type PostureCheckUpdate struct {
|
||||||
|
// Checks List of objects that perform the actual checks
|
||||||
|
Checks *Checks `json:"checks,omitempty"`
|
||||||
|
|
||||||
|
// Description Posture check friendly description
|
||||||
|
Description string `json:"description"`
|
||||||
|
|
||||||
|
// Name Posture check name identifier
|
||||||
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route defines model for Route.
|
// Route defines model for Route.
|
||||||
@@ -1012,6 +1207,12 @@ type PostApiPoliciesJSONRequestBody = PolicyUpdate
|
|||||||
// PutApiPoliciesPolicyIdJSONRequestBody defines body for PutApiPoliciesPolicyId for application/json ContentType.
|
// PutApiPoliciesPolicyIdJSONRequestBody defines body for PutApiPoliciesPolicyId for application/json ContentType.
|
||||||
type PutApiPoliciesPolicyIdJSONRequestBody = PolicyUpdate
|
type PutApiPoliciesPolicyIdJSONRequestBody = PolicyUpdate
|
||||||
|
|
||||||
|
// PostApiPostureChecksJSONRequestBody defines body for PostApiPostureChecks for application/json ContentType.
|
||||||
|
type PostApiPostureChecksJSONRequestBody = PostureCheckUpdate
|
||||||
|
|
||||||
|
// PutApiPostureChecksPostureCheckIdJSONRequestBody defines body for PutApiPostureChecksPostureCheckId for application/json ContentType.
|
||||||
|
type PutApiPostureChecksPostureCheckIdJSONRequestBody = PostureCheckUpdate
|
||||||
|
|
||||||
// PostApiRoutesJSONRequestBody defines body for PostApiRoutes for application/json ContentType.
|
// PostApiRoutesJSONRequestBody defines body for PostApiRoutes for application/json ContentType.
|
||||||
type PostApiRoutesJSONRequestBody = RouteRequest
|
type PostApiRoutesJSONRequestBody = RouteRequest
|
||||||
|
|
||||||
|
|||||||