Compare commits

...

35 Commits

Author SHA1 Message Date
Maycon Santos
5af9bbfec9 check for closed channel and adjust tests 2024-03-02 01:02:22 +01:00
Maycon Santos
19873db1a9 add comment and adjust log position 2024-02-27 18:55:09 +01:00
Maycon Santos
87d1fc3a2f Handle canceling schedule and avoid recursive call
Using time.Ticker allow us to avoid recursive call that may end up in schedule running and possible deadlock if no routine is listening for cancel calls

switch to closing channel
2024-02-27 18:38:04 +01:00
pascal-fischer
b085419ab8 FIx order when validating account settings (#1632)
* moved extraSettings validation to the end

* moved extraSettings validation directly after permission check
2024-02-27 14:17:22 +01:00
Bethuel Mmbaga
d78b652ff7 Rename PrivateNetworkCheck to PeerNetworkRangeCheck (#1629)
* Rename PrivateNetworkCheck to PeerNetworkRangeCheck

* update description and example

---------

Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
2024-02-27 11:59:48 +01:00
Viktor Liu
7251150c1c Combine update-available and connected/disconnected tray icon states (#1615)
This PR updates the system tray icons to reflect both connection status and availability of updates. Now, the tray will show distinct icons for the following states: connected, disconnected, update available while connected, and update available while disconnected. This change improves user experience by providing a clear visual status indicator.

- Add new icons for connected and disconnected states with update available.
- Implement logic to switch icons based on connection status and update availability.
- Remove old icon references for default and update states.
2024-02-26 23:28:33 +01:00
Bethuel Mmbaga
b65c2f69b0 Add support for downloading Geo databases to the management service (#1626)
Adds support for downloading Geo databases to the management service. If the Geo databases are not found, the service will automatically attempt to download them during startup.
2024-02-26 22:49:28 +01:00
Yury Gargay
d8ce08d898 Extend bypass middleware with support of wildcard paths (#1628)
---------

Co-authored-by: Viktor Liu <viktor@netbird.io>
2024-02-26 17:54:58 +01:00
Maycon Santos
e1c50248d9 Add support for device flow on getting started with zitadel (#1616) 2024-02-26 12:33:16 +01:00
Maycon Santos
ce2d14c08e Update download-geolite2.sh to use packages URLs (#1624)
makes use of our hosted download URLs for geolocation DBs
2024-02-26 10:29:54 +01:00
Misha Bragin
52fd9a575a Add quantum resistance status output (#1608) 2024-02-24 11:41:13 +00:00
Yury Gargay
9028c3c1f7 Check git status after go mod tidy on CI (#1614) 2024-02-23 12:00:43 +01:00
ph1ll
9357a587e9 Make SQLite default in configuration generation script (#1610) 2024-02-23 11:43:11 +01:00
Bethuel Mmbaga
a47c69c472 Add private network posture check (#1606)
* wip: Add PrivateNetworkCheck checks interface implementation

* use generic CheckAction constant

* Add private network check to posture checks

* Fix copy function target in posture checks

* Add network check functionality to posture package

* regenerate the openapi specs

* Update Posture Check actions in test file

* Remove unused function

* Refactor network address handling in PrivateNetworkCheck

* Refactor Prefixes to Ranges in private network checks

* Implement private network checks in posture checks handler tests

* Add test for check copy

* Add gorm serializer for network range
2024-02-22 19:22:43 +03:00
Yury Gargay
bbea4c3cc3 Use SQLite store as default when running tests when env is not set (#1612) 2024-02-22 16:51:56 +01:00
Viktor Liu
b7a6cbfaa5 Add account usage logic (#1567)
---------

Co-authored-by: Yury Gargay <yury.gargay@gmail.com>
2024-02-22 12:27:08 +01:00
pascal-fischer
e18bf565a2 Add permissive mode to rosenpass (#1599)
* add rosenpass-permissive flag

* Clarify rosenpass-permissive flag message

Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>

---------

Co-authored-by: Misha Bragin <bangvalo@gmail.com>
Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
2024-02-21 17:23:17 +01:00
Bethuel Mmbaga
51fa3c92c5 Fix copying function in posture checks process (#1605) 2024-02-21 19:19:13 +03:00
Maycon Santos
d65602f904 Add posture checks metrics report (#1603) 2024-02-21 15:16:43 +01:00
Yury Gargay
8d9e1fed5f Mark new peer meta fields required in OpenAPI spec (#1604) 2024-02-21 15:06:42 +01:00
Bethuel Mmbaga
e1eddd1cab Fix incorrect assignment of SystemSerialNumber and SystemManufacturer (#1600) 2024-02-20 22:50:14 +03:00
Yury Gargay
0fbf72434e Make SQLite default for new installations (#1529)
* Make SQLite default for new installations

* if var is not set, return empty string

this allows getStoreEngineFromDatadir to detect json store files

---------

Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
2024-02-20 15:06:32 +01:00
pascal-fischer
51f133fdc6 Extend system meta (#1598)
* wip: add posture checks structs

* add netbird version check

* Refactor posture checks and add version checks

* Add posture check activities (#1445)

* Integrate Endpoints for Posture Checks (#1432)

* wip: add posture checks structs

* add netbird version check

* Refactor posture checks and add version checks

* Implement posture and version checks in API models

* Refactor API models and enhance posture check functionality

* wip: add posture checks endpoints

* go mod tidy

* Reference the posture checks by id's in policy

* Add posture checks management to server

* Add posture checks management mocks

* implement posture checks handlers

* Add posture checks to account copy and fix tests

* Refactor posture checks validation

* wip: Add posture checks handler tests

* Add JSON encoding support to posture checks

* Encode posture checks to correct api response object

* Refactored posture checks implementation to align with the new API schema

* Refactor structure of `Checks` from slice to map

* Cleanup

* Add posture check activities (#1445)

* Revert map to use list of checks

* Add posture check activity events

* Refactor posture check initialization in account test

* Improve the handling of version range in posture check

* Fix tests and linter

* Remove max_version from NBVersionCheck

* Added unit tests for NBVersionCheck

* go mod tidy

* Extend policy endpoint with posture checks (#1450)

* Implement posture and version checks in API models

* go mod tidy

* Allow attaching posture checks to policy

* Update error message for linked posture check on deleting

* Refactor PostureCheck and Checks structures

* go mod tidy

* Add validation for non-existing posture checks

* fix unit tests

* use Wt version

* Remove the enabled field, as posture check will now automatically be activated by default when attaching to a policy

* wip: add posture checks structs

* add netbird version check

* Refactor posture checks and add version checks

* Add posture check activities (#1445)

* Integrate Endpoints for Posture Checks (#1432)

* wip: add posture checks structs

* add netbird version check

* Refactor posture checks and add version checks

* Implement posture and version checks in API models

* Refactor API models and enhance posture check functionality

* wip: add posture checks endpoints

* go mod tidy

* Reference the posture checks by id's in policy

* Add posture checks management to server

* Add posture checks management mocks

* implement posture checks handlers

* Add posture checks to account copy and fix tests

* Refactor posture checks validation

* wip: Add posture checks handler tests

* Add JSON encoding support to posture checks

* Encode posture checks to correct api response object

* Refactored posture checks implementation to align with the new API schema

* Refactor structure of `Checks` from slice to map

* Cleanup

* Add posture check activities (#1445)

* Revert map to use list of checks

* Add posture check activity events

* Refactor posture check initialization in account test

* Improve the handling of version range in posture check

* Fix tests and linter

* Remove max_version from NBVersionCheck

* Added unit tests for NBVersionCheck

* go mod tidy

* Extend policy endpoint with posture checks (#1450)

* Implement posture and version checks in API models

* go mod tidy

* Allow attaching posture checks to policy

* Update error message for linked posture check on deleting

* Refactor PostureCheck and Checks structures

* go mod tidy

* Add validation for non-existing posture checks

* fix unit tests

* use Wt version

* Remove the enabled field, as posture check will now automatically be activated by default when attaching to a policy

* Extend network map generation with posture checks (#1466)

* Apply posture checks to network map generation

* run policy posture checks on peers to connect

* Refactor and streamline policy posture check process for peers to connect.

* Add posture checks testing in a network map

* Remove redundant nil check in policy.go

* Refactor peer validation check in policy.go

* Update 'Check' function signature and use logger for version check

* Refactor posture checks run on sources and updated the validation func

* Update peer validation

* fix tests

* improved test coverage for policy posture check

* Refactoring

* Extend NetBird agent to collect kernel version (#1495)

* Add KernelVersion field to LoginRequest

* Add KernelVersion to system info retrieval

* Fix tests

* Remove Core field from system info

* Replace Core field with new OSVersion field in system info

* Added WMI dependency to info_windows.go

* Add OS Version posture checks  (#1479)

* Initial support of Geolocation service (#1491)

* Add Geo Location posture check (#1500)

* wip: implement geolocation check

* add geo location posture checks to posture api

* Merge branch 'feature/posture-checks' into geo-posture-check

* Remove CityGeoNameID and update required fields in API

* Add geoLocation checks to posture checks handler tests

* Implement geo location-based checks for peers

* Update test values and embed location struct in peer system

* add support for country wide checks

* initialize country code regex once

* Fix peer meta core compability with older clients (#1515)

* Refactor extraction of OSVersion in grpcserver

* Ignore lint check

* Fix peer meta core compability with older management (#1532)

* Revert core field deprecation

* fix tests

* Extend peer meta with location information (#1517)

This PR uses the geolocation service to resolve IP to location. 
The lookup happens once on the first connection - when a client calls the Sync func.
The location is stored as part of the peer:

* Add Locations endpoints (#1516)

* add locations endpoints

* Add sqlite3 check and database generation in geolite script

* Add SQLite storage for geolocation data

* Refactor file existence check into a separate function

* Integrate geolocation services into management application

* Refactoring

* Refactor city retrieval to include Geonames ID

* Add signature verification for GeoLite2 database download

* Change to in-memory database for geolocation store

* Merge manager to geolocation

* Update GetAllCountries to return Country name and iso code

* fix tests

* Add reload to SqliteStore

* Add geoname indexes

* move db file check to connectDB

* Add concurrency safety to SQL queries and database reloading

The commit adds mutex locks to the GetAllCountries and GetCitiesByCountry functions to ensure thread-safety during database queries. Additionally, it introduces a mechanism to safely close the old database connection before a new connection is established upon reloading, which improves the reliability of database operations. Lastly, it moves the checking of database file existence to the connectDB function.

* Add sha256 sum check to geolocation store before reload

* Use read lock

* Check SHA256 twice when reload geonames db

---------

Co-authored-by: Yury Gargay <yury.gargay@gmail.com>

* Add tests and validation for empty peer location in GeoLocationCheck (#1546)

* Disallow Geo check creation/update without configured Geo DB (#1548)

* Fix shared access to in memory copy of geonames.db (#1550)

* Trim suffix in when evaluate Min Kernel Version in OS check

* Add Valid Peer Windows Kernel version test

* Add Geolocation handler tests (#1556)

* Implement user admin checks in posture checks

* Add geolocation handler tests

* Mark initGeolocationTestData as helper func

* Add error handling to geolocation database closure

* Add cleanup function to close geolocation resources

* Simplify checks definition serialisation (#1555)

* Regenerate network map on posture check update (#1563)

* change network state and generate map on posture check update

* Refactoring

* Make city name optional (#1575)

* Do not return empty city name

* Validate action param of geo location checks (#1577)

We only support allow and deny

* Switch realip middleware to upstream (#1578)

* Be more silent in download-geolite2.sh script

* Fix geonames db reload (#1580)

* Ensure posture check name uniqueness when create (#1594)

* Enhance the management of posture checks (#1595)

* add a correct min version and kernel for os posture check example

* handle error when geo or location db is nil

* expose all peer location details in api response

* Check for nil geolocation manager only

* Validate posture check before save

* bump open api version

* add peer location fields to toPeerListItemResponse

* Feautre/extend sys meta (#1536)

* Collect network addresses

* Add Linux sys product info

* Fix peer meta comparison

* Collect sys info on mac

* Add windows sys info

* Fix test

* Fix test

* Fix grpc client

* Ignore test

* Fix test

* Collect IPv6 addresses

* Change the IP to IP + net

* fix tests

* Use netip on server side

* Serialize netip to json

* Extend Peer metadata with cloud detection (#1552)

* add cloud detection + test binary

* test windows exe

* Collect IPv6 addresses

* Change the IP to IP + net

* switch to forked cloud detect lib

* new test builds

* new GCE build

* discontinue using library but local copy instead

* fix imports

* remove openstack check

* add hierarchy to cloud check

* merge IBM and SoftLayer

* close resp bodies and use os lib for file reading

* close more resp bodies

* fix error check logic

* parallelize IBM checks

* fix response value

* go mod tidy

* include context + change kubernetes detection

* add context in info functions

* extract platform into separate field

* fix imports

* add missing wmi import

---------

Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>

---------

Co-authored-by: pascal-fischer <32096965+pascal-fischer@users.noreply.github.com>

* generate proto

* remove test binaries

---------

Co-authored-by: bcmmbaga <bethuelmbaga12@gmail.com>
Co-authored-by: Yury Gargay <yury.gargay@gmail.com>
Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>
2024-02-20 11:53:11 +01:00
charnesp
d5338c09dc Disable SSH server by default on client side and add the flag --allow-server-ssh to enable it (#1508)
This changes the default behavior for new peers, by requiring the agent to be executed with allow-server-ssh set to true in order for the management configuration to take effect.
2024-02-20 11:13:27 +01:00
Oskar Manhart
8fd4166c53 feat: add --disable-auto-connectflag to prevent auto connection after daemon service start (fixes #444, fixes #1382) (#1161)
With these changes, the command up supports the flag --disable-auto-connect that allows users to disable auto connection on the client after a computer restart or when the daemon restarts.
2024-02-20 10:10:05 +01:00
Yury Gargay
9bc7b9e897 Add initial support of device posture checks (#1540)
This PR implements the following posture checks:

* Agent minimum version allowed
* OS minimum version allowed
* Geo-location based on connection IP

For the geo-based location, we rely on GeoLite2 databases which are free IP geolocation databases. MaxMind was tested and we provide a script that easily allows to download of all necessary files, see infrastructure_files/download-geolite2.sh.

The OpenAPI spec should extensively cover the life cycle of current version posture checks.
2024-02-20 09:59:56 +01:00
Yury Gargay
db3cba5e0f Remove Account.Rules from Store engines (#1528) 2024-02-19 17:17:36 +01:00
Maycon Santos
cb3408a10b Allow adding 3 nameserver addresses (#1588) 2024-02-19 14:29:20 +01:00
Viktor Liu
0afd738509 Make sure the iOS dialer does not get overwritten (#1585)
* Make sure our iOS dialer does not get overwritten

* set dial timeout for both clients on ios

---------

Co-authored-by: Pascal Fischer <pascal@netbird.io>
2024-02-16 14:37:47 +01:00
Maycon Santos
cf87f1e702 Fix/prevent returning error from external cache (#1576)
* Prevent returning error from external cache query

* link comment

* fix spell and remove unnecessary return
2024-02-13 13:10:17 +01:00
Maycon Santos
e890fdae54 Return error when peer is not valid (#1573)
Fix count with invalid peers
2024-02-13 10:59:31 +01:00
Maycon Santos
dd14db6478 Properly handle cache error and return userdata (#1571) 2024-02-12 21:54:16 +01:00
Maycon Santos
88747e3e01 Add an extra server reflexive candidate with WG port (#1549)
sends an extra server reflexive candidate to the remote peer with our related port (usually the Wireguard port)
this is useful when a network has an existing port forwarding rule for the Wireguard port and the local peer and avoids creating a 1:1 NAT on the local network.
2024-02-08 16:50:37 +01:00
Yury Gargay
fb30931365 Expose trusted proxy list and counter configuration for realip middleware (#1535) 2024-02-08 14:40:40 +01:00
Maycon Santos
a7547b9990 Get cache from external cache when refresh fails (#1537)
In some cases, when the refresh cache fails, we should try to get the cache from the external cache obj.

This may happen if the IDP is not responsive between storing metadata and refreshing the cache
2024-02-07 16:14:30 +01:00
137 changed files with 8877 additions and 1046 deletions

View File

@@ -35,5 +35,8 @@ jobs:
- name: Install modules
run: go mod tidy
- name: check git status
run: git --no-pager diff --exit-code
- 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 ./...

View File

@@ -41,8 +41,11 @@ jobs:
- name: Install modules
run: go mod tidy
- name: check git status
run: git --no-pager diff --exit-code
- 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:
runs-on: ubuntu-20.04
@@ -69,6 +72,9 @@ jobs:
- name: Install modules
run: go mod tidy
- name: check git status
run: git --no-pager diff --exit-code
- name: Generate Iface Test bin
run: CGO_ENABLED=0 go test -c -o iface-testing.bin ./iface/

View File

@@ -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 GOCACHE=C:\Users\runneradmin\AppData\Local\go-build
- run: "[Environment]::SetEnvironmentVariable('NETBIRD_STORE_ENGINE', 'jsonfile', 'Machine')"
- 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"

View File

@@ -190,6 +190,9 @@ jobs:
-
name: Install modules
run: go mod tidy
-
name: check git status
run: git --no-pager diff --exit-code
-
name: Run GoReleaser
id: goreleaser

View File

@@ -127,6 +127,9 @@ jobs:
- name: Install modules
run: go mod tidy
- name: check git status
run: git --no-pager diff --exit-code
- name: Build management binary
working-directory: management
run: CGO_ENABLED=1 go build -o netbird-mgmt main.go
@@ -186,3 +189,16 @@ jobs:
run: test -f zitadel.env
- name: test dashboard.env file gen
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
View File

@@ -29,4 +29,4 @@ infrastructure_files/setup.env
infrastructure_files/setup-*.env
.vscode
.DS_Store
*.db
GeoLite2-City*

View File

@@ -54,7 +54,7 @@ nfpms:
contents:
- src: client/ui/netbird.desktop
dst: /usr/share/applications/netbird.desktop
- src: client/ui/netbird-systemtray-default.png
- src: client/ui/netbird-systemtray-connected.png
dst: /usr/share/pixmaps/netbird.png
dependencies:
- netbird
@@ -71,7 +71,7 @@ nfpms:
contents:
- src: client/ui/netbird.desktop
dst: /usr/share/applications/netbird.desktop
- src: client/ui/netbird-systemtray-default.png
- src: client/ui/netbird-systemtray-connected.png
dst: /usr/share/pixmaps/netbird.png
dependencies:
- netbird

View File

@@ -79,6 +79,7 @@ func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsRead
return err
}
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
c.recorder.UpdateRosenpass(cfg.RosenpassEnabled, cfg.RosenpassPermissive)
var ctx context.Context
//nolint
@@ -109,6 +110,7 @@ func (c *Client) RunWithoutLogin(dns *DNSList, dnsReadyListener DnsReadyListener
return err
}
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
c.recorder.UpdateRosenpass(cfg.RosenpassEnabled, cfg.RosenpassPermissive)
var ctx context.Context
//nolint

View File

@@ -25,12 +25,15 @@ import (
)
const (
externalIPMapFlag = "external-ip-map"
dnsResolverAddress = "dns-resolver-address"
enableRosenpassFlag = "enable-rosenpass"
preSharedKeyFlag = "preshared-key"
interfaceNameFlag = "interface-name"
wireguardPortFlag = "wireguard-port"
externalIPMapFlag = "external-ip-map"
dnsResolverAddress = "dns-resolver-address"
enableRosenpassFlag = "enable-rosenpass"
rosenpassPermissiveFlag = "rosenpass-permissive"
preSharedKeyFlag = "preshared-key"
interfaceNameFlag = "interface-name"
wireguardPortFlag = "wireguard-port"
disableAutoConnectFlag = "disable-auto-connect"
serverSSHAllowedFlag = "allow-server-ssh"
)
var (
@@ -54,8 +57,11 @@ var (
natExternalIPs []string
customDNSAddress string
rosenpassEnabled bool
rosenpassPermissive bool
serverSSHAllowed bool
interfaceName string
wireguardPort uint16
autoConnectDisabled bool
rootCmd = &cobra.Command{
Use: "netbird",
Short: "",
@@ -126,6 +132,9 @@ func init() {
`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(&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
@@ -176,7 +185,7 @@ func FlagNameToEnvVar(cmdFlag string, prefix string) string {
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) {
ctx, cancel := context.WithTimeout(ctx, time.Second*3)
defer cancel()

View File

@@ -77,6 +77,7 @@ var installCmd = &cobra.Command{
cmd.PrintErrln(err)
return err
}
cmd.Println("Netbird service has been installed")
return nil
},
@@ -106,7 +107,7 @@ var uninstallCmd = &cobra.Command{
if err != nil {
return err
}
cmd.Println("Netbird has been uninstalled")
cmd.Println("Netbird service has been uninstalled")
return nil
},
}

View File

@@ -34,6 +34,7 @@ type peerStateDetailOutput struct {
LastWireguardHandshake time.Time `json:"lastWireguardHandshake" yaml:"lastWireguardHandshake"`
TransferReceived int64 `json:"transferReceived" yaml:"transferReceived"`
TransferSent int64 `json:"transferSent" yaml:"transferSent"`
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
}
type peersStateOutput struct {
@@ -72,16 +73,18 @@ type iceCandidateType struct {
}
type statusOutputOverview struct {
Peers peersStateOutput `json:"peers" yaml:"peers"`
CliVersion string `json:"cliVersion" yaml:"cliVersion"`
DaemonVersion string `json:"daemonVersion" yaml:"daemonVersion"`
ManagementState managementStateOutput `json:"management" yaml:"management"`
SignalState signalStateOutput `json:"signal" yaml:"signal"`
Relays relayStateOutput `json:"relays" yaml:"relays"`
IP string `json:"netbirdIp" yaml:"netbirdIp"`
PubKey string `json:"publicKey" yaml:"publicKey"`
KernelInterface bool `json:"usesKernelInterface" yaml:"usesKernelInterface"`
FQDN string `json:"fqdn" yaml:"fqdn"`
Peers peersStateOutput `json:"peers" yaml:"peers"`
CliVersion string `json:"cliVersion" yaml:"cliVersion"`
DaemonVersion string `json:"daemonVersion" yaml:"daemonVersion"`
ManagementState managementStateOutput `json:"management" yaml:"management"`
SignalState signalStateOutput `json:"signal" yaml:"signal"`
Relays relayStateOutput `json:"relays" yaml:"relays"`
IP string `json:"netbirdIp" yaml:"netbirdIp"`
PubKey string `json:"publicKey" yaml:"publicKey"`
KernelInterface bool `json:"usesKernelInterface" yaml:"usesKernelInterface"`
FQDN string `json:"fqdn" yaml:"fqdn"`
RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"`
RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"`
}
var (
@@ -253,16 +256,18 @@ func convertToStatusOutputOverview(resp *proto.StatusResponse) statusOutputOverv
peersOverview := mapPeers(resp.GetFullStatus().GetPeers())
overview := statusOutputOverview{
Peers: peersOverview,
CliVersion: version.NetbirdVersion(),
DaemonVersion: resp.GetDaemonVersion(),
ManagementState: managementOverview,
SignalState: signalOverview,
Relays: relayOverview,
IP: pbFullStatus.GetLocalPeerState().GetIP(),
PubKey: pbFullStatus.GetLocalPeerState().GetPubKey(),
KernelInterface: pbFullStatus.GetLocalPeerState().GetKernelInterface(),
FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(),
Peers: peersOverview,
CliVersion: version.NetbirdVersion(),
DaemonVersion: resp.GetDaemonVersion(),
ManagementState: managementOverview,
SignalState: signalOverview,
Relays: relayOverview,
IP: pbFullStatus.GetLocalPeerState().GetIP(),
PubKey: pbFullStatus.GetLocalPeerState().GetPubKey(),
KernelInterface: pbFullStatus.GetLocalPeerState().GetKernelInterface(),
FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(),
RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(),
RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(),
}
return overview
@@ -346,6 +351,7 @@ func mapPeers(peers []*proto.PeerState) peersStateOutput {
LastWireguardHandshake: lastHandshake,
TransferReceived: transferReceived,
TransferSent: transferSent,
RosenpassEnabled: pbPeerState.GetRosenpassEnabled(),
}
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)
rosenpassEnabledStatus := "false"
if overview.RosenpassEnabled {
rosenpassEnabledStatus = "true"
if overview.RosenpassPermissive {
rosenpassEnabledStatus = "true (permissive)" //nolint:gosec
}
}
summary := fmt.Sprintf(
"Daemon version: %s\n"+
"CLI version: %s\n"+
@@ -460,6 +474,7 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays
"FQDN: %s\n"+
"NetBird IP: %s\n"+
"Interface type: %s\n"+
"Quantum resistance: %s\n"+
"Peers count: %s\n",
overview.DaemonVersion,
version.NetbirdVersion(),
@@ -469,13 +484,14 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays
overview.FQDN,
interfaceIP,
interfaceTypeString,
rosenpassEnabledStatus,
peersCountString,
)
return summary
}
func parseToFullDetailSummary(overview statusOutputOverview) string {
parsedPeersString := parsePeers(overview.Peers)
parsedPeersString := parsePeers(overview.Peers, overview.RosenpassEnabled, overview.RosenpassPermissive)
summary := parseGeneralSummary(overview, true, true)
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 (
peersString = ""
)
@@ -518,9 +534,26 @@ func parsePeers(peers peersStateOutput) string {
lastStatusUpdate = peerState.LastStatusUpdate.Format("2006-01-02 15:04:05")
}
lastWireguardHandshake := "-"
lastWireGuardHandshake := "-"
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(
@@ -534,8 +567,9 @@ func parsePeers(peers peersStateOutput) string {
" ICE candidate (Local/Remote): %s/%s\n"+
" ICE candidate endpoints (Local/Remote): %s/%s\n"+
" Last connection update: %s\n"+
" Last Wireguard handshake: %s\n"+
" Transfer status (received/sent) %s/%s\n",
" Last WireGuard handshake: %s\n"+
" Transfer status (received/sent) %s/%s\n"+
" Quantum resistance: %s\n",
peerState.FQDN,
peerState.IP,
peerState.PubKey,
@@ -547,9 +581,10 @@ func parsePeers(peers peersStateOutput) string {
localICEEndpoint,
remoteICEEndpoint,
lastStatusUpdate,
lastWireguardHandshake,
lastWireGuardHandshake,
toIEC(peerState.TransferReceived),
toIEC(peerState.TransferSent),
rosenpassEnabledStatus,
)
peersString += peerString

View File

@@ -231,7 +231,8 @@ func TestParsingToJSON(t *testing.T) {
},
"lastWireguardHandshake": "2001-01-01T01:01:02Z",
"transferReceived": 200,
"transferSent": 100
"transferSent": 100,
"quantumResistance":false
},
{
"fqdn": "peer-2.awesome-domain.com",
@@ -251,7 +252,8 @@ func TestParsingToJSON(t *testing.T) {
},
"lastWireguardHandshake": "2002-02-02T02:02:03Z",
"transferReceived": 2000,
"transferSent": 1000
"transferSent": 1000,
"quantumResistance":false
}
]
},
@@ -286,7 +288,9 @@ func TestParsingToJSON(t *testing.T) {
"netbirdIp": "192.168.178.100/16",
"publicKey": "Some-Pub-Key",
"usesKernelInterface": true,
"fqdn": "some-localhost.awesome-domain.com"
"fqdn": "some-localhost.awesome-domain.com",
"quantumResistance":false,
"quantumResistancePermissive":false
}`
// @formatter:on
@@ -320,6 +324,7 @@ func TestParsingToYAML(t *testing.T) {
lastWireguardHandshake: 2001-01-01T01:01:02Z
transferReceived: 200
transferSent: 100
quantumResistance: false
- fqdn: peer-2.awesome-domain.com
netbirdIp: 192.168.178.102
publicKey: Pubkey2
@@ -336,6 +341,7 @@ func TestParsingToYAML(t *testing.T) {
lastWireguardHandshake: 2002-02-02T02:02:03Z
transferReceived: 2000
transferSent: 1000
quantumResistance: false
cliVersion: development
daemonVersion: 0.14.1
management:
@@ -360,6 +366,8 @@ netbirdIp: 192.168.178.100/16
publicKey: Some-Pub-Key
usesKernelInterface: true
fqdn: some-localhost.awesome-domain.com
quantumResistance: false
quantumResistancePermissive: false
`
assert.Equal(t, expectedYAML, yaml)
@@ -380,8 +388,9 @@ func TestParsingToDetail(t *testing.T) {
ICE candidate (Local/Remote): -/-
ICE candidate endpoints (Local/Remote): -/-
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
Quantum resistance: false
peer-2.awesome-domain.com:
NetBird IP: 192.168.178.102
@@ -393,8 +402,9 @@ func TestParsingToDetail(t *testing.T) {
ICE candidate (Local/Remote): relay/prflx
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 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
Quantum resistance: false
Daemon version: 0.14.1
CLI version: development
@@ -406,6 +416,7 @@ Relays:
FQDN: some-localhost.awesome-domain.com
NetBird IP: 192.168.178.100/16
Interface type: Kernel
Quantum resistance: false
Peers count: 2/2 Connected
`
@@ -424,6 +435,7 @@ Relays: 1/2 Available
FQDN: some-localhost.awesome-domain.com
NetBird IP: 192.168.178.100/16
Interface type: Kernel
Quantum resistance: false
Peers count: 2/2 Connected
`

View File

@@ -78,8 +78,7 @@ func startManagement(t *testing.T, config *mgmt.Config) (*grpc.Server, net.Liste
if err != nil {
return nil, nil
}
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "",
eventStore, false)
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false)
if err != nil {
t.Fatal(err)
}

View File

@@ -94,6 +94,14 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
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 err := parseInterfaceName(interfaceName); err != nil {
return err
@@ -110,6 +118,18 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
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)
if err != nil {
return fmt.Errorf("get config file: %v", err)
@@ -180,6 +200,18 @@ func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
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 err := parseInterfaceName(interfaceName); err != nil {
return err

View File

@@ -35,15 +35,18 @@ var defaultInterfaceBlacklist = []string{iface.WgInterfaceDefault, "wt", "utun",
// ConfigInput carries configuration changes to the client
type ConfigInput struct {
ManagementURL string
AdminURL string
ConfigPath string
PreSharedKey *string
NATExternalIPs []string
CustomDNSAddress []byte
RosenpassEnabled *bool
InterfaceName *string
WireguardPort *int
ManagementURL string
AdminURL string
ConfigPath string
PreSharedKey *string
ServerSSHAllowed *bool
NATExternalIPs []string
CustomDNSAddress []byte
RosenpassEnabled *bool
RosenpassPermissive *bool
InterfaceName *string
WireguardPort *int
DisableAutoConnect *bool
}
// Config Configuration type
@@ -58,6 +61,8 @@ type Config struct {
IFaceBlackList []string
DisableIPv6Discovery bool
RosenpassEnabled bool
RosenpassPermissive bool
ServerSSHAllowed *bool
// SSHKey is a private SSH key in a PEM format
SSHKey string
@@ -79,6 +84,10 @@ type Config struct {
NATExternalIPs []string
// CustomDNSAddress sets the DNS resolver listening address in format ip:port
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
@@ -88,6 +97,7 @@ func ReadConfig(configPath string) (*Config, error) {
if _, err := util.ReadJson(configPath, config); err != nil {
return nil, err
}
return config, nil
}
@@ -152,6 +162,8 @@ func createNewConfig(input ConfigInput) (*Config, error) {
DisableIPv6Discovery: false,
NATExternalIPs: input.NATExternalIPs,
CustomDNSAddress: string(input.CustomDNSAddress),
ServerSSHAllowed: util.False(),
DisableAutoConnect: false,
}
defaultManagementURL, err := parseURL("Management URL", DefaultManagementURL)
@@ -186,6 +198,14 @@ func createNewConfig(input ConfigInput) (*Config, error) {
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)
if err != nil {
return nil, err
@@ -280,6 +300,26 @@ func update(input ConfigInput) (*Config, error) {
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 {
// since we have new management URL, we need to update config file
if err := util.WriteJson(input.ConfigPath, config); err != nil {

View File

@@ -23,6 +23,7 @@ import (
mgm "github.com/netbirdio/netbird/management/client"
mgmProto "github.com/netbirdio/netbird/management/proto"
signal "github.com/netbirdio/netbird/signal/client"
"github.com/netbirdio/netbird/util"
"github.com/netbirdio/netbird/version"
)
@@ -283,6 +284,8 @@ func createEngineConfig(key wgtypes.Key, config *Config, peerConfig *mgmProto.Pe
NATExternalIPs: config.NATExternalIPs,
CustomDNSAddress: config.CustomDNSAddress,
RosenpassEnabled: config.RosenpassEnabled,
RosenpassPermissive: config.RosenpassPermissive,
ServerSSHAllowed: util.ReturnBoolWithDefaultTrue(config.ServerSSHAllowed),
}
if config.PreSharedKey != "" {

View File

@@ -46,24 +46,32 @@ func (u *upstreamResolverIOS) exchange(ctx context.Context, upstream string, r *
if err != nil {
log.Errorf("error while parsing upstream host: %s", err)
}
timeout := upstreamTimeout
if deadline, ok := ctx.Deadline(); ok {
timeout = time.Until(deadline)
}
client.DialTimeout = timeout
upstreamIP := net.ParseIP(upstreamHost)
if u.lNet.Contains(upstreamIP) || net.IP.IsPrivate(upstreamIP) {
log.Debugf("using private client to query upstream: %s", upstream)
client = u.getClientPrivate()
client = u.getClientPrivate(timeout)
}
return client.ExchangeContext(ctx, r, upstream)
// Cannot use client.ExchangeContext because it overwrites our Dialer
return client.Exchange(r, upstream)
}
// getClientPrivate returns a new DNS client bound to the local IP address of the Netbird interface
// This method is needed for iOS
func (u *upstreamResolverIOS) getClientPrivate() *dns.Client {
func (u *upstreamResolverIOS) getClientPrivate(dialTimeout time.Duration) *dns.Client {
dialer := &net.Dialer{
LocalAddr: &net.UDPAddr{
IP: u.lIP,
Port: 0, // Let the OS pick a free port
},
Timeout: upstreamTimeout,
Timeout: dialTimeout,
Control: func(network, address string, c syscall.RawConn) error {
var operr error
fn := func(s uintptr) {

View File

@@ -79,7 +79,10 @@ type EngineConfig struct {
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.
@@ -234,6 +237,11 @@ func (e *Engine) Start() error {
if e.config.RosenpassEnabled {
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)
if err != nil {
return err
@@ -482,44 +490,52 @@ func isNil(server nbssh.Server) bool {
}
func (e *Engine) updateSSH(sshConf *mgmProto.SSHConfig) error {
if sshConf.GetSshEnabled() {
if runtime.GOOS == "windows" {
log.Warnf("running SSH server on Windows is not supported")
return nil
}
// start SSH server if it wasn't running
if isNil(e.sshServer) {
// 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 {
return err
if !e.config.ServerSSHAllowed {
log.Warnf("running SSH server is not permitted")
return nil
} else {
if sshConf.GetSshEnabled() {
if runtime.GOOS == "windows" {
log.Warnf("running SSH server on Windows is not supported")
return nil
}
go func() {
// blocking
err = e.sshServer.Start()
// start SSH server if it wasn't running
if isNil(e.sshServer) {
// 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 {
// will throw error when we stop it even if it is a graceful stop
log.Debugf("stopped SSH server with error %v", err)
return err
}
e.syncMsgMux.Lock()
defer e.syncMsgMux.Unlock()
e.sshServer = nil
log.Infof("stopped SSH server")
}()
} else {
log.Debugf("SSH server is already running")
go func() {
// blocking
err = e.sshServer.Start()
if err != nil {
// will throw error when we stop it even if it is a graceful stop
log.Debugf("stopped SSH server with error %v", err)
}
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) {
// 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
}
return nil
}
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,
}
if e.config.RosenpassEnabled {
if e.config.RosenpassEnabled && !e.config.RosenpassPermissive {
lk := []byte(e.config.WgPrivateKey.PublicKey().String())
rk := []byte(wgConfig.RemoteKey)
var keyInput []byte
@@ -1270,7 +1286,7 @@ func (e *Engine) receiveProbeEvents() {
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
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)
}
}

View File

@@ -74,6 +74,7 @@ func TestEngine_SSH(t *testing.T) {
WgAddr: "100.64.0.1/24",
WgPrivateKey: key,
WgPort: 33100,
ServerSSHAllowed: true,
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
engine.dnsServer = &dns.MockServer{
@@ -1049,8 +1050,7 @@ func startManagement(dataDir string) (*grpc.Server, string, error) {
if err != nil {
return nil, "", err
}
accountManager, err := server.BuildManager(store, peersUpdateManager, nil, "", "",
eventStore, false)
accountManager, err := server.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false)
if err != nil {
return nil, "", err
}

View File

@@ -130,8 +130,9 @@ type Conn struct {
remoteModeCh chan ModeMessage
meta meta
adapter iface.TunAdapter
iFaceDiscover stdnet.ExternalIFaceDiscover
adapter iface.TunAdapter
iFaceDiscover stdnet.ExternalIFaceDiscover
sentExtraSrflx bool
}
// meta holds meta information about a connection
@@ -406,6 +407,10 @@ func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int, rem
}
conn.status = StatusConnected
rosenpassEnabled := false
if remoteRosenpassPubKey != nil {
rosenpassEnabled = true
}
peerState := State{
PubKey: conn.config.Key,
@@ -416,6 +421,7 @@ func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int, rem
LocalIceCandidateEndpoint: fmt.Sprintf("%s:%d", pair.Local.Address(), pair.Local.Port()),
RemoteIceCandidateEndpoint: fmt.Sprintf("%s:%d", pair.Remote.Address(), pair.Local.Port()),
Direct: !isRelayCandidate(pair.Local),
RosenpassEnabled: rosenpassEnabled,
}
if pair.Local.Type() == ice.CandidateTypeRelay || pair.Remote.Type() == ice.CandidateTypeRelay {
peerState.Relayed = true
@@ -464,6 +470,8 @@ func (conn *Conn) cleanup() error {
conn.mu.Lock()
defer conn.mu.Unlock()
conn.sentExtraSrflx = false
var err1, err2, err3 error
if conn.agent != nil {
err1 = conn.agent.Close()
@@ -502,7 +510,7 @@ func (conn *Conn) cleanup() error {
// todo rethink status updates
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)
}
@@ -557,6 +565,30 @@ func (conn *Conn) onICECandidate(candidate ice.Candidate) {
if err != nil {
log.Errorf("failed signaling candidate to the remote peer %s %s", conn.config.Key, err)
}
// sends an extra server reflexive candidate to the remote peer with our related port (usually the wireguard port)
// this is useful when network has an existing port forwarding rule for the wireguard port and this peer
if !conn.sentExtraSrflx && candidate.Type() == ice.CandidateTypeServerReflexive && candidate.Port() != candidate.RelatedAddress().Port {
relatedAdd := candidate.RelatedAddress()
extraSrflx, err := ice.NewCandidateServerReflexive(&ice.CandidateServerReflexiveConfig{
Network: candidate.NetworkType().String(),
Address: candidate.Address(),
Port: relatedAdd.Port,
Component: candidate.Component(),
RelAddr: relatedAdd.Address,
RelPort: relatedAdd.Port,
})
if err != nil {
log.Errorf("failed creating extra server reflexive candidate %s", err)
return
}
err = conn.signalCandidate(extraSrflx)
if err != nil {
log.Errorf("failed signaling the extra server reflexive candidate to the remote peer %s: %s", conn.config.Key, err)
return
}
conn.sentExtraSrflx = true
}
}()
}
}

View File

@@ -25,6 +25,7 @@ type State struct {
LastWireguardHandshake time.Time
BytesTx int64
BytesRx int64
RosenpassEnabled bool
}
// LocalPeerState contains the latest state of the local peer
@@ -49,30 +50,39 @@ type ManagementState struct {
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
type FullStatus struct {
Peers []State
ManagementState ManagementState
SignalState SignalState
LocalPeerState LocalPeerState
RosenpassState RosenpassState
Relays []relay.ProbeResult
}
// Status holds a state of peers, signal, management connections and relays
type Status struct {
mux sync.Mutex
peers map[string]State
changeNotify map[string]chan struct{}
signalState bool
signalError error
managementState bool
managementError error
relayStates []relay.ProbeResult
localPeer LocalPeerState
offlinePeers []State
mgmAddress string
signalAddress string
notifier *notifier
mux sync.Mutex
peers map[string]State
changeNotify map[string]chan struct{}
signalState bool
signalError error
managementState bool
managementError error
relayStates []relay.ProbeResult
localPeer LocalPeerState
offlinePeers []State
mgmAddress string
signalAddress string
notifier *notifier
rosenpassEnabled bool
rosenpassPermissive bool
// 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
@@ -172,6 +182,7 @@ func (d *Status) UpdatePeerState(receivedState State) error {
peerState.RemoteIceCandidateType = receivedState.RemoteIceCandidateType
peerState.LocalIceCandidateEndpoint = receivedState.LocalIceCandidateEndpoint
peerState.RemoteIceCandidateEndpoint = receivedState.RemoteIceCandidateEndpoint
peerState.RosenpassEnabled = receivedState.RosenpassEnabled
}
d.peers[receivedState.PubKey] = peerState
@@ -190,8 +201,8 @@ func (d *Status) UpdatePeerState(receivedState State) error {
return nil
}
// UpdateWireguardPeerState updates the wireguard bits of the peer state
func (d *Status) UpdateWireguardPeerState(pubKey string, wgStats iface.WGStats) error {
// UpdateWireGuardPeerState updates the WireGuard bits of the peer state
func (d *Status) UpdateWireGuardPeerState(pubKey string, wgStats iface.WGStats) error {
d.mux.Lock()
defer d.mux.Unlock()
@@ -316,6 +327,14 @@ func (d *Status) UpdateManagementAddress(mgmAddress string) {
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
func (d *Status) MarkSignalDisconnected(err error) {
d.mux.Lock()
@@ -342,6 +361,13 @@ func (d *Status) UpdateRelayStates(relayResults []relay.ProbeResult) {
d.relayStates = relayResults
}
func (d *Status) GetRosenpassState() RosenpassState {
return RosenpassState{
d.rosenpassEnabled,
d.rosenpassPermissive,
}
}
func (d *Status) GetManagementState() ManagementState {
return ManagementState{
d.mgmAddress,
@@ -372,6 +398,7 @@ func (d *Status) GetFullStatus() FullStatus {
SignalState: d.GetSignalState(),
LocalPeerState: d.localPeer,
Relays: d.GetRelayStates(),
RosenpassState: d.GetRosenpassState(),
}
for _, status := range d.peers {

View File

@@ -82,6 +82,7 @@ func (c *Client) Run(fd int32, interfaceName string) error {
return err
}
c.recorder.UpdateManagementAddress(cfg.ManagementURL.String())
c.recorder.UpdateRosenpass(cfg.RosenpassEnabled, cfg.RosenpassPermissive)
var ctx context.Context
//nolint

View File

@@ -1,16 +1,16 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.21.9
// protoc v3.12.4
// source: daemon.proto
package proto
import (
_ "github.com/golang/protobuf/protoc-gen-go/descriptor"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
_ "google.golang.org/protobuf/types/descriptorpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
)
@@ -51,6 +51,9 @@ type LoginRequest struct {
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"`
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() {
@@ -177,6 +180,27 @@ func (x *LoginRequest) GetOptionalPreSharedKey() string {
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 {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -733,20 +757,21 @@ type PeerState struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
IP string `protobuf:"bytes,1,opt,name=IP,proto3" json:"IP,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"`
ConnStatusUpdate *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=connStatusUpdate,proto3" json:"connStatusUpdate,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"`
LocalIceCandidateType string `protobuf:"bytes,7,opt,name=localIceCandidateType,proto3" json:"localIceCandidateType,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"`
LocalIceCandidateEndpoint string `protobuf:"bytes,10,opt,name=localIceCandidateEndpoint,proto3" json:"localIceCandidateEndpoint,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"`
BytesRx int64 `protobuf:"varint,13,opt,name=bytesRx,proto3" json:"bytesRx,omitempty"`
BytesTx int64 `protobuf:"varint,14,opt,name=bytesTx,proto3" json:"bytesTx,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"`
ConnStatus string `protobuf:"bytes,3,opt,name=connStatus,proto3" json:"connStatus,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"`
Direct bool `protobuf:"varint,6,opt,name=direct,proto3" json:"direct,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"`
Fqdn string `protobuf:"bytes,9,opt,name=fqdn,proto3" json:"fqdn,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"`
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"`
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() {
@@ -802,7 +827,7 @@ func (x *PeerState) GetConnStatus() string {
return ""
}
func (x *PeerState) GetConnStatusUpdate() *timestamppb.Timestamp {
func (x *PeerState) GetConnStatusUpdate() *timestamp.Timestamp {
if x != nil {
return x.ConnStatusUpdate
}
@@ -858,7 +883,7 @@ func (x *PeerState) GetRemoteIceCandidateEndpoint() string {
return ""
}
func (x *PeerState) GetLastWireguardHandshake() *timestamppb.Timestamp {
func (x *PeerState) GetLastWireguardHandshake() *timestamp.Timestamp {
if x != nil {
return x.LastWireguardHandshake
}
@@ -879,16 +904,25 @@ func (x *PeerState) GetBytesTx() int64 {
return 0
}
func (x *PeerState) GetRosenpassEnabled() bool {
if x != nil {
return x.RosenpassEnabled
}
return false
}
// LocalPeerState contains the latest state of the local peer
type LocalPeerState struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
IP string `protobuf:"bytes,1,opt,name=IP,proto3" json:"IP,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"`
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,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"`
KernelInterface bool `protobuf:"varint,3,opt,name=kernelInterface,proto3" json:"kernelInterface,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() {
@@ -951,6 +985,20 @@ func (x *LocalPeerState) GetFqdn() string {
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
type SignalState struct {
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,
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,
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,
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,
@@ -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,
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,
0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x72, 0x6f, 0x73, 0x65, 0x6e,
0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f,
0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x10, 0x0a,
0x0e, 0x5f, 0x77, 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, 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,
0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
0x65, 0x41, 0x75, 0x74, 0x6f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x18, 0x0e, 0x20, 0x01,
0x28, 0x08, 0x48, 0x04, 0x52, 0x12, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x75, 0x74,
0x6f, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x10, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x53, 0x48, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18,
0x0f, 0x20, 0x01, 0x28, 0x08, 0x48, 0x05, 0x52, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53,
0x53, 0x48, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x13,
0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
0x69, 0x76, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x48, 0x06, 0x52, 0x13, 0x72, 0x6f, 0x73,
0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65,
0x88, 0x01, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73,
0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x69, 0x6e, 0x74,
0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x77,
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,
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, 0x65,
0x22, 0x4d, 0x0a, 0x13, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43,
0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x43,
0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x22,
0x16, 0x0a, 0x14, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0b, 0x0a, 0x09, 0x55, 0x70, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x22, 0x0c, 0x0a, 0x0a, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x3d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x65,
0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11,
0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75,
0x73, 0x22, 0x82, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a, 0x0a,
0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x12, 0x24, 0x0a, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x0d, 0x0a, 0x0b, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb3, 0x01, 0x0a, 0x11, 0x47, 0x65,
0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x72, 0x6c,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46,
0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c, 0x65,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 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,
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64,
0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, 0x4c, 0x18,
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, 0x4c, 0x22,
0xd5, 0x04, 0x0a, 0x09, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a,
0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a,
0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70,
0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53,
0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x6f, 0x6e,
0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a,
0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63,
0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x12,
0x34, 0x0a, 0x15, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69,
0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74,
0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49,
0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18,
0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65,
0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a,
0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64,
0x6e, 0x12, 0x3c, 0x0a, 0x19, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e,
0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61,
0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12,
0x3e, 0x0a, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64,
0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0b, 0x20,
0x01, 0x28, 0x09, 0x52, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65, 0x43, 0x61,
0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12,
0x52, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64,
0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x16, 0x6c, 0x61, 0x73,
0x74, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68,
0x61, 0x6b, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x78, 0x18, 0x0d,
0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x78, 0x12, 0x18, 0x0a,
0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07,
0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x22, 0x76, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x6c,
0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62,
0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65,
0x79, 0x12, 0x28, 0x0a, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72,
0x66, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6b, 0x65, 0x72, 0x6e,
0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66,
0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22,
0x53, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10,
0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c,
0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20,
0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14,
0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
0x72, 0x72, 0x6f, 0x72, 0x22, 0x57, 0x0a, 0x0f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e,
0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f,
0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a,
0x0a, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55,
0x52, 0x49, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x49, 0x12, 0x1c, 0x0a,
0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08,
0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65,
0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f,
0x72, 0x22, 0x9b, 0x02, 0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x12, 0x41, 0x0a, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74,
0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x61, 0x65, 0x6d,
0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61,
0x74, 0x65, 0x52, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74,
0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61,
0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x73,
0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x63, 0x61,
0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61,
0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x65,
0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d,
0x6f, 0x6e, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x70, 0x65,
0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x05, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x6c,
0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x32,
0xf7, 0x02, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x12, 0x36, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65,
0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69,
0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d,
0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64,
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12,
0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
0x12, 0x33, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
0x6e, 0x2e, 0x44, 0x6f, 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,
0x65, 0x22, 0x4d, 0x0a, 0x13, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69,
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72,
0x43, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72,
0x43, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65,
0x22, 0x16, 0x0a, 0x14, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0b, 0x0a, 0x09, 0x55, 0x70, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0c, 0x0a, 0x0a, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x3d, 0x0a, 0x0d, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50,
0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
0x11, 0x67, 0x65, 0x74, 0x46, 0x75, 0x6c, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x32, 0x0a,
0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x75, 0x6c, 0x6c, 0x53,
0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x0a, 0x66, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75,
0x73, 0x12, 0x24, 0x0a, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x0d, 0x0a, 0x0b, 0x44, 0x6f, 0x77, 0x6e, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x12, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb3, 0x01, 0x0a, 0x11, 0x47,
0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x24, 0x0a, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x55, 0x72,
0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x46, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x67, 0x46, 0x69, 0x6c,
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 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, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x65, 0x53, 0x68, 0x61, 0x72, 0x65,
0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, 0x4c,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x55, 0x52, 0x4c,
0x22, 0x81, 0x05, 0x0a, 0x09, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e,
0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16,
0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e,
0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x63, 0x6f,
0x6e, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x18,
0x0a, 0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
0x07, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x72, 0x65,
0x63, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
0x12, 0x34, 0x0a, 0x15, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64,
0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
0x15, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61,
0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65,
0x49, 0x63, 0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65,
0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63,
0x65, 0x43, 0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12,
0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71,
0x64, 0x6e, 0x12, 0x3c, 0x0a, 0x19, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43, 0x61,
0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18,
0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x63, 0x65, 0x43,
0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x12, 0x3e, 0x0a, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65, 0x43, 0x61, 0x6e,
0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x0b,
0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x63, 0x65, 0x43,
0x61, 0x6e, 0x64, 0x69, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x12, 0x52, 0x0a, 0x16, 0x6c, 0x61, 0x73, 0x74, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72,
0x64, 0x48, 0x61, 0x6e, 0x64, 0x73, 0x68, 0x61, 0x6b, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x16, 0x6c, 0x61,
0x73, 0x74, 0x57, 0x69, 0x72, 0x65, 0x67, 0x75, 0x61, 0x72, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x73,
0x68, 0x61, 0x6b, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x78, 0x18,
0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x52, 0x78, 0x12, 0x18,
0x0a, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x03, 0x52,
0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73, 0x65,
0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01,
0x28, 0x08, 0x52, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x22, 0xd4, 0x01, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65,
0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65,
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12,
0x28, 0x0a, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61,
0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c,
0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64,
0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x12, 0x2a, 0x0a,
0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65,
0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61,
0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73,
0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65,
0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73,
0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x22, 0x53, 0x0a, 0x0b, 0x53,
0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52,
0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09,
0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
0x22, 0x57, 0x0a, 0x0f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74,
0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x0a, 0x52, 0x65, 0x6c,
0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x49, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x49, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61,
0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x76,
0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x9b, 0x02,
0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x41, 0x0a, 0x0f,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12,
0x35, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x69,
0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61,
0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50,
0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65,
0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65,
0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18,
0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x50,
0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12,
0x2a, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x74,
0x61, 0x74, 0x65, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x32, 0xf7, 0x02, 0x0a, 0x0d,
0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a,
0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x64,
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f,
0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57,
0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74,
0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x61,
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x15, 0x2e, 0x64, 0x61,
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04,
0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f,
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 (
@@ -1438,25 +1509,25 @@ func file_daemon_proto_rawDescGZIP() []byte {
var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 18)
var file_daemon_proto_goTypes = []interface{}{
(*LoginRequest)(nil), // 0: daemon.LoginRequest
(*LoginResponse)(nil), // 1: daemon.LoginResponse
(*WaitSSOLoginRequest)(nil), // 2: daemon.WaitSSOLoginRequest
(*WaitSSOLoginResponse)(nil), // 3: daemon.WaitSSOLoginResponse
(*UpRequest)(nil), // 4: daemon.UpRequest
(*UpResponse)(nil), // 5: daemon.UpResponse
(*StatusRequest)(nil), // 6: daemon.StatusRequest
(*StatusResponse)(nil), // 7: daemon.StatusResponse
(*DownRequest)(nil), // 8: daemon.DownRequest
(*DownResponse)(nil), // 9: daemon.DownResponse
(*GetConfigRequest)(nil), // 10: daemon.GetConfigRequest
(*GetConfigResponse)(nil), // 11: daemon.GetConfigResponse
(*PeerState)(nil), // 12: daemon.PeerState
(*LocalPeerState)(nil), // 13: daemon.LocalPeerState
(*SignalState)(nil), // 14: daemon.SignalState
(*ManagementState)(nil), // 15: daemon.ManagementState
(*RelayState)(nil), // 16: daemon.RelayState
(*FullStatus)(nil), // 17: daemon.FullStatus
(*timestamppb.Timestamp)(nil), // 18: google.protobuf.Timestamp
(*LoginRequest)(nil), // 0: daemon.LoginRequest
(*LoginResponse)(nil), // 1: daemon.LoginResponse
(*WaitSSOLoginRequest)(nil), // 2: daemon.WaitSSOLoginRequest
(*WaitSSOLoginResponse)(nil), // 3: daemon.WaitSSOLoginResponse
(*UpRequest)(nil), // 4: daemon.UpRequest
(*UpResponse)(nil), // 5: daemon.UpResponse
(*StatusRequest)(nil), // 6: daemon.StatusRequest
(*StatusResponse)(nil), // 7: daemon.StatusResponse
(*DownRequest)(nil), // 8: daemon.DownRequest
(*DownResponse)(nil), // 9: daemon.DownResponse
(*GetConfigRequest)(nil), // 10: daemon.GetConfigRequest
(*GetConfigResponse)(nil), // 11: daemon.GetConfigResponse
(*PeerState)(nil), // 12: daemon.PeerState
(*LocalPeerState)(nil), // 13: daemon.LocalPeerState
(*SignalState)(nil), // 14: daemon.SignalState
(*ManagementState)(nil), // 15: daemon.ManagementState
(*RelayState)(nil), // 16: daemon.RelayState
(*FullStatus)(nil), // 17: daemon.FullStatus
(*timestamp.Timestamp)(nil), // 18: google.protobuf.Timestamp
}
var file_daemon_proto_depIdxs = []int32{
17, // 0: daemon.StatusResponse.fullStatus:type_name -> daemon.FullStatus

View File

@@ -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
// 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.
string managementUrl = 3;
@@ -63,6 +63,12 @@ message LoginRequest {
optional int64 wireguardPort = 12;
optional string optionalPreSharedKey = 13;
optional bool disableAutoConnect = 14;
optional bool serverSSHAllowed = 15;
optional bool rosenpassPermissive = 16;
}
message LoginResponse {
@@ -134,6 +140,7 @@ message PeerState {
google.protobuf.Timestamp lastWireguardHandshake = 12;
int64 bytesRx = 13;
int64 bytesTx = 14;
bool rosenpassEnabled = 15;
}
// LocalPeerState contains the latest state of the local peer
@@ -142,6 +149,8 @@ message LocalPeerState {
string pubKey = 2;
bool kernelInterface = 3;
string fqdn = 4;
bool rosenpassEnabled = 5;
bool rosenpassPermissive = 6;
}
// SignalState contains the latest state of a signal connection

View File

@@ -112,15 +112,17 @@ func (s *Server) Start() error {
if s.statusRecorder == nil {
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 err := internal.RunClientWithProbes(ctx, config, s.statusRecorder, s.mgmProbe, s.signalProbe, s.relayProbe, s.wgProbe); err != nil {
log.Errorf("init connections: %v", err)
}
}()
if !config.DisableAutoConnect {
go func() {
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
}
@@ -204,6 +206,21 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro
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 {
inputConfig.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 {
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() {
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 {
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 {
s.runProbes()
@@ -550,6 +567,8 @@ func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
pbFullStatus.LocalPeerState.PubKey = fullStatus.LocalPeerState.PubKey
pbFullStatus.LocalPeerState.KernelInterface = fullStatus.LocalPeerState.KernelInterface
pbFullStatus.LocalPeerState.Fqdn = fullStatus.LocalPeerState.FQDN
pbFullStatus.LocalPeerState.RosenpassPermissive = fullStatus.RosenpassState.Permissive
pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled
for _, peerState := range fullStatus.Peers {
pbPeerState := &proto.PeerState{
@@ -567,6 +586,7 @@ func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
LastWireguardHandshake: timestamppb.New(peerState.LastWireguardHandshake),
BytesRx: peerState.BytesRx,
BytesTx: peerState.BytesTx,
RosenpassEnabled: peerState.RosenpassEnabled,
}
pbFullStatus.Peers = append(pbFullStatus.Peers, pbPeerState)
}

View 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 ""
}

View 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
}

View 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 ""
}

View 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 ""
}

View 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 ""
}

View 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 ""
}

View 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
}

View 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
}

View 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 ""
}

View 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 ""
}

View 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 ""
}

View 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 ""
}

View 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 ""
}

View File

@@ -2,6 +2,8 @@ package system
import (
"context"
"net"
"net/netip"
"strings"
"google.golang.org/grpc/metadata"
@@ -18,12 +20,21 @@ const OsVersionCtxKey = "OsVersion"
// OsNameCtxKey context key for operating system name
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
// Most of the code is taken from https://github.com/matishsiao/goInfo
type Info struct {
GoOS string
Kernel string
Core string
Platform string
OS string
OSVersion string
@@ -31,6 +42,12 @@ type Info struct {
CPUs int
WiretrusteeVersion 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
@@ -62,3 +79,53 @@ func extractDeviceName(ctx context.Context, defaultName string) string {
func GetDesktopUIUserAgent() string {
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
}

View File

@@ -23,7 +23,12 @@ func GetInfo(ctx context.Context) *Info {
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.WiretrusteeVersion = version.NetbirdVersion()
gio.UIVersion = extractUserAgent(ctx)

View File

@@ -15,6 +15,8 @@ import (
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"
)
@@ -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)
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()
gio.Hostname = extractDeviceName(ctx, systemHostname)
gio.WiretrusteeVersion = version.NetbirdVersion()
@@ -41,3 +70,31 @@ func GetInfo(ctx context.Context) *Info {
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, `"`)
}

View 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)
}

View File

@@ -10,6 +10,8 @@ import (
"strings"
"time"
"github.com/netbirdio/netbird/client/system/detect_cloud"
"github.com/netbirdio/netbird/client/system/detect_platform"
"github.com/netbirdio/netbird/version"
)
@@ -23,7 +25,14 @@ func GetInfo(ctx context.Context) *Info {
osStr := strings.Replace(out, "\n", "", -1)
osStr = strings.Replace(osStr, "\r\n", "", -1)
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()
gio.Hostname = extractDeviceName(ctx, systemHostname)
gio.WiretrusteeVersion = version.NetbirdVersion()

View File

@@ -17,7 +17,7 @@ func GetInfo(ctx context.Context) *Info {
sysName := extractOsName(ctx, "sysName")
swVersion := extractOsVersion(ctx, "swVersion")
gio := &Info{Kernel: sysName, OSVersion: swVersion, Core: swVersion, Platform: "unknown", OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
gio := &Info{Kernel: sysName, OSVersion: swVersion, Platform: "unknown", OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU(), KernelVersion: swVersion}
gio.Hostname = extractDeviceName(ctx, "hostname")
gio.WiretrusteeVersion = version.NetbirdVersion()
gio.UIVersion = extractUserAgent(ctx)

View File

@@ -13,7 +13,10 @@ import (
"time"
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"
)
@@ -50,11 +53,38 @@ func GetInfo(ctx context.Context) *Info {
if osName == "" {
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()
gio.Hostname = extractDeviceName(ctx, systemHostname)
gio.WiretrusteeVersion = version.NetbirdVersion()
gio.UIVersion = extractUserAgent(ctx)
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: 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
}
@@ -86,3 +116,9 @@ func _getReleaseInfo() 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
}

View File

@@ -33,3 +33,13 @@ func Test_CustomHostname(t *testing.T) {
got := GetInfo(ctx)
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")
}
}

View File

@@ -11,6 +11,8 @@ import (
"github.com/yusufpapurcu/wmi"
"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"
)
@@ -18,11 +20,63 @@ type Win32_OperatingSystem struct {
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
func GetInfo(ctx context.Context) *Info {
osName, osVersion := getOSNameAndVersion()
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()
gio.Hostname = extractDeviceName(ctx, systemHostname)
gio.WiretrusteeVersion = version.NetbirdVersion()
@@ -93,3 +147,33 @@ func getBuildVersion() string {
ver := fmt.Sprintf("%d.%d.%s.%d", major, minor, build, ubr)
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
}

View 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)
}

View File

@@ -82,17 +82,23 @@ var iconConnectedICO []byte
//go:embed netbird-systemtray-connected.png
var iconConnectedPNG []byte
//go:embed netbird-systemtray-default.ico
//go:embed netbird-systemtray-disconnected.ico
var iconDisconnectedICO []byte
//go:embed netbird-systemtray-default.png
//go:embed netbird-systemtray-disconnected.png
var iconDisconnectedPNG []byte
//go:embed netbird-systemtray-update.ico
var iconUpdateICO []byte
//go:embed netbird-systemtray-update-disconnected.ico
var iconUpdateDisconnectedICO []byte
//go:embed netbird-systemtray-update.png
var iconUpdatePNG []byte
//go:embed netbird-systemtray-update-disconnected.png
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
var iconUpdateCloudICO []byte
@@ -105,10 +111,11 @@ type serviceClient struct {
addr string
conn proto.DaemonServiceClient
icConnected []byte
icDisconnected []byte
icUpdate []byte
icUpdateCloud []byte
icConnected []byte
icDisconnected []byte
icUpdateConnected []byte
icUpdateDisconnected []byte
icUpdateCloud []byte
// systray menu items
mStatus *systray.MenuItem
@@ -139,6 +146,7 @@ type serviceClient struct {
preSharedKey string
adminURL string
connected bool
update *version.Update
daemonVersion string
updateIndicationLock sync.Mutex
@@ -161,13 +169,15 @@ func newServiceClient(addr string, a fyne.App, showSettings bool) *serviceClient
if runtime.GOOS == "windows" {
s.icConnected = iconConnectedICO
s.icDisconnected = iconDisconnectedICO
s.icUpdate = iconUpdateICO
s.icUpdateConnected = iconUpdateConnectedICO
s.icUpdateDisconnected = iconUpdateDisconnectedICO
s.icUpdateCloud = iconUpdateCloudICO
} else {
s.icConnected = iconConnectedPNG
s.icDisconnected = iconDisconnectedPNG
s.icUpdate = iconUpdatePNG
s.icUpdateConnected = iconUpdateConnectedPNG
s.icUpdateDisconnected = iconUpdateDisconnectedPNG
s.icUpdateCloud = iconUpdateCloudPNG
}
@@ -369,7 +379,10 @@ func (s *serviceClient) updateStatus() error {
var systrayIconState bool
if status.Status == string(internal.StatusConnected) && !s.mUp.Disabled() {
if !s.isUpdateIconActive {
s.connected = true
if s.isUpdateIconActive {
systray.SetIcon(s.icUpdateConnected)
} else {
systray.SetIcon(s.icConnected)
}
systray.SetTooltip("NetBird (Connected)")
@@ -378,7 +391,10 @@ func (s *serviceClient) updateStatus() error {
s.mDown.Enable()
systrayIconState = true
} 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.SetTooltip("NetBird (Disconnected)")
@@ -605,10 +621,13 @@ func (s *serviceClient) onUpdateAvailable() {
defer s.updateIndicationLock.Unlock()
s.mUpdate.Show()
s.mAbout.SetIcon(s.icUpdateCloud)
s.isUpdateIconActive = true
systray.SetIcon(s.icUpdate)
if s.connected {
systray.SetIcon(s.icUpdateConnected)
} else {
systray.SetIcon(s.icUpdateDisconnected)
}
}
func openURL(url string) error {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

8
go.mod
View File

@@ -47,7 +47,7 @@ require (
github.com/google/go-cmp v0.5.9
github.com/google/gopacket v1.1.19
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-version v1.6.0
github.com/libp2p/go-netroute v0.2.0
@@ -57,9 +57,10 @@ require (
github.com/miekg/dns v1.1.43
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/nadoo/ipset v0.5.0
github.com/netbirdio/management-integrations/additions v0.0.0-20240118163419-8a7c87accb22
github.com/netbirdio/management-integrations/integrations v0.0.0-20240118163419-8a7c87accb22
github.com/netbirdio/management-integrations/additions v0.0.0-20240212121739-8ea8c89a4552
github.com/netbirdio/management-integrations/integrations v0.0.0-20240212121739-8ea8c89a4552
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/pion/logging v0.2.2
github.com/pion/stun/v2 v2.0.0
@@ -71,6 +72,7 @@ require (
github.com/stretchr/testify v1.8.4
github.com/things-go/go-socks5 v0.0.4
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/exporters/prometheus v0.33.0
go.opentelemetry.io/otel/metric v0.33.0

16
go.sum
View File

@@ -286,8 +286,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/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240202184442-37827591b26c h1:Kvw2BIua5WGDnknpnODn9K74PYWLhhqt8G3l0chyzEI=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.2-0.20240202184442-37827591b26c/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4=
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/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=
@@ -376,10 +376,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/nadoo/ipset v0.5.0 h1:5GJUAuZ7ITQQQGne5J96AmFjRtI8Avlbk6CabzYWVUc=
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-20240118163419-8a7c87accb22/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-20240118163419-8a7c87accb22/go.mod h1:B0nMS3es77gOvPYhc0K91fAzTkQLi/jRq5TffUN3klM=
github.com/netbirdio/management-integrations/additions v0.0.0-20240212121739-8ea8c89a4552 h1:yzcQKizAK9YufCHMMCIsr467Dw/OU/4xyHbWizGb1E4=
github.com/netbirdio/management-integrations/additions v0.0.0-20240212121739-8ea8c89a4552/go.mod h1:31FhBNvQ+riHEIu6LSTmqr8IeuSIsGfQffqV4LFmbwA=
github.com/netbirdio/management-integrations/integrations v0.0.0-20240212121739-8ea8c89a4552 h1:OFlzVZtkXCoJsfDKrMigFpuad8ZXTm8epq6x27K0irA=
github.com/netbirdio/management-integrations/integrations v0.0.0-20240212121739-8ea8c89a4552/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/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/netbirdio/systray v0.0.0-20231030152038-ef1ed2a27949 h1:xbWM9BU6mwZZLHxEjxIX/V8Hv3HurQt4mReIE4mY4DM=
@@ -409,6 +409,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.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
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/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
@@ -539,6 +541,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/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
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.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=

View File

@@ -62,7 +62,7 @@ NETBIRD_DASH_AUTH_USE_AUDIENCE=${NETBIRD_DASH_AUTH_USE_AUDIENCE:-true}
NETBIRD_DASH_AUTH_AUDIENCE=$NETBIRD_AUTH_AUDIENCE
# Store config
NETBIRD_STORE_CONFIG_ENGINE=${NETBIRD_STORE_CONFIG_ENGINE:-"jsonfile"}
NETBIRD_STORE_CONFIG_ENGINE=${NETBIRD_STORE_CONFIG_ENGINE:-"sqlite"}
# Image tags
NETBIRD_DASHBOARD_TAG=${NETBIRD_DASHBOARD_TAG:-"latest"}

View 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"

View File

@@ -137,6 +137,13 @@ create_new_application() {
BASE_REDIRECT_URL2=$5
LOGOUT_URL=$6
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=$(
curl -sS -X POST "$INSTANCE_URL/management/v1/projects/$PROJECT_ID/apps/oidc" \
@@ -154,10 +161,7 @@ create_new_application() {
"RESPONSETypes": [
"OIDC_RESPONSE_TYPE_CODE"
],
"grantTypes": [
"OIDC_GRANT_TYPE_AUTHORIZATION_CODE",
"OIDC_GRANT_TYPE_REFRESH_TOKEN"
],
"grantTypes": '"$GRANT_TYPES"',
"appType": "OIDC_APP_TYPE_USER_AGENT",
"authMethodType": "OIDC_AUTH_METHOD_TYPE_NONE",
"version": "OIDC_VERSION_1_0",
@@ -340,10 +344,10 @@ init_zitadel() {
# create zitadel spa applications
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"
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")
@@ -561,6 +565,8 @@ renderCaddyfile() {
reverse_proxy /.well-known/openid-configuration h2c://zitadel:8080
reverse_proxy /openapi/* h2c://zitadel:8080
reverse_proxy /debug/* h2c://zitadel:8080
reverse_proxy /device/* h2c://zitadel:8080
reverse_proxy /device h2c://zitadel:8080
# Dashboard
reverse_proxy /* dashboard:80
}
@@ -629,6 +635,14 @@ renderManagementJson() {
"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": {
"ProviderConfig": {
"Audience": "$NETBIRD_AUTH_CLIENT_ID_CLI",

View File

@@ -60,8 +60,7 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "",
eventStore, false)
accountManager, err := mgmt.BuildManager(store, peersUpdateManager, nil, "", "", eventStore, nil, false)
if err != nil {
t.Fatal(err)
}
@@ -344,18 +343,71 @@ func Test_SystemMetaDataFromClient(t *testing.T) {
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{
Hostname: info.Hostname,
GoOS: info.GoOS,
Kernel: info.Kernel,
Core: info.OSVersion,
Platform: info.Platform,
OS: info.OS,
Core: info.OSVersion,
OSVersion: info.OSVersion,
WiretrusteeVersion: info.WiretrusteeVersion,
KernelVersion: info.KernelVersion,
NetworkAddresses: protoNetAddr,
SysSerialNumber: info.SystemSerialNumber,
SysProductName: info.SystemProductName,
SysManufacturer: info.SystemManufacturer,
}
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()
}
func Test_GetDeviceAuthorizationFlow(t *testing.T) {

View File

@@ -450,14 +450,29 @@ func infoToMetaData(info *system.Info) *proto.PeerSystemMeta {
if info == 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{
Hostname: info.Hostname,
GoOS: info.GoOS,
OS: info.OS,
Core: info.OSVersion,
OSVersion: info.OSVersion,
Platform: info.Platform,
Kernel: info.Kernel,
WiretrusteeVersion: info.WiretrusteeVersion,
UiVersion: info.UIVersion,
KernelVersion: info.KernelVersion,
NetworkAddresses: addresses,
SysSerialNumber: info.SystemSerialNumber,
SysManufacturer: info.SystemManufacturer,
SysProductName: info.SystemProductName,
}
}

View File

@@ -15,11 +15,10 @@ import (
"net/url"
"os"
"path"
"slices"
"strings"
"time"
"github.com/netbirdio/management-integrations/integrations"
"github.com/google/uuid"
"github.com/miekg/dns"
log "github.com/sirupsen/logrus"
@@ -32,9 +31,12 @@ import (
"google.golang.org/grpc/keepalive"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/realip"
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/encryption"
mgmtProto "github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/geolocation"
httpapi "github.com/netbirdio/netbird/management/server/http"
"github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/netbird/management/server/jwtclaims"
@@ -162,29 +164,46 @@ 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)
}
accountManager, err := server.BuildManager(store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain,
dnsDomain, eventStore, userDeleteFromIDPEnabled)
dnsDomain, eventStore, geo, userDeleteFromIDPEnabled)
if err != nil {
return fmt.Errorf("failed to build default manager: %v", err)
}
turnManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig)
trustedPeers := config.TrustedHTTPProxies
if len(trustedPeers) == 0 {
trustedPeers = []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")}
trustedPeers := config.ReverseProxy.TrustedPeers
defaultTrustedPeers := []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0")}
if len(trustedPeers) == 0 || slices.Equal[[]netip.Prefix](trustedPeers, defaultTrustedPeers) {
log.Warn("TrustedPeers are configured to default value '0.0.0.0/0', '::/0'. This allows connection IP spoofing.")
trustedPeers = defaultTrustedPeers
}
trustedHTTPProxies := config.ReverseProxy.TrustedHTTPProxies
trustedProxiesCount := config.ReverseProxy.TrustedHTTPProxiesCount
if len(trustedHTTPProxies) > 0 && trustedProxiesCount > 0 {
log.Warn("TrustedHTTPProxies and TrustedHTTPProxiesCount both are configured. " +
"This is not recommended way to extract X-Forwarded-For. Consider using one of these options.")
}
realipOpts := []realip.Option{
realip.WithTrustedPeers(trustedPeers),
realip.WithTrustedProxies(trustedHTTPProxies),
realip.WithTrustedProxiesCount(trustedProxiesCount),
realip.WithHeaders([]string{realip.XForwardedFor, realip.XRealIp}),
}
headers := []string{realip.XForwardedFor, realip.XRealIp}
gRPCOpts := []grpc.ServerOption{
grpc.KeepaliveEnforcementPolicy(kaep),
grpc.KeepaliveParams(kasp),
grpc.ChainUnaryInterceptor(
realip.UnaryServerInterceptor(trustedPeers, headers),
),
grpc.ChainStreamInterceptor(
realip.StreamServerInterceptor(trustedPeers, headers),
),
grpc.ChainUnaryInterceptor(realip.UnaryServerInterceptorOpts(realipOpts...)),
grpc.ChainStreamInterceptor(realip.StreamServerInterceptorOpts(realipOpts...)),
}
var certManager *autocert.Manager
var tlsConfig *tls.Config
tlsEnabled := false
@@ -223,7 +242,10 @@ var (
UserIDClaim: config.HttpConfig.AuthUserIDClaim,
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 {
return fmt.Errorf("failed creating HTTP API handler: %v", err)
}
@@ -245,8 +267,6 @@ var (
}
if !disableMetrics {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
idpManager := "disabled"
if config.IdpManagerConfig != nil && config.IdpManagerConfig.ManagerType != "" {
idpManager = config.IdpManagerConfig.ManagerType
@@ -301,6 +321,9 @@ var (
SetupCloseHandler()
<-stopCh
if geo != nil {
_ = geo.Stop()
}
ephemeralManager.Stop()
_ = appMetrics.Close()
_ = listener.Close()

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.21.9
// protoc v4.24.3
// source: management.proto
package proto
@@ -595,14 +595,20 @@ type PeerSystemMeta struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"`
GoOS string `protobuf:"bytes,2,opt,name=goOS,proto3" json:"goOS,omitempty"`
Kernel string `protobuf:"bytes,3,opt,name=kernel,proto3" json:"kernel,omitempty"`
Core string `protobuf:"bytes,4,opt,name=core,proto3" json:"core,omitempty"`
Platform string `protobuf:"bytes,5,opt,name=platform,proto3" json:"platform,omitempty"`
OS string `protobuf:"bytes,6,opt,name=OS,proto3" json:"OS,omitempty"`
WiretrusteeVersion string `protobuf:"bytes,7,opt,name=wiretrusteeVersion,proto3" json:"wiretrusteeVersion,omitempty"`
UiVersion string `protobuf:"bytes,8,opt,name=uiVersion,proto3" json:"uiVersion,omitempty"`
Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"`
GoOS string `protobuf:"bytes,2,opt,name=goOS,proto3" json:"goOS,omitempty"`
Kernel string `protobuf:"bytes,3,opt,name=kernel,proto3" json:"kernel,omitempty"`
Core string `protobuf:"bytes,4,opt,name=core,proto3" json:"core,omitempty"`
Platform string `protobuf:"bytes,5,opt,name=platform,proto3" json:"platform,omitempty"`
OS string `protobuf:"bytes,6,opt,name=OS,proto3" json:"OS,omitempty"`
WiretrusteeVersion string `protobuf:"bytes,7,opt,name=wiretrusteeVersion,proto3" json:"wiretrusteeVersion,omitempty"`
UiVersion string `protobuf:"bytes,8,opt,name=uiVersion,proto3" json:"uiVersion,omitempty"`
KernelVersion string `protobuf:"bytes,9,opt,name=kernelVersion,proto3" json:"kernelVersion,omitempty"`
OSVersion string `protobuf:"bytes,10,opt,name=OSVersion,proto3" json:"OSVersion,omitempty"`
NetworkAddresses []*NetworkAddress `protobuf:"bytes,11,rep,name=networkAddresses,proto3" json:"networkAddresses,omitempty"`
SysSerialNumber string `protobuf:"bytes,12,opt,name=sysSerialNumber,proto3" json:"sysSerialNumber,omitempty"`
SysProductName string `protobuf:"bytes,13,opt,name=sysProductName,proto3" json:"sysProductName,omitempty"`
SysManufacturer string `protobuf:"bytes,14,opt,name=sysManufacturer,proto3" json:"sysManufacturer,omitempty"`
}
func (x *PeerSystemMeta) Reset() {
@@ -693,6 +699,48 @@ func (x *PeerSystemMeta) GetUiVersion() string {
return ""
}
func (x *PeerSystemMeta) GetKernelVersion() string {
if x != nil {
return x.KernelVersion
}
return ""
}
func (x *PeerSystemMeta) GetOSVersion() string {
if x != nil {
return x.OSVersion
}
return ""
}
func (x *PeerSystemMeta) GetNetworkAddresses() []*NetworkAddress {
if x != nil {
return x.NetworkAddresses
}
return nil
}
func (x *PeerSystemMeta) GetSysSerialNumber() string {
if x != nil {
return x.SysSerialNumber
}
return ""
}
func (x *PeerSystemMeta) GetSysProductName() string {
if x != nil {
return x.SysProductName
}
return ""
}
func (x *PeerSystemMeta) GetSysManufacturer() string {
if x != nil {
return x.SysManufacturer
}
return ""
}
type LoginResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -2209,6 +2257,61 @@ func (x *FirewallRule) GetPort() string {
return ""
}
type NetworkAddress struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
NetIP string `protobuf:"bytes,1,opt,name=netIP,proto3" json:"netIP,omitempty"`
Mac string `protobuf:"bytes,2,opt,name=mac,proto3" json:"mac,omitempty"`
}
func (x *NetworkAddress) Reset() {
*x = NetworkAddress{}
if protoimpl.UnsafeEnabled {
mi := &file_management_proto_msgTypes[28]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *NetworkAddress) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*NetworkAddress) ProtoMessage() {}
func (x *NetworkAddress) ProtoReflect() protoreflect.Message {
mi := &file_management_proto_msgTypes[28]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use NetworkAddress.ProtoReflect.Descriptor instead.
func (*NetworkAddress) Descriptor() ([]byte, []int) {
return file_management_proto_rawDescGZIP(), []int{28}
}
func (x *NetworkAddress) GetNetIP() string {
if x != nil {
return x.NetIP
}
return ""
}
func (x *NetworkAddress) GetMac() string {
if x != nil {
return x.Mac
}
return ""
}
var File_management_proto protoreflect.FileDescriptor
var file_management_proto_rawDesc = []byte{
@@ -2257,7 +2360,7 @@ var file_management_proto_rawDesc = []byte{
0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12,
0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0xe6, 0x01, 0x0a, 0x0e,
0x0c, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0xee, 0x03, 0x0a, 0x0e,
0x50, 0x65, 0x65, 0x72, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1a,
0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x67, 0x6f,
@@ -2272,256 +2375,276 @@ var file_management_proto_rawDesc = []byte{
0x28, 0x09, 0x52, 0x12, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x69, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x69, 0x56, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x22, 0x94, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72,
0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57,
0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x79, 0x0a, 0x11, 0x53,
0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
0x65, 0x79, 0x12, 0x38, 0x0a, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22,
0xa8, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x73, 0x74,
0x75, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x05, 0x74, 0x75, 0x72, 0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69,
0x67, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x48,
0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x3b, 0x0a, 0x08, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x3b, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74,
0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a,
0x03, 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x02,
0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x44,
0x54, 0x4c, 0x53, 0x10, 0x04, 0x22, 0x7d, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74,
0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a,
0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f,
0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73,
0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73,
0x77, 0x6f, 0x72, 0x64, 0x22, 0x81, 0x01, 0x0a, 0x0a, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a,
0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12,
0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0xe2, 0x03, 0x0a, 0x0a, 0x4e, 0x65, 0x74,
0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61,
0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12,
0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65,
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74,
0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65,
0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f,
0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74,
0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20,
0x01, 0x28, 0x08, 0x52, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73,
0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65,
0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x52, 0x6f, 0x75, 0x74,
0x65, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18,
0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x44, 0x4e,
0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69,
0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74,
0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6f, 0x66, 0x66,
0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x46, 0x69, 0x72,
0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69,
0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0d, 0x46, 0x69, 0x72, 0x65,
0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x66, 0x69, 0x72,
0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74,
0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c,
0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x97, 0x01,
0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e,
0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33,
0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53,
0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c,
0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65,
0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b,
0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68,
0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41,
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77,
0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72,
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6b, 0x65, 0x72,
0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x4f, 0x53,
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4f,
0x53, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x10, 0x6e, 0x65, 0x74, 0x77,
0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e,
0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16,
0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x4f,
0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x1e, 0x0a, 0x1c, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75,
0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5b, 0x0a, 0x15, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75,
0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12,
0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x22, 0xea, 0x02, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72,
0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a,
0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x44, 0x65,
0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75,
0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x6f,
0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74,
0x12, 0x14, 0x0a, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x55, 0x73, 0x65, 0x49,
0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34, 0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18,
0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c,
0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x18, 0x0a, 0x20, 0x03,
0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73,
0x22, 0xb5, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x65,
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x65, 0x74,
0x77, 0x6f, 0x72, 0x6b, 0x12, 0x20, 0x0a, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54,
0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f,
0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65,
0x74, 0x72, 0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x72,
0x69, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65,
0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61,
0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44, 0x22, 0xb4, 0x01, 0x0a, 0x09, 0x44, 0x4e, 0x53,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x47, 0x0a, 0x10,
0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72,
0x6f, 0x75, 0x70, 0x52, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47,
0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x38, 0x0a, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a,
0x6f, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f,
0x6e, 0x65, 0x52, 0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x22,
0x58, 0x0a, 0x0a, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x16, 0x0a,
0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44,
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x32, 0x0a, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73,
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64,
0x52, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x74, 0x0a, 0x0c, 0x53, 0x69, 0x6d,
0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a,
0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x54, 0x79, 0x70,
0x65, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x04,
0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x44, 0x61,
0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x22,
0xb3, 0x01, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72,
0x6f, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65,
0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a,
0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69,
0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
0x73, 0x12, 0x32, 0x0a, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69,
0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52,
0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x48, 0x0a, 0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x03, 0x52, 0x06, 0x4e, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50,
0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22,
0xf0, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65,
0x12, 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x12, 0x40, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c,
0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x06, 0x41, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c,
0x52, 0x75, 0x6c, 0x65, 0x2e, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f,
0x55, 0x54, 0x10, 0x01, 0x22, 0x1e, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a,
0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x52,
0x4f, 0x50, 0x10, 0x01, 0x22, 0x3c, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a,
0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12,
0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50,
0x10, 0x04, 0x32, 0xd1, 0x03, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69,
0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a,
0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63,
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12,
0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x10,
0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73,
0x12, 0x28, 0x0a, 0x0f, 0x73, 0x79, 0x73, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d,
0x62, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x79, 0x73, 0x53, 0x65,
0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x79,
0x73, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0e, 0x73, 0x79, 0x73, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x4e, 0x61,
0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x73, 0x79, 0x73, 0x4d, 0x61, 0x6e, 0x75, 0x66, 0x61, 0x63,
0x74, 0x75, 0x72, 0x65, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x79, 0x73,
0x4d, 0x61, 0x6e, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, 0x72, 0x65, 0x72, 0x22, 0x94, 0x01, 0x0a,
0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b,
0x0a, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x57, 0x69, 0x72, 0x65, 0x74, 0x72, 0x75, 0x73, 0x74,
0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x77, 0x69, 0x72, 0x65, 0x74, 0x72,
0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x70,
0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65,
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x22, 0x79, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x38, 0x0a, 0x09, 0x65, 0x78,
0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72,
0x65, 0x73, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x07,
0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xa8, 0x01, 0x0a, 0x11, 0x57, 0x69, 0x72, 0x65,
0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2c, 0x0a,
0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x73, 0x74, 0x75, 0x6e, 0x73, 0x12, 0x35, 0x0a, 0x05, 0x74,
0x75, 0x72, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65,
0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x05, 0x74, 0x75, 0x72,
0x6e, 0x73, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x73, 0x69, 0x67, 0x6e,
0x61, 0x6c, 0x22, 0x98, 0x01, 0x0a, 0x0a, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
0x75, 0x72, 0x69, 0x12, 0x3b, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x72,
0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x22, 0x3b, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03,
0x55, 0x44, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x01, 0x12, 0x08,
0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50,
0x53, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x54, 0x4c, 0x53, 0x10, 0x04, 0x22, 0x7d, 0x0a,
0x13, 0x50, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x0a, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04,
0x75, 0x73, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72,
0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x81, 0x01, 0x0a,
0x0a, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61,
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x64, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04,
0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e,
0x22, 0xe2, 0x03, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12,
0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x3e, 0x0a, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x52, 0x0b, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12,
0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73,
0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x72, 0x65, 0x6d,
0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12,
0x29, 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x6f, 0x75,
0x74, 0x65, 0x52, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x44, 0x4e,
0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x4e, 0x53, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x40, 0x0a, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18,
0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x52, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72,
0x73, 0x12, 0x3e, 0x0a, 0x0d, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c,
0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75,
0x6c, 0x65, 0x52, 0x0d, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65,
0x73, 0x12, 0x32, 0x0a, 0x14, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c,
0x65, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52,
0x14, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x49, 0x73,
0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65,
0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67,
0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67,
0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65,
0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f,
0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x66,
0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22,
0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a,
0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08,
0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09,
0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65,
0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a,
0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76,
0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75,
0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e,
0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64,
0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64,
0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x1e,
0x0a, 0x1c, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5b,
0x0a, 0x15, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69,
0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f,
0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f,
0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0xea, 0x02, 0x0a, 0x0e,
0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a,
0x0a, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x6c,
0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x16,
0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e,
0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x75, 0x64, 0x69, 0x65, 0x6e,
0x63, 0x65, 0x12, 0x2e, 0x0a, 0x12, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68,
0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12,
0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69,
0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f,
0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x63, 0x6f, 0x70,
0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1e,
0x0a, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0a, 0x55, 0x73, 0x65, 0x49, 0x44, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x34,
0x0a, 0x15, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45,
0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x41,
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70,
0x6f, 0x69, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
0x55, 0x52, 0x4c, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x52, 0x65, 0x64, 0x69,
0x72, 0x65, 0x63, 0x74, 0x55, 0x52, 0x4c, 0x73, 0x22, 0xb5, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75,
0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x20, 0x0a, 0x0b,
0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
0x03, 0x52, 0x0b, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12,
0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x65,
0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x05, 0x20, 0x01,
0x28, 0x03, 0x52, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x1e, 0x0a, 0x0a, 0x4d, 0x61,
0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a,
0x4d, 0x61, 0x73, 0x71, 0x75, 0x65, 0x72, 0x61, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4e, 0x65,
0x74, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x4e, 0x65, 0x74, 0x49, 0x44,
0x22, 0xb4, 0x01, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24,
0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x45, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76,
0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61, 0x6d, 0x65,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x10, 0x4e, 0x61, 0x6d,
0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x38, 0x0a,
0x0b, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x0b, 0x43, 0x75, 0x73, 0x74,
0x6f, 0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x73, 0x22, 0x58, 0x0a, 0x0a, 0x43, 0x75, 0x73, 0x74, 0x6f,
0x6d, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x32, 0x0a,
0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x69, 0x6d, 0x70,
0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x07, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64,
0x73, 0x22, 0x74, 0x0a, 0x0c, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x65, 0x63, 0x6f, 0x72,
0x64, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x03, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x43, 0x6c, 0x61,
0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12,
0x10, 0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x54, 0x54,
0x4c, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x52, 0x44, 0x61, 0x74, 0x61, 0x22, 0xb3, 0x01, 0x0a, 0x0f, 0x4e, 0x61, 0x6d, 0x65,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x0b, 0x4e,
0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x16, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x4e, 0x61,
0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x0b, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65,
0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79,
0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x12,
0x18, 0x0a, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09,
0x52, 0x07, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x53, 0x65, 0x61,
0x72, 0x63, 0x68, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65,
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x44,
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x48, 0x0a,
0x0a, 0x4e, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x49,
0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x4e,
0x53, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4e, 0x53, 0x54,
0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
0x03, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0xf0, 0x02, 0x0a, 0x0c, 0x46, 0x69, 0x72, 0x65,
0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x65, 0x65, 0x72,
0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x65, 0x65, 0x72, 0x49, 0x50,
0x12, 0x40, 0x0a, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x64, 0x69,
0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x46, 0x69, 0x72, 0x65, 0x77, 0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x61, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x08, 0x50,
0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x21, 0x2e,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x72, 0x65, 0x77,
0x61, 0x6c, 0x6c, 0x52, 0x75, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f,
0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x1c,
0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x06, 0x0a, 0x02, 0x49,
0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x55, 0x54, 0x10, 0x01, 0x22, 0x1e, 0x0a, 0x06,
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54,
0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x52, 0x4f, 0x50, 0x10, 0x01, 0x22, 0x3c, 0x0a, 0x08,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e,
0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x07,
0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x03,
0x12, 0x08, 0x0a, 0x04, 0x49, 0x43, 0x4d, 0x50, 0x10, 0x04, 0x22, 0x38, 0x0a, 0x0e, 0x4e, 0x65,
0x74, 0x77, 0x6f, 0x72, 0x6b, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, 0x05,
0x6e, 0x65, 0x74, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x65, 0x74,
0x49, 0x50, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x61, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x03, 0x6d, 0x61, 0x63, 0x32, 0xd1, 0x03, 0x0a, 0x11, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x4c, 0x6f,
0x67, 0x69, 0x6e, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
0x00, 0x12, 0x46, 0x0a, 0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65,
0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65,
0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69,
0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61,
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00,
0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74,
0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c,
0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74,
0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a,
0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e,
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41,
0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77,
0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e,
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72,
0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70,
0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18,
0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58,
0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43, 0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65,
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -2537,7 +2660,7 @@ func file_management_proto_rawDescGZIP() []byte {
}
var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 28)
var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 29)
var file_management_proto_goTypes = []interface{}{
(HostConfig_Protocol)(0), // 0: management.HostConfig.Protocol
(DeviceAuthorizationFlowProvider)(0), // 1: management.DeviceAuthorizationFlow.provider
@@ -2572,7 +2695,8 @@ var file_management_proto_goTypes = []interface{}{
(*NameServerGroup)(nil), // 30: management.NameServerGroup
(*NameServer)(nil), // 31: management.NameServer
(*FirewallRule)(nil), // 32: management.FirewallRule
(*timestamppb.Timestamp)(nil), // 33: google.protobuf.Timestamp
(*NetworkAddress)(nil), // 33: management.NetworkAddress
(*timestamppb.Timestamp)(nil), // 34: google.protobuf.Timestamp
}
var file_management_proto_depIdxs = []int32{
14, // 0: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig
@@ -2581,49 +2705,50 @@ var file_management_proto_depIdxs = []int32{
18, // 3: management.SyncResponse.NetworkMap:type_name -> management.NetworkMap
10, // 4: management.LoginRequest.meta:type_name -> management.PeerSystemMeta
9, // 5: management.LoginRequest.peerKeys:type_name -> management.PeerKeys
14, // 6: management.LoginResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig
17, // 7: management.LoginResponse.peerConfig:type_name -> management.PeerConfig
33, // 8: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp
15, // 9: management.WiretrusteeConfig.stuns:type_name -> management.HostConfig
16, // 10: management.WiretrusteeConfig.turns:type_name -> management.ProtectedHostConfig
15, // 11: management.WiretrusteeConfig.signal:type_name -> management.HostConfig
0, // 12: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol
15, // 13: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig
20, // 14: management.PeerConfig.sshConfig:type_name -> management.SSHConfig
17, // 15: management.NetworkMap.peerConfig:type_name -> management.PeerConfig
19, // 16: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig
26, // 17: management.NetworkMap.Routes:type_name -> management.Route
27, // 18: management.NetworkMap.DNSConfig:type_name -> management.DNSConfig
19, // 19: management.NetworkMap.offlinePeers:type_name -> management.RemotePeerConfig
32, // 20: management.NetworkMap.FirewallRules:type_name -> management.FirewallRule
20, // 21: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig
1, // 22: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider
25, // 23: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig
25, // 24: management.PKCEAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig
30, // 25: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup
28, // 26: management.DNSConfig.CustomZones:type_name -> management.CustomZone
29, // 27: management.CustomZone.Records:type_name -> management.SimpleRecord
31, // 28: management.NameServerGroup.NameServers:type_name -> management.NameServer
2, // 29: management.FirewallRule.Direction:type_name -> management.FirewallRule.direction
3, // 30: management.FirewallRule.Action:type_name -> management.FirewallRule.action
4, // 31: management.FirewallRule.Protocol:type_name -> management.FirewallRule.protocol
5, // 32: management.ManagementService.Login:input_type -> management.EncryptedMessage
5, // 33: management.ManagementService.Sync:input_type -> management.EncryptedMessage
13, // 34: management.ManagementService.GetServerKey:input_type -> management.Empty
13, // 35: management.ManagementService.isHealthy:input_type -> management.Empty
5, // 36: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage
5, // 37: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage
5, // 38: management.ManagementService.Login:output_type -> management.EncryptedMessage
5, // 39: management.ManagementService.Sync:output_type -> management.EncryptedMessage
12, // 40: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse
13, // 41: management.ManagementService.isHealthy:output_type -> management.Empty
5, // 42: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage
5, // 43: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage
38, // [38:44] is the sub-list for method output_type
32, // [32:38] is the sub-list for method input_type
32, // [32:32] is the sub-list for extension type_name
32, // [32:32] is the sub-list for extension extendee
0, // [0:32] is the sub-list for field type_name
33, // 6: management.PeerSystemMeta.networkAddresses:type_name -> management.NetworkAddress
14, // 7: management.LoginResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig
17, // 8: management.LoginResponse.peerConfig:type_name -> management.PeerConfig
34, // 9: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp
15, // 10: management.WiretrusteeConfig.stuns:type_name -> management.HostConfig
16, // 11: management.WiretrusteeConfig.turns:type_name -> management.ProtectedHostConfig
15, // 12: management.WiretrusteeConfig.signal:type_name -> management.HostConfig
0, // 13: management.HostConfig.protocol:type_name -> management.HostConfig.Protocol
15, // 14: management.ProtectedHostConfig.hostConfig:type_name -> management.HostConfig
20, // 15: management.PeerConfig.sshConfig:type_name -> management.SSHConfig
17, // 16: management.NetworkMap.peerConfig:type_name -> management.PeerConfig
19, // 17: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig
26, // 18: management.NetworkMap.Routes:type_name -> management.Route
27, // 19: management.NetworkMap.DNSConfig:type_name -> management.DNSConfig
19, // 20: management.NetworkMap.offlinePeers:type_name -> management.RemotePeerConfig
32, // 21: management.NetworkMap.FirewallRules:type_name -> management.FirewallRule
20, // 22: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig
1, // 23: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider
25, // 24: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig
25, // 25: management.PKCEAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig
30, // 26: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup
28, // 27: management.DNSConfig.CustomZones:type_name -> management.CustomZone
29, // 28: management.CustomZone.Records:type_name -> management.SimpleRecord
31, // 29: management.NameServerGroup.NameServers:type_name -> management.NameServer
2, // 30: management.FirewallRule.Direction:type_name -> management.FirewallRule.direction
3, // 31: management.FirewallRule.Action:type_name -> management.FirewallRule.action
4, // 32: management.FirewallRule.Protocol:type_name -> management.FirewallRule.protocol
5, // 33: management.ManagementService.Login:input_type -> management.EncryptedMessage
5, // 34: management.ManagementService.Sync:input_type -> management.EncryptedMessage
13, // 35: management.ManagementService.GetServerKey:input_type -> management.Empty
13, // 36: management.ManagementService.isHealthy:input_type -> management.Empty
5, // 37: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage
5, // 38: management.ManagementService.GetPKCEAuthorizationFlow:input_type -> management.EncryptedMessage
5, // 39: management.ManagementService.Login:output_type -> management.EncryptedMessage
5, // 40: management.ManagementService.Sync:output_type -> management.EncryptedMessage
12, // 41: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse
13, // 42: management.ManagementService.isHealthy:output_type -> management.Empty
5, // 43: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage
5, // 44: management.ManagementService.GetPKCEAuthorizationFlow:output_type -> management.EncryptedMessage
39, // [39:45] is the sub-list for method output_type
33, // [33:39] is the sub-list for method input_type
33, // [33:33] is the sub-list for extension type_name
33, // [33:33] is the sub-list for extension extendee
0, // [0:33] is the sub-list for field type_name
}
func init() { file_management_proto_init() }
@@ -2968,6 +3093,18 @@ func file_management_proto_init() {
return nil
}
}
file_management_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*NetworkAddress); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -2975,7 +3112,7 @@ func file_management_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_management_proto_rawDesc,
NumEnums: 5,
NumMessages: 28,
NumMessages: 29,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -102,6 +102,12 @@ message PeerSystemMeta {
string OS = 6;
string wiretrusteeVersion = 7;
string uiVersion = 8;
string kernelVersion = 9;
string OSVersion = 10;
repeated NetworkAddress networkAddresses = 11;
string sysSerialNumber = 12;
string sysProductName = 13;
string sysManufacturer = 14;
}
message LoginResponse {
@@ -351,3 +357,8 @@ message FirewallRule {
ICMP = 4;
}
}
message NetworkAddress {
string netIP = 1;
string mac = 2;
}

View File

@@ -27,9 +27,11 @@ import (
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/geolocation"
"github.com/netbirdio/netbird/management/server/idp"
"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/management/server/status"
"github.com/netbirdio/netbird/route"
)
@@ -70,11 +72,12 @@ type AccountManager interface {
CheckUserAccessByJWTGroups(claims jwtclaims.AuthorizationClaims) error
GetAccountFromPAT(pat string) (*Account, *User, *PersonalAccessToken, error)
DeleteAccount(accountID, userID string) error
GetUsage(ctx context.Context, accountID string, start time.Time, end time.Time) (*AccountUsageStats, error)
MarkPATUsed(tokenID string) error
GetUser(claims jwtclaims.AuthorizationClaims) (*User, error)
ListUsers(accountID string) ([]*User, 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
UpdatePeer(accountID, userID string, peer *nbpeer.Peer) (*nbpeer.Peer, error)
GetNetworkMap(peerID string) (*NetworkMap, error)
@@ -108,7 +111,7 @@ type AccountManager interface {
DeleteNameServerGroup(accountID, nsGroupID, userID string) error
ListNameServerGroups(accountID string, userID string) ([]*nbdns.NameServerGroup, error)
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)
GetDNSSettings(accountID string, userID string) (*DNSSettings, error)
SaveDNSSettings(accountID string, userID string, dnsSettingsToSave *DNSSettings) error
@@ -119,6 +122,10 @@ type AccountManager interface {
GetAllConnectedPeers() (map[string]struct{}, error)
HasConnectedChannel(peerID string) bool
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)
}
type DefaultAccountManager struct {
@@ -133,6 +140,7 @@ type DefaultAccountManager struct {
externalCacheManager ExternalCacheManager
ctx context.Context
eventStore activity.Store
geo *geolocation.Geolocation
// singleAccountMode indicates whether the instance has a single account.
// If true, then every new user will end up under the same account.
@@ -209,16 +217,26 @@ type Account struct {
UsersG []User `json:"-" gorm:"foreignKey:AccountID;references:id"`
Groups map[string]*Group `gorm:"-"`
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"`
Routes map[string]*route.Route `gorm:"-"`
RoutesG []route.Route `json:"-" gorm:"foreignKey:AccountID;references:id"`
NameServerGroups map[string]*nbdns.NameServerGroup `gorm:"-"`
NameServerGroupsG []nbdns.NameServerGroup `json:"-" gorm:"foreignKey:AccountID;references:id"`
DNSSettings DNSSettings `gorm:"embedded;embeddedPrefix:dns_settings_"`
PostureChecks []*posture.Checks `gorm:"foreignKey:AccountID;references:id"`
// Settings is a dictionary of Account 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 {
@@ -635,11 +653,6 @@ func (a *Account) Copy() *Account {
groups[id] = group.Copy()
}
rules := map[string]*Rule{}
for id, rule := range a.Rules {
rules[id] = rule.Copy()
}
policies := []*Policy{}
for _, policy := range a.Policies {
policies = append(policies, policy.Copy())
@@ -662,6 +675,11 @@ func (a *Account) Copy() *Account {
settings = a.Settings.Copy()
}
postureChecks := []*posture.Checks{}
for _, postureCheck := range a.PostureChecks {
postureChecks = append(postureChecks, postureCheck.Copy())
}
return &Account{
Id: a.Id,
CreatedBy: a.CreatedBy,
@@ -673,11 +691,11 @@ func (a *Account) Copy() *Account {
Peers: peers,
Users: users,
Groups: groups,
Rules: rules,
Policies: policies,
Routes: routes,
NameServerGroups: nsGroups,
DNSSettings: dnsSettings,
PostureChecks: postureChecks,
Settings: settings,
}
}
@@ -804,10 +822,12 @@ func (a *Account) UserGroupsRemoveFromPeers(userID string, groups ...string) {
// BuildManager creates a new DefaultAccountManager with a provided Store
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,
) (*DefaultAccountManager, error) {
am := &DefaultAccountManager{
Store: store,
geo: geo,
peersUpdateManager: peersUpdateManager,
idpManager: idpManager,
ctx: context.Background(),
@@ -897,12 +917,7 @@ func (am *DefaultAccountManager) UpdateAccountSettings(accountID, userID string,
unlock := am.Store.AcquireAccountLock(accountID)
defer unlock()
account, err := am.Store.GetAccountByUser(userID)
if err != nil {
return nil, err
}
err = additions.ValidateExtraSettings(newSettings.Extra, account.Settings.Extra, account.Peers, userID, accountID, am.eventStore)
account, err := am.Store.GetAccount(accountID)
if err != nil {
return nil, err
}
@@ -916,6 +931,11 @@ func (am *DefaultAccountManager) UpdateAccountSettings(accountID, userID string,
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
if oldSettings.PeerLoginExpirationEnabled != newSettings.PeerLoginExpirationEnabled {
event := activity.AccountPeerLoginExpirationEnabled
@@ -1094,8 +1114,20 @@ func (am *DefaultAccountManager) DeleteAccount(accountID, userID string) error {
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
// 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) {
if accountID != "" {
return am.Store.GetAccount(accountID)
@@ -1223,7 +1255,21 @@ func (am *DefaultAccountManager) lookupUserInCache(userID string, account *Accou
}
}
return nil, nil //nolint:nilnil
// add extra check on external cache manager. We may get to this point when the user is not yet findable in IDP,
// or it didn't have its metadata updated with am.addAccountIDToIDPAppMeta
user, err := account.FindUser(userID)
if err != nil {
log.Errorf("failed finding user %s in account %s", userID, account.Id)
return nil, err
}
key := user.IntegrationReference.CacheKey(account.Id, userID)
ud, err := am.externalCacheManager.Get(am.ctx, key)
if err != nil {
log.Debugf("failed to get externalCache for key: %s, error: %s", key, err)
}
return ud, nil
}
func (am *DefaultAccountManager) refreshCache(accountID string) ([]*idp.UserData, error) {
@@ -1766,7 +1812,7 @@ func (am *DefaultAccountManager) CheckUserAccessByJWTGroups(claims jwtclaims.Aut
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 {
if len(account.Groups) == 0 {
allGroup := &Group{
@@ -1779,21 +1825,28 @@ func addAllGroup(account *Account) error {
}
account.Groups = map[string]*Group{allGroup.ID: allGroup}
defaultRule := &Rule{
ID: xid.New().String(),
id := xid.New().String()
defaultPolicy := &Policy{
ID: id,
Name: DefaultRuleName,
Description: DefaultRuleDescription,
Disabled: false,
Source: []string{allGroup.ID},
Destination: []string{allGroup.ID},
Enabled: true,
Rules: []*PolicyRule{
{
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}
}
return nil

View File

@@ -16,6 +16,7 @@ import (
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/management/server/posture"
"github.com/netbirdio/netbird/route"
"github.com/stretchr/testify/assert"
@@ -96,16 +97,6 @@ func verifyNewAccountHasDefaultFields(t *testing.T, account *Account, createdBy
if account.Domain != 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) {
@@ -1528,18 +1519,12 @@ func TestAccount_Copy(t *testing.T) {
Peers: []string{"peer1"},
},
},
Rules: map[string]*Rule{
"rule1": {
ID: "rule1",
Destination: []string{},
Source: []string{},
},
},
Policies: []*Policy{
{
ID: "policy1",
Enabled: true,
Rules: make([]*PolicyRule, 0),
ID: "policy1",
Enabled: true,
Rules: make([]*PolicyRule, 0),
SourcePostureChecks: make([]string, 0),
},
},
Routes: map[string]*route.Route{
@@ -1558,7 +1543,12 @@ func TestAccount_Copy(t *testing.T) {
},
},
DNSSettings: DNSSettings{DisabledManagementGroups: []string{}},
Settings: &Settings{},
PostureChecks: []*posture.Checks{
{
ID: "posture Checks1",
},
},
Settings: &Settings{},
}
err := hasNilField(account)
if err != nil {
@@ -1630,7 +1620,7 @@ func TestDefaultAccountManager_UpdatePeer_PeerLoginExpiration(t *testing.T) {
LoginExpirationEnabled: true,
})
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")
account, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
PeerLoginExpiration: time.Hour,
@@ -1697,7 +1687,7 @@ func TestDefaultAccountManager_MarkPeerConnected_PeerLoginExpiration(t *testing.
}
// 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")
failed := waitTimeout(wg, time.Second)
@@ -1720,7 +1710,7 @@ func TestDefaultAccountManager_UpdateAccountSettings_PeerLoginExpiration(t *test
LoginExpirationEnabled: true,
})
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")
wg := &sync.WaitGroup{}
@@ -2228,7 +2218,7 @@ func createManager(t *testing.T) (*DefaultAccountManager, error) {
return nil, err
}
eventStore := &activity.InMemoryEventStore{}
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, false)
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false)
}
func createStore(t *testing.T) (Store, error) {

View File

@@ -1,12 +1,14 @@
package activity
import "maps"
// Activity that triggered an Event
type Activity int
// Code is an activity string representation
type Code struct {
message string
code string
Message string
Code string
}
const (
@@ -130,6 +132,12 @@ const (
PeerApprovalRevoked
// TransferredOwnerRole indicates that the user transferred the owner role of the account
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{
@@ -193,12 +201,15 @@ var activityMap = map[Activity]Code{
PeerApproved: {"Peer approved", "peer.approve"},
PeerApprovalRevoked: {"Peer approval revoked", "peer.approval.revoke"},
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
func (a Activity) StringCode() string {
if code, ok := activityMap[a]; ok {
return code.code
return code.Code
}
return "UNKNOWN_ACTIVITY"
}
@@ -206,7 +217,12 @@ func (a Activity) StringCode() string {
// Message returns a string representation of an activity
func (a Activity) Message() string {
if code, ok := activityMap[a]; ok {
return code.message
return code.Message
}
return "UNKNOWN_ACTIVITY"
}
// RegisterActivityMap adds new codes to the activity map
func RegisterActivityMap(codes map[Activity]Code) {
maps.Copy(activityMap, codes)
}

View File

@@ -8,12 +8,18 @@ const (
SystemInitiator = "sys"
)
// ActivityDescriber is an interface that describes an activity
type ActivityDescriber interface {
StringCode() string
Message() string
}
// Event represents a network/system activity event.
type Event struct {
// Timestamp of the event
Timestamp time.Time
// 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 uint64
// InitiatorID is the ID of an object that initiated the event (e.g., a user)

View File

@@ -41,8 +41,6 @@ type Config struct {
HttpConfig *HttpServerConfig
TrustedHTTPProxies []netip.Prefix
IdpManagerConfig *idp.Config
DeviceAuthorizationFlow *DeviceAuthorizationFlow
@@ -50,6 +48,8 @@ type Config struct {
PKCEAuthorizationFlow *PKCEAuthorizationFlow
StoreConfig StoreConfig
ReverseProxy ReverseProxy
}
// GetAuthAudiences returns the audience from the http config and device authorization flow config
@@ -146,6 +146,27 @@ type StoreConfig struct {
Engine StoreEngine
}
// ReverseProxy contains reverse proxy configuration in front of management.
type ReverseProxy struct {
// TrustedHTTPProxies represents a list of trusted HTTP proxies by their IP prefixes.
// When extracting the real IP address from request headers, the middleware will verify
// if the peer's address falls within one of these trusted IP prefixes.
TrustedHTTPProxies []netip.Prefix
// TrustedHTTPProxiesCount specifies the count of trusted HTTP proxies between the internet
// and the server. When using the trusted proxy count method to extract the real IP address,
// the middleware will search the X-Forwarded-For IP list from the rightmost by this count
// minus one.
TrustedHTTPProxiesCount uint
// TrustedPeers represents a list of trusted peers by their IP prefixes.
// These peers are considered trustworthy by the gRPC server operator,
// and the middleware will attempt to extract the real IP address from
// request headers if the peer's address falls within one of these
// trusted IP prefixes.
TrustedPeers []netip.Prefix
}
// validateURL validates input http url
func validateURL(httpURL string) bool {
_, err := url.ParseRequestURI(httpURL)

View File

@@ -193,7 +193,7 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
return nil, err
}
eventStore := &activity.InMemoryEventStore{}
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, false)
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false)
}
func createDNSStore(t *testing.T) (Store, error) {

View File

@@ -54,8 +54,7 @@ func (am *DefaultAccountManager) GetEvents(accountID, userID string) ([]*activit
return filtered, nil
}
func (am *DefaultAccountManager) StoreEvent(initiatorID, targetID, accountID string, activityID activity.Activity,
meta map[string]any) {
func (am *DefaultAccountManager) StoreEvent(initiatorID, targetID, accountID string, activityID activity.ActivityDescriber, meta map[string]any) {
go func() {
_, err := am.eventStore.Save(&activity.Event{

View File

@@ -1,6 +1,8 @@
package server
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
@@ -159,18 +161,6 @@ func restore(file string) (*FileStore, error) {
if account.Policies == nil {
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
existingLabels := account.getPeerDNSLabels()
@@ -342,13 +332,6 @@ func (s *FileStore) SaveAccount(account *Account) error {
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)
}
@@ -626,6 +609,27 @@ func (s *FileStore) SavePeerStatus(accountID, peerID string, peerStatus nbpeer.P
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.
func (s *FileStore) SaveUserLastLogin(accountID, userID string, lastLogin time.Time) error {
s.mux.Lock()
@@ -660,3 +664,40 @@ func (s *FileStore) Close() error {
func (s *FileStore) GetStoreEngine() StoreEngine {
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
}

View File

@@ -1,6 +1,7 @@
package server
import (
"context"
"crypto/sha256"
"net"
"path/filepath"
@@ -193,18 +194,18 @@ func TestStore(t *testing.T) {
Name: "all",
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{
ID: "all",
Name: "all",
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{
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")
}
// 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) {
storeDir := t.TempDir()
@@ -634,6 +600,55 @@ func TestFileStore_SavePeerStatus(t *testing.T) {
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 {
t.Helper()
store, err := NewFileStore(t.TempDir(), nil)
@@ -643,3 +658,32 @@ func newStore(t *testing.T) *FileStore {
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)
}

View File

@@ -0,0 +1,187 @@
package geolocation
import (
"encoding/csv"
"fmt"
"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 os.Rename(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
}

View 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
}

View 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)
}

View 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
}

View 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
}

View File

@@ -115,6 +115,13 @@ func (am *DefaultAccountManager) SaveGroup(accountID, userID string, newGroup *G
if err != nil {
return err
}
for _, peerID := range newGroup.Peers {
if account.Peers[peerID] == nil {
return status.Errorf(status.InvalidArgument, "peer with ID \"%s\" not found", peerID)
}
}
oldGroup, exists := account.Groups[newGroup.ID]
account.Groups[newGroup.ID] = newGroup

View File

@@ -3,6 +3,8 @@ package server
import (
"context"
"fmt"
"net"
"net/netip"
"strings"
"time"
@@ -109,11 +111,11 @@ func (s *GRPCServer) GetServerKey(ctx context.Context, req *proto.Empty) (*proto
}, nil
}
func getRealIP(ctx context.Context) string {
if ip, ok := realip.FromContext(ctx); ok {
return ip.String()
func getRealIP(ctx context.Context) net.IP {
if addr, ok := realip.FromContext(ctx); ok {
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
@@ -124,7 +126,7 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
s.appMetrics.GRPCMetrics().CountSyncRequest()
}
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{}
peerKey, err := s.parseRequest(req, syncReq)
@@ -147,7 +149,7 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
s.ephemeralManager.OnPeerConnected(peer)
err = s.accountManager.MarkPeerConnected(peerKey.String(), true)
err = s.accountManager.MarkPeerConnected(peerKey.String(), true, realIP)
if err != nil {
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) {
s.peersUpdateManager.CloseChannel(peer.ID)
s.turnCredentialsManager.CancelRefresh(peer.ID)
_ = s.accountManager.MarkPeerConnected(peer.Key, false)
_ = s.accountManager.MarkPeerConnected(peer.Key, false, nil)
s.ephemeralManager.OnPeerDisconnected(peer)
}
@@ -254,15 +256,38 @@ func mapError(err error) error {
}
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{
Hostname: loginReq.GetMeta().GetHostname(),
GoOS: loginReq.GetMeta().GetGoOS(),
Kernel: loginReq.GetMeta().GetKernel(),
Core: loginReq.GetMeta().GetCore(),
Platform: loginReq.GetMeta().GetPlatform(),
OS: loginReq.GetMeta().GetOS(),
WtVersion: loginReq.GetMeta().GetWiretrusteeVersion(),
UIVersion: loginReq.GetMeta().GetUiVersion(),
Hostname: loginReq.GetMeta().GetHostname(),
GoOS: loginReq.GetMeta().GetGoOS(),
Kernel: loginReq.GetMeta().GetKernel(),
Platform: loginReq.GetMeta().GetPlatform(),
OS: loginReq.GetMeta().GetOS(),
OSVersion: osVersion,
WtVersion: loginReq.GetMeta().GetWiretrusteeVersion(),
UIVersion: loginReq.GetMeta().GetUiVersion(),
KernelVersion: loginReq.GetMeta().GetKernelVersion(),
NetworkAddresses: networkAddresses,
SystemSerialNumber: loginReq.GetMeta().GetSysSerialNumber(),
SystemProductName: loginReq.GetMeta().GetSysProductName(),
SystemManufacturer: loginReq.GetMeta().GetSysManufacturer(),
}
}
@@ -296,7 +321,7 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p
s.appMetrics.GRPCMetrics().CountLoginRequest()
}
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{}
peerKey, err := s.parseRequest(req, loginReq)

View File

@@ -1,4 +1,4 @@
openapi: 3.0.1
openapi: 3.1.0
servers:
- url: https://api.netbird.io
description: Default server
@@ -21,6 +21,8 @@ tags:
description: Interact with and view information about rules.
- name: Policies
description: Interact with and view information about policies.
- name: Posture Checks
description: Interact with and view information about posture checks.
- name: Routes
description: Interact with and view information about routes.
- name: DNS
@@ -245,6 +247,10 @@ components:
description: Peer's IP address
type: string
example: 10.64.0.1
connection_ip:
description: Peer's public connection IP address
type: string
example: 35.64.0.1
connected:
description: Peer to Management connection status
type: boolean
@@ -258,6 +264,14 @@ components:
description: Peer's operating system and version
type: string
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:
description: Peer's daemon or cli version
type: string
@@ -304,19 +318,30 @@ components:
description: (Cloud only) Indicates whether peer needs approval
type: boolean
example: true
country_code:
$ref: '#/components/schemas/CountryCode'
city_name:
$ref: '#/components/schemas/CityName'
required:
- ip
- city_name
- connected
- last_seen
- os
- version
- groups
- ssh_enabled
- hostname
- connection_ip
- country_code
- dns_label
- geoname_id
- groups
- hostname
- ip
- kernel_version
- last_login
- last_seen
- login_expiration_enabled
- login_expired
- last_login
- os
- ssh_enabled
- user_id
- version
- ui_version
AccessiblePeer:
allOf:
- $ref: '#/components/schemas/PeerMinimum'
@@ -774,6 +799,12 @@ components:
- $ref: '#/components/schemas/PolicyMinimum'
- type: object
properties:
source_posture_checks:
description: Posture checks ID's applied to policy source groups
type: array
items:
type: string
example: "chacdk86lnnboviihd70"
rules:
description: Policy rule object for policy UI editor
type: array
@@ -786,6 +817,12 @@ components:
- $ref: '#/components/schemas/PolicyMinimum'
- type: object
properties:
source_posture_checks:
description: Posture checks ID's applied to policy source groups
type: array
items:
type: string
example: "chacdk86lnnboviihd70"
rules:
description: Policy rule object for policy UI editor
type: array
@@ -793,6 +830,190 @@ components:
$ref: '#/components/schemas/PolicyRule'
required:
- 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:
type: object
properties:
@@ -904,7 +1125,7 @@ components:
nameservers:
description: Nameserver list
minLength: 1
maxLength: 2
maxLength: 3
type: array
items:
$ref: '#/components/schemas/Nameserver'
@@ -2144,7 +2365,6 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/routes/{routeId}:
get:
summary: Retrieve a Route
@@ -2289,7 +2509,6 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/dns/nameservers/{nsgroupId}:
get:
summary: Retrieve a Nameserver Group
@@ -2381,7 +2600,6 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/dns/settings:
get:
summary: Retrieve DNS settings
@@ -2459,3 +2677,194 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$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"

View File

@@ -63,11 +63,23 @@ const (
EventActivityCodeUserUnblock EventActivityCode = "user.unblock"
)
// Defines values for GeoLocationCheckAction.
const (
GeoLocationCheckActionAllow GeoLocationCheckAction = "allow"
GeoLocationCheckActionDeny GeoLocationCheckAction = "deny"
)
// Defines values for NameserverNsType.
const (
NameserverNsTypeUdp NameserverNsType = "udp"
)
// Defines values for PeerNetworkRangeCheckAction.
const (
PeerNetworkRangeCheckActionAllow PeerNetworkRangeCheckAction = "allow"
PeerNetworkRangeCheckActionDeny PeerNetworkRangeCheckAction = "deny"
)
// Defines values for PolicyRuleAction.
const (
PolicyRuleActionAccept PolicyRuleAction = "accept"
@@ -176,6 +188,45 @@ type AccountSettings struct {
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.
type DNSSettings struct {
// 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
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.
type Group struct {
// Id Group ID
@@ -257,6 +320,30 @@ type GroupRequest struct {
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.
type Nameserver struct {
// Ip Nameserver IP
@@ -329,6 +416,24 @@ type NameserverGroupRequest struct {
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.
type Peer struct {
// AccessiblePeers List of accessible peers
@@ -337,12 +442,24 @@ type Peer struct {
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
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 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 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 []GroupMinimum `json:"groups"`
@@ -355,6 +472,9 @@ type Peer struct {
// Ip Peer's IP address
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 time.Time `json:"last_login"`
@@ -377,10 +497,10 @@ type Peer struct {
SshEnabled bool `json:"ssh_enabled"`
// 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 *string `json:"user_id,omitempty"`
UserId string `json:"user_id"`
// Version Peer's daemon or cli version
Version string `json:"version"`
@@ -391,12 +511,24 @@ type PeerBase struct {
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
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 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 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 []GroupMinimum `json:"groups"`
@@ -409,6 +541,9 @@ type PeerBase struct {
// Ip Peer's IP address
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 time.Time `json:"last_login"`
@@ -431,10 +566,10 @@ type PeerBase struct {
SshEnabled bool `json:"ssh_enabled"`
// 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 *string `json:"user_id,omitempty"`
UserId string `json:"user_id"`
// Version Peer's daemon or cli version
Version string `json:"version"`
@@ -448,12 +583,24 @@ type PeerBatch struct {
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
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 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 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 []GroupMinimum `json:"groups"`
@@ -466,6 +613,9 @@ type PeerBatch struct {
// Ip Peer's IP address
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 time.Time `json:"last_login"`
@@ -488,10 +638,10 @@ type PeerBatch struct {
SshEnabled bool `json:"ssh_enabled"`
// 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 *string `json:"user_id,omitempty"`
UserId string `json:"user_id"`
// Version Peer's daemon or cli version
Version string `json:"version"`
@@ -506,6 +656,18 @@ type PeerMinimum struct {
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.
type PeerRequest struct {
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
@@ -569,6 +731,9 @@ type Policy struct {
// Rules Policy rule object for policy UI editor
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.
@@ -713,6 +878,36 @@ type PolicyUpdate struct {
// Rules Policy rule object for policy UI editor
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.
@@ -1012,6 +1207,12 @@ type PostApiPoliciesJSONRequestBody = PolicyUpdate
// PutApiPoliciesPolicyIdJSONRequestBody defines body for PutApiPoliciesPolicyId for application/json ContentType.
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.
type PostApiRoutesJSONRequestBody = RouteRequest

View File

@@ -0,0 +1,236 @@
package http
import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"path"
"testing"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/geolocation"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/mock_server"
"github.com/netbirdio/netbird/util"
)
func initGeolocationTestData(t *testing.T) *GeolocationsHandler {
t.Helper()
var (
mmdbPath = "../testdata/GeoLite2-City-Test.mmdb"
geonamesDBPath = "../testdata/geonames-test.db"
)
tempDir := t.TempDir()
err := util.CopyFileContents(mmdbPath, path.Join(tempDir, geolocation.MMDBFileName))
assert.NoError(t, err)
err = util.CopyFileContents(geonamesDBPath, path.Join(tempDir, geolocation.GeoSqliteDBFile))
assert.NoError(t, err)
geo, err := geolocation.NewGeolocation(tempDir)
assert.NoError(t, err)
t.Cleanup(func() { _ = geo.Stop() })
return &GeolocationsHandler{
accountManager: &mock_server.MockAccountManager{
GetAccountFromTokenFunc: func(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User, error) {
user := server.NewAdminUser("test_user")
return &server.Account{
Id: claims.AccountId,
Users: map[string]*server.User{
"test_user": user,
},
}, user, nil
},
},
geolocationManager: geo,
claimsExtractor: jwtclaims.NewClaimsExtractor(
jwtclaims.WithFromRequestContext(func(r *http.Request) jwtclaims.AuthorizationClaims {
return jwtclaims.AuthorizationClaims{
UserId: "test_user",
Domain: "hotmail.com",
AccountId: "test_id",
}
}),
),
}
}
func TestGetCitiesByCountry(t *testing.T) {
tt := []struct {
name string
expectedStatus int
expectedBody bool
expectedCities []api.City
requestType string
requestPath string
}{
{
name: "Get cities with valid country iso code",
expectedStatus: http.StatusOK,
expectedBody: true,
expectedCities: []api.City{
{
CityName: "Souni",
GeonameId: 5819,
},
{
CityName: "Protaras",
GeonameId: 18918,
},
},
requestType: http.MethodGet,
requestPath: "/api/locations/countries/CY/cities",
},
{
name: "Get cities with valid country iso code but zero cities",
expectedStatus: http.StatusOK,
expectedBody: true,
expectedCities: make([]api.City, 0),
requestType: http.MethodGet,
requestPath: "/api/locations/countries/DE/cities",
},
{
name: "Get cities with invalid country iso code",
expectedStatus: http.StatusUnprocessableEntity,
expectedBody: false,
requestType: http.MethodGet,
requestPath: "/api/locations/countries/12ds/cities",
},
}
geolocationHandler := initGeolocationTestData(t)
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
recorder := httptest.NewRecorder()
req := httptest.NewRequest(tc.requestType, tc.requestPath, nil)
router := mux.NewRouter()
router.HandleFunc("/api/locations/countries/{country}/cities", geolocationHandler.GetCitiesByCountry).Methods("GET")
router.ServeHTTP(recorder, req)
res := recorder.Result()
defer res.Body.Close()
content, err := io.ReadAll(res.Body)
if err != nil {
t.Fatalf("I don't know what I expected; %v", err)
return
}
if status := recorder.Code; status != tc.expectedStatus {
t.Errorf("handler returned wrong status code: got %v want %v, content: %s",
status, tc.expectedStatus, string(content))
return
}
if !tc.expectedBody {
return
}
cities := make([]api.City, 0)
if err = json.Unmarshal(content, &cities); err != nil {
t.Fatalf("unmarshal request cities response : %v", err)
return
}
assert.ElementsMatch(t, tc.expectedCities, cities)
})
}
}
func TestGetAllCountries(t *testing.T) {
tt := []struct {
name string
expectedStatus int
expectedBody bool
expectedCountries []api.Country
requestType string
requestPath string
}{
{
name: "Get all countries",
expectedStatus: http.StatusOK,
expectedBody: true,
expectedCountries: []api.Country{
{
CountryCode: "IR",
CountryName: "Iran",
},
{
CountryCode: "CY",
CountryName: "Cyprus",
},
{
CountryCode: "RW",
CountryName: "Rwanda",
},
{
CountryCode: "SO",
CountryName: "Somalia",
},
{
CountryCode: "YE",
CountryName: "Yemen",
},
{
CountryCode: "LY",
CountryName: "Libya",
},
{
CountryCode: "IQ",
CountryName: "Iraq",
},
},
requestType: http.MethodGet,
requestPath: "/api/locations/countries",
},
}
geolocationHandler := initGeolocationTestData(t)
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
recorder := httptest.NewRecorder()
req := httptest.NewRequest(tc.requestType, tc.requestPath, nil)
router := mux.NewRouter()
router.HandleFunc("/api/locations/countries", geolocationHandler.GetAllCountries).Methods("GET")
router.ServeHTTP(recorder, req)
res := recorder.Result()
defer res.Body.Close()
content, err := io.ReadAll(res.Body)
if err != nil {
t.Fatalf("I don't know what I expected; %v", err)
return
}
if status := recorder.Code; status != tc.expectedStatus {
t.Errorf("handler returned wrong status code: got %v want %v, content: %s",
status, tc.expectedStatus, string(content))
return
}
if !tc.expectedBody {
return
}
countries := make([]api.Country, 0)
if err = json.Unmarshal(content, &countries); err != nil {
t.Fatalf("unmarshal request cities response : %v", err)
return
}
assert.ElementsMatch(t, tc.expectedCountries, countries)
})
}
}

View File

@@ -0,0 +1,119 @@
package http
import (
"net/http"
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/geolocation"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/status"
)
// GeolocationsHandler is a handler that returns locations.
type GeolocationsHandler struct {
accountManager server.AccountManager
geolocationManager *geolocation.Geolocation
claimsExtractor *jwtclaims.ClaimsExtractor
}
// NewGeolocationsHandlerHandler creates a new Geolocations handler
func NewGeolocationsHandlerHandler(accountManager server.AccountManager, geolocationManager *geolocation.Geolocation, authCfg AuthCfg) *GeolocationsHandler {
return &GeolocationsHandler{
accountManager: accountManager,
geolocationManager: geolocationManager,
claimsExtractor: jwtclaims.NewClaimsExtractor(
jwtclaims.WithAudience(authCfg.Audience),
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
),
}
}
// GetAllCountries retrieves a list of all countries
func (l *GeolocationsHandler) GetAllCountries(w http.ResponseWriter, r *http.Request) {
if err := l.authenticateUser(r); err != nil {
util.WriteError(err, w)
return
}
if l.geolocationManager == nil {
// TODO: update error message to include geo db self hosted doc link when ready
util.WriteError(status.Errorf(status.PreconditionFailed, "Geo location database is not initialized"), w)
return
}
allCountries, err := l.geolocationManager.GetAllCountries()
if err != nil {
util.WriteError(err, w)
return
}
countries := make([]api.Country, 0, len(allCountries))
for _, country := range allCountries {
countries = append(countries, toCountryResponse(country))
}
util.WriteJSONObject(w, countries)
}
// GetCitiesByCountry retrieves a list of cities based on the given country code
func (l *GeolocationsHandler) GetCitiesByCountry(w http.ResponseWriter, r *http.Request) {
if err := l.authenticateUser(r); err != nil {
util.WriteError(err, w)
return
}
vars := mux.Vars(r)
countryCode := vars["country"]
if !countryCodeRegex.MatchString(countryCode) {
util.WriteError(status.Errorf(status.InvalidArgument, "invalid country code"), w)
return
}
if l.geolocationManager == nil {
// TODO: update error message to include geo db self hosted doc link when ready
util.WriteError(status.Errorf(status.PreconditionFailed, "Geo location database is not initialized"), w)
return
}
allCities, err := l.geolocationManager.GetCitiesByCountry(countryCode)
if err != nil {
util.WriteError(err, w)
return
}
cities := make([]api.City, 0, len(allCities))
for _, city := range allCities {
cities = append(cities, toCityResponse(city))
}
util.WriteJSONObject(w, cities)
}
func (l *GeolocationsHandler) authenticateUser(r *http.Request) error {
claims := l.claimsExtractor.FromRequestContext(r)
_, user, err := l.accountManager.GetAccountFromToken(claims)
if err != nil {
return err
}
if !user.HasAdminPower() {
return status.Errorf(status.PermissionDenied, "user is not allowed to perform this action")
}
return nil
}
func toCountryResponse(country geolocation.Country) api.Country {
return api.Country{
CountryName: country.CountryName,
CountryCode: country.CountryISOCode,
}
}
func toCityResponse(city geolocation.City) api.City {
return api.City{
CityName: city.CityName,
GeonameId: city.GeoNameID,
}
}

View File

@@ -240,10 +240,9 @@ func (h *GroupsHandler) GetGroup(w http.ResponseWriter, r *http.Request) {
func toGroupResponse(account *server.Account, group *server.Group) *api.Group {
cache := make(map[string]api.PeerMinimum)
gr := api.Group{
Id: group.ID,
Name: group.Name,
PeersCount: len(group.Peers),
Issued: &group.Issued,
Id: group.ID,
Name: group.Name,
Issued: &group.Issued,
}
for _, pid := range group.Peers {
@@ -261,5 +260,8 @@ func toGroupResponse(account *server.Account, group *server.Group) *api.Group {
gr.Peers = append(gr.Peers, peerResp)
}
}
gr.PeersCount = len(gr.Peers)
return &gr
}

View File

@@ -1,6 +1,8 @@
package http
import (
"context"
"fmt"
"net/http"
"github.com/gorilla/mux"
@@ -9,11 +11,14 @@ import (
"github.com/netbirdio/management-integrations/integrations"
s "github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/geolocation"
"github.com/netbirdio/netbird/management/server/http/middleware"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/telemetry"
)
const apiPrefix = "/api"
// AuthCfg contains parameters for authentication middleware
type AuthCfg struct {
Issuer string
@@ -23,9 +28,10 @@ type AuthCfg struct {
}
type apiHandler struct {
Router *mux.Router
AccountManager s.AccountManager
AuthCfg AuthCfg
Router *mux.Router
AccountManager s.AccountManager
geolocationManager *geolocation.Geolocation
AuthCfg AuthCfg
}
// EmptyObject is an empty struct used to return empty JSON object
@@ -33,7 +39,7 @@ type emptyObject struct {
}
// APIHandler creates the Management service HTTP API handler registering all the available endpoints.
func APIHandler(accountManager s.AccountManager, jwtValidator jwtclaims.JWTValidator, appMetrics telemetry.AppMetrics, authCfg AuthCfg) (http.Handler, error) {
func APIHandler(ctx context.Context, accountManager s.AccountManager, LocationManager *geolocation.Geolocation, jwtValidator jwtclaims.JWTValidator, appMetrics telemetry.AppMetrics, authCfg AuthCfg) (http.Handler, error) {
claimsExtractor := jwtclaims.NewClaimsExtractor(
jwtclaims.WithAudience(authCfg.Audience),
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
@@ -59,16 +65,21 @@ func APIHandler(accountManager s.AccountManager, jwtValidator jwtclaims.JWTValid
rootRouter := mux.NewRouter()
metricsMiddleware := appMetrics.HTTPMiddleware()
router := rootRouter.PathPrefix("/api").Subrouter()
prefix := apiPrefix
router := rootRouter.PathPrefix(prefix).Subrouter()
router.Use(metricsMiddleware.Handler, corsMiddleware.Handler, authMiddleware.Handler, acMiddleware.Handler)
api := apiHandler{
Router: router,
AccountManager: accountManager,
AuthCfg: authCfg,
Router: router,
AccountManager: accountManager,
geolocationManager: LocationManager,
AuthCfg: authCfg,
}
if _, err := integrations.RegisterHandlers(ctx, prefix, api.Router, accountManager, claimsExtractor); err != nil {
return nil, fmt.Errorf("register integrations endpoints: %w", err)
}
integrations.RegisterHandlers(api.Router, accountManager, claimsExtractor)
api.addAccountsEndpoint()
api.addPeersEndpoint()
api.addUsersEndpoint()
@@ -81,6 +92,8 @@ func APIHandler(accountManager s.AccountManager, jwtValidator jwtclaims.JWTValid
api.addDNSNameserversEndpoint()
api.addDNSSettingEndpoint()
api.addEventsEndpoint()
api.addPostureCheckEndpoint()
api.addLocationsEndpoint()
err := api.Router.Walk(func(route *mux.Route, _ *mux.Router, _ []*mux.Route) error {
methods, err := route.GetMethods()
@@ -200,3 +213,18 @@ func (apiHandler *apiHandler) addEventsEndpoint() {
eventsHandler := NewEventsHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
apiHandler.Router.HandleFunc("/events", eventsHandler.GetAllEvents).Methods("GET", "OPTIONS")
}
func (apiHandler *apiHandler) addPostureCheckEndpoint() {
postureCheckHandler := NewPostureChecksHandler(apiHandler.AccountManager, apiHandler.geolocationManager, apiHandler.AuthCfg)
apiHandler.Router.HandleFunc("/posture-checks", postureCheckHandler.GetAllPostureChecks).Methods("GET", "OPTIONS")
apiHandler.Router.HandleFunc("/posture-checks", postureCheckHandler.CreatePostureCheck).Methods("POST", "OPTIONS")
apiHandler.Router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.UpdatePostureCheck).Methods("PUT", "OPTIONS")
apiHandler.Router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.GetPostureCheck).Methods("GET", "OPTIONS")
apiHandler.Router.HandleFunc("/posture-checks/{postureCheckId}", postureCheckHandler.DeletePostureCheck).Methods("DELETE", "OPTIONS")
}
func (apiHandler *apiHandler) addLocationsEndpoint() {
locationHandler := NewGeolocationsHandlerHandler(apiHandler.AccountManager, apiHandler.geolocationManager, apiHandler.AuthCfg)
apiHandler.Router.HandleFunc("/locations/countries", locationHandler.GetAllCountries).Methods("GET", "OPTIONS")
apiHandler.Router.HandleFunc("/locations/countries/{country}/cities", locationHandler.GetCitiesByCountry).Methods("GET", "OPTIONS")
}

View File

@@ -7,6 +7,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/http/middleware/bypass"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/status"
@@ -36,9 +37,13 @@ func NewAccessControl(audience, userIDClaim string, getUser GetUser) *AccessCont
var tokenPathRegexp = regexp.MustCompile(`^.*/api/users/.*/tokens.*$`)
// Handler method of the middleware which forbids all modify requests for non admin users
// It also adds
func (a *AccessControl) Handler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if bypass.ShouldBypass(r.URL.Path, h, w, r) {
return
}
claims := a.claimsExtract.FromRequestContext(r)
user, err := a.getUser(claims)

View File

@@ -12,6 +12,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/http/middleware/bypass"
"github.com/netbirdio/netbird/management/server/http/util"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/management/server/status"
@@ -66,6 +67,11 @@ func NewAuthMiddleware(getAccountFromPAT GetAccountFromPATFunc, validateAndParse
// Handler method of the middleware which authenticates a user either by JWT claims or by PAT
func (m *AuthMiddleware) Handler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if bypass.ShouldBypass(r.URL.Path, h, w, r) {
return
}
auth := strings.Split(r.Header.Get("Authorization"), " ")
authType := strings.ToLower(auth[0])

View File

@@ -10,6 +10,7 @@ import (
"github.com/golang-jwt/jwt"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/http/middleware/bypass"
"github.com/netbirdio/netbird/management/server/jwtclaims"
)
@@ -88,39 +89,68 @@ func mockCheckUserAccessByJWTGroups(claims jwtclaims.AuthorizationClaims) error
func TestAuthMiddleware_Handler(t *testing.T) {
tt := []struct {
name string
path string
authHeader string
expectedStatusCode int
shouldBypassAuth bool
}{
{
name: "Valid PAT Token",
path: "/test",
authHeader: "Token " + PAT,
expectedStatusCode: 200,
},
{
name: "Invalid PAT Token",
path: "/test",
authHeader: "Token " + wrongToken,
expectedStatusCode: 401,
},
{
name: "Fallback to PAT Token",
path: "/test",
authHeader: "Bearer " + PAT,
expectedStatusCode: 200,
},
{
name: "Valid JWT Token",
path: "/test",
authHeader: "Bearer " + JWT,
expectedStatusCode: 200,
},
{
name: "Invalid JWT Token",
path: "/test",
authHeader: "Bearer " + wrongToken,
expectedStatusCode: 401,
},
{
name: "Basic Auth",
path: "/test",
authHeader: "Basic " + PAT,
expectedStatusCode: 401,
},
{
name: "Webhook Path Bypass",
path: "/webhook",
authHeader: "",
expectedStatusCode: 200,
shouldBypassAuth: true,
},
{
name: "Webhook Path Bypass with Subpath",
path: "/webhook/test",
authHeader: "",
expectedStatusCode: 200,
shouldBypassAuth: true,
},
{
name: "Different Webhook Path",
path: "/webhooktest",
authHeader: "",
expectedStatusCode: 401,
shouldBypassAuth: false,
},
}
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -146,7 +176,14 @@ func TestAuthMiddleware_Handler(t *testing.T) {
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
req := httptest.NewRequest("GET", "http://testing", nil)
if tc.shouldBypassAuth {
err := bypass.AddBypassPath(tc.path)
if err != nil {
t.Fatalf("failed to add bypass path: %v", err)
}
}
req := httptest.NewRequest("GET", "http://testing"+tc.path, nil)
req.Header.Set("Authorization", tc.authHeader)
rec := httptest.NewRecorder()
@@ -159,5 +196,4 @@ func TestAuthMiddleware_Handler(t *testing.T) {
}
})
}
}

View File

@@ -0,0 +1,74 @@
package bypass
import (
"fmt"
"net/http"
"path"
"sync"
log "github.com/sirupsen/logrus"
)
var byPassMutex sync.RWMutex
// bypassPaths is a set of paths that should bypass middleware.
var bypassPaths = make(map[string]struct{})
// AddBypassPath adds an exact path to the list of paths that bypass middleware.
// Paths can include wildcards, such as /api/*. Paths are matched using path.Match.
// Returns an error if the path has invalid pattern.
func AddBypassPath(path string) error {
byPassMutex.Lock()
defer byPassMutex.Unlock()
if err := validatePath(path); err != nil {
return fmt.Errorf("validate: %w", err)
}
bypassPaths[path] = struct{}{}
return nil
}
// RemovePath removes a path from the list of paths that bypass middleware.
func RemovePath(path string) {
byPassMutex.Lock()
defer byPassMutex.Unlock()
delete(bypassPaths, path)
}
// GetList returns a list of all bypass paths.
func GetList() []string {
byPassMutex.RLock()
defer byPassMutex.RUnlock()
list := make([]string, 0, len(bypassPaths))
for k := range bypassPaths {
list = append(list, k)
}
return list
}
// ShouldBypass checks if the request path is one of the auth bypass paths and returns true if the middleware should be bypassed.
// This can be used to bypass authz/authn middlewares for certain paths, such as webhooks that implement their own authentication.
func ShouldBypass(requestPath string, h http.Handler, w http.ResponseWriter, r *http.Request) bool {
byPassMutex.RLock()
defer byPassMutex.RUnlock()
for bypassPath := range bypassPaths {
matched, err := path.Match(bypassPath, requestPath)
if err != nil {
log.Errorf("Error matching path %s with %s from %s: %v", bypassPath, requestPath, GetList(), err)
continue
}
if matched {
h.ServeHTTP(w, r)
return true
}
}
return false
}
func validatePath(p string) error {
_, err := path.Match(p, "")
return err
}

View File

@@ -0,0 +1,131 @@
package bypass_test
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/management/server/http/middleware/bypass"
)
func TestGetList(t *testing.T) {
bypassPaths := []string{"/path1", "/path2", "/path3"}
for _, path := range bypassPaths {
err := bypass.AddBypassPath(path)
require.NoError(t, err, "Adding bypass path should not fail")
}
list := bypass.GetList()
assert.ElementsMatch(t, bypassPaths, list, "Bypass path list did not match expected paths")
}
func TestAuthBypass(t *testing.T) {
dummyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
tests := []struct {
name string
pathToAdd string
pathToRemove string
testPath string
expectBypass bool
expectHTTPCode int
}{
{
name: "Path added to bypass",
pathToAdd: "/bypass",
testPath: "/bypass",
expectBypass: true,
expectHTTPCode: http.StatusOK,
},
{
name: "Wildcard path added to bypass",
pathToAdd: "/bypass/*",
testPath: "/bypass/extra",
expectBypass: true,
expectHTTPCode: http.StatusOK,
},
{
name: "Path not added to bypass",
testPath: "/no-bypass",
expectBypass: false,
expectHTTPCode: http.StatusOK,
},
{
name: "Path removed from bypass",
pathToAdd: "/remove-bypass",
pathToRemove: "/remove-bypass",
testPath: "/remove-bypass",
expectBypass: false,
expectHTTPCode: http.StatusOK,
},
{
name: "Exact path matches bypass",
pathToAdd: "/webhook",
testPath: "/webhook",
expectBypass: true,
expectHTTPCode: http.StatusOK,
},
{
name: "Subpath does not match bypass",
pathToAdd: "/webhook",
testPath: "/webhook/extra",
expectBypass: false,
expectHTTPCode: http.StatusOK,
},
{
name: "Wildcard subpath does not match bypass",
pathToAdd: "/webhook/*",
testPath: "/webhook/extra/path",
expectBypass: false,
expectHTTPCode: http.StatusOK,
},
{
name: "Similar path does not match bypass",
pathToAdd: "/webhook",
testPath: "/webhooking",
expectBypass: false,
expectHTTPCode: http.StatusOK,
},
{
name: "Prefix path does not match bypass",
pathToAdd: "/webhook",
testPath: "/web",
expectBypass: false,
expectHTTPCode: http.StatusOK,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
if tc.pathToAdd != "" {
err := bypass.AddBypassPath(tc.pathToAdd)
require.NoError(t, err, "Adding bypass path should not fail")
defer bypass.RemovePath(tc.pathToAdd)
}
if tc.pathToRemove != "" {
bypass.RemovePath(tc.pathToRemove)
}
request, err := http.NewRequest("GET", tc.testPath, nil)
require.NoError(t, err, "Creating request should not fail")
recorder := httptest.NewRecorder()
bypassed := bypass.ShouldBypass(tc.testPath, dummyHandler, recorder, request)
assert.Equal(t, tc.expectBypass, bypassed, "Bypass check did not match expectation")
if tc.expectBypass {
assert.Equal(t, tc.expectHTTPCode, recorder.Code, "HTTP status code did not match expectation for bypassed path")
}
})
}
}

View File

@@ -231,48 +231,68 @@ func toGroupsInfo(groups map[string]*server.Group, peerID string) []api.GroupMin
}
func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, accessiblePeer []api.AccessiblePeer) *api.Peer {
osVersion := peer.Meta.OSVersion
if osVersion == "" {
osVersion = peer.Meta.Core
}
return &api.Peer{
Id: peer.ID,
Name: peer.Name,
Ip: peer.IP.String(),
ConnectionIp: peer.Location.ConnectionIP.String(),
Connected: peer.Status.Connected,
LastSeen: peer.Status.LastSeen,
Os: fmt.Sprintf("%s %s", peer.Meta.OS, peer.Meta.Core),
Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion),
KernelVersion: peer.Meta.KernelVersion,
GeonameId: int(peer.Location.GeoNameID),
Version: peer.Meta.WtVersion,
Groups: groupsInfo,
SshEnabled: peer.SSHEnabled,
Hostname: peer.Meta.Hostname,
UserId: &peer.UserID,
UiVersion: &peer.Meta.UIVersion,
UserId: peer.UserID,
UiVersion: peer.Meta.UIVersion,
DnsLabel: fqdn(peer, dnsDomain),
LoginExpirationEnabled: peer.LoginExpirationEnabled,
LastLogin: peer.LastLogin,
LoginExpired: peer.Status.LoginExpired,
AccessiblePeers: accessiblePeer,
ApprovalRequired: &peer.Status.RequiresApproval,
CountryCode: peer.Location.CountryCode,
CityName: peer.Location.CityName,
}
}
func toPeerListItemResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, accessiblePeersCount int) *api.PeerBatch {
osVersion := peer.Meta.OSVersion
if osVersion == "" {
osVersion = peer.Meta.Core
}
return &api.PeerBatch{
Id: peer.ID,
Name: peer.Name,
Ip: peer.IP.String(),
ConnectionIp: peer.Location.ConnectionIP.String(),
Connected: peer.Status.Connected,
LastSeen: peer.Status.LastSeen,
Os: fmt.Sprintf("%s %s", peer.Meta.OS, peer.Meta.Core),
Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion),
KernelVersion: peer.Meta.KernelVersion,
GeonameId: int(peer.Location.GeoNameID),
Version: peer.Meta.WtVersion,
Groups: groupsInfo,
SshEnabled: peer.SSHEnabled,
Hostname: peer.Meta.Hostname,
UserId: &peer.UserID,
UiVersion: &peer.Meta.UIVersion,
UserId: peer.UserID,
UiVersion: peer.Meta.UIVersion,
DnsLabel: fqdn(peer, dnsDomain),
LoginExpirationEnabled: peer.LoginExpirationEnabled,
LastLogin: peer.LastLogin,
LoginExpired: peer.Status.LoginExpired,
AccessiblePeersCount: accessiblePeersCount,
ApprovalRequired: &peer.Status.RequiresApproval,
CountryCode: peer.Location.CountryCode,
CityName: peer.Location.CityName,
}
}

View File

@@ -206,6 +206,10 @@ func (h *Policies) savePolicy(
policy.Rules = append(policy.Rules, &pr)
}
if req.SourcePostureChecks != nil {
policy.SourcePostureChecks = sourcePostureChecksToStrings(account, *req.SourcePostureChecks)
}
if err := h.accountManager.SavePolicy(account.Id, user.Id, &policy); err != nil {
util.WriteError(err, w)
return
@@ -284,10 +288,11 @@ func (h *Policies) GetPolicy(w http.ResponseWriter, r *http.Request) {
func toPolicyResponse(account *server.Account, policy *server.Policy) *api.Policy {
cache := make(map[string]api.GroupMinimum)
ap := &api.Policy{
Id: &policy.ID,
Name: policy.Name,
Description: policy.Description,
Enabled: policy.Enabled,
Id: &policy.ID,
Name: policy.Name,
Description: policy.Description,
Enabled: policy.Enabled,
SourcePostureChecks: policy.SourcePostureChecks,
}
for _, r := range policy.Rules {
rID := r.ID
@@ -351,3 +356,17 @@ func groupMinimumsToStrings(account *server.Account, gm []string) []string {
}
return result
}
func sourcePostureChecksToStrings(account *server.Account, postureChecksIds []string) []string {
result := make([]string, 0, len(postureChecksIds))
for _, id := range postureChecksIds {
for _, postureCheck := range account.PostureChecks {
if id == postureCheck.ID {
result = append(result, id)
continue
}
}
}
return result
}

Some files were not shown because too many files have changed in this diff Show More