Compare commits

...

36 Commits

Author SHA1 Message Date
Hugo Hakim Damer
8b0398c0db Add support for IPv6 networks (on Linux clients) (#1459)
* Feat add basic support for IPv6 networks

Newly generated networks automatically generate an IPv6 prefix of size
64 within the ULA address range, devices obtain a randomly generated
address within this prefix.

Currently, this is Linux only and does not yet support all features
(routes currently cause an error).

* Fix firewall configuration for IPv6 networks

* Fix routing configuration for IPv6 networks

* Feat provide info on IPv6 support for specific client to mgmt server

* Feat allow configuration of IPv6 support through API, improve stability

* Feat add IPv6 support to new firewall implementation

* Fix peer list item response not containing IPv6 address

* Fix nftables breaking on IPv6 address change

* Fix build issues for non-linux systems

* Fix intermittent disconnections when IPv6 is enabled

* Fix test issues and make some minor revisions

* Fix some more testing issues

* Fix more CI issues due to IPv6

* Fix more testing issues

* Add inheritance of IPv6 enablement status from groups

* Fix IPv6 events not having associated messages

* Address first review comments regarding IPv6 support

* Fix IPv6 table being created even when IPv6 is disabled

Also improved stability of IPv6 route and firewall handling on client side

* Fix IPv6 routes not being removed

* Fix DNS IPv6 issues, limit IPv6 nameservers to IPv6 peers

* Improve code for IPv6 DNS server selection, add AAAA custom records

* Ensure IPv6 routes can only exist for IPv6 routing peers

* Fix IPv6 network generation randomness

* Fix a bunch of compilation issues and test failures

* Replace method calls that are unavailable in Go 1.21

* Fix nil dereference in cleanUpDefaultForwardRules6

* Fix nil pointer dereference when persisting IPv6 network in sqlite

* Clean up of client-side code changes for IPv6

* Fix nil dereference in rule mangling and compilation issues

* Add a bunch of client-side test cases for IPv6

* Fix IPv6 tests running on unsupported environments

* Fix import cycle in tests

* Add missing method SupportsIPv6() for windows

* Require IPv6 default route for IPv6 tests

* Fix panics in routemanager tests on non-linux

* Fix some more route manager tests concerning IPv6

* Add some final client-side tests

* Add IPv6 tests for management code, small fixes

* Fix linting issues

* Fix small test suite issues

* Fix linter issues and builds on macOS and Windows again

* fix builds for iOS because of IPv6 breakage
2024-08-13 17:26:27 +02:00
Gabriel Górski
4da29451d0 Add missing openid scope when requesting JWT token (#2089)
According to the Zitadel documentation, `openid` scope is required
when requesting JWT tokens.

Apparently Zitadel was accepting requests without it until very
recently. Now lack thereof causes 400 Bad Requests which makes it
impossible to authenticate to the Netbird dashboard.

https://zitadel.com/docs/guides/integrate/service-users/client-credentials#2-authenticating-a-service-user-and-request-a-token
2024-06-04 10:46:24 +02:00
Viktor Liu
9b3449753e Ignore candidates whose IP falls into a routed network. (#2084)
This will prevent peer connections via other peers.
2024-06-03 17:31:37 +02:00
Maycon Santos
456629811b Prevent using expired ctx when sending metrics (#2088) 2024-06-03 12:41:15 +02:00
Zoltan Papp
c311d0d19e Fill the UI version info in system meta on Android (#2077) 2024-05-31 17:26:56 +02:00
pascal-fischer
521f7dd39f Improve login performance (#2061) 2024-05-31 16:41:12 +02:00
pascal-fischer
f9ec0a9a2e Fix PKCE auth html (#2079) 2024-05-30 17:22:58 +02:00
pascal-fischer
012235ff12 Add FindExistingPostureCheck (#2075) 2024-05-30 15:22:42 +02:00
Maycon Santos
f176807ebe Add extra logs for account not found, peer login and getAccount (#2053) 2024-05-27 12:29:28 +02:00
Maycon Santos
d4c47eaf8a Don't allow delete group from peer groups (#2055) 2024-05-27 11:06:43 +02:00
Bethuel Mmbaga
d35a79d3b5 Upgrade gRPC and OpenTelemetry packages for compatibility (#2003)
Upgrades `go.opentelemetry.io/otel` from version` v1.11.1` to `v1.26.0`. The upgrade addresses compatibility issues caused by the removal of several sub-packages in the latest OpenTelemetry release, which were causing broken dependencies.

**Key Changes:**
- Upgraded `go.opentelemetry.io/otel` from `v1.11.1` to `v1.26.0`.

- Fixed broken dependencies by replacing the deprecated sub-packages:
  - `go.opentelemetry.io/otel/metric/instrument`
  - `go.opentelemetry.io/otel/metric/instrument/asyncint64`
  - `go.opentelemetry.io/otel/metric/instrument/syncint64`
  
- Upgraded `google.golang.org/grpc` from `v1.56.3`  to `v1.64.0` which deprecate `Dial` and `DialContext` to `NewClient`.
2024-05-27 08:39:18 +02:00
Maycon Santos
6a2929011d Refactor firewall manager check (#2054)
Some systems don't play nice with a test chain
So we dropped the idea, and instead we check for the filter table

With this check, we might face a case where iptables is selected once and on the 
next netbird up/down it will go back to using nftables
2024-05-27 08:37:32 +02:00
Maycon Santos
e877c9d6c1 Update CODE_OF_CONDUCT.md (#2048) 2024-05-24 17:29:14 +02:00
Maycon Santos
7a1c96ebf4 Remove extra error mapping (#2050) 2024-05-24 14:46:11 +02:00
Zoltan Papp
41fe9f84ec Extend integrated validator with error handling (#2044) 2024-05-24 13:29:25 +02:00
Viktor Liu
d13fb0e379 Restore netbird state and log level after debug (#2047) 2024-05-24 13:27:41 +02:00
Maycon Santos
f3214527ea Use info log-level for firewall manager discover (#2045)
* Use info log-level for firewall manager discover

* Update client/firewall/create_linux.go

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>

---------

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>
2024-05-24 13:03:19 +02:00
Maycon Santos
69048bfd34 Revert "Accept any XDG_ environment variable to determine desktop (#2037)" (#2042)
This reverts commit 67e2185964.
2024-05-23 23:15:02 +02:00
Maycon Santos
29a2d93873 Log global lock acquisition per user (#2039) 2024-05-23 17:09:58 +02:00
Maycon Santos
6b01b0020e Enhance firewall manager checks to detect unsupported iptables (#2038)
Our nftables firewall manager may cause issues when rules are created using older iptable versions
2024-05-23 16:09:51 +02:00
Maycon Santos
9d3db68805 Return the proper error when a peer is deleted (#2035)
this fixes an issue causing peers to keep retrying the connection after a peer is removed from the management system
2024-05-23 14:59:09 +02:00
Maycon Santos
2e315311e0 Fix the initial daemon retry interval (#2036) 2024-05-23 14:52:52 +02:00
Maycon Santos
67e2185964 Accept any XDG_ environment variable to determine desktop (#2037) 2024-05-23 12:34:19 +02:00
Maycon Santos
89149dc6f4 Increase the status checks timeout (#2033)
Some systems might respond with a small delay depending on various factors. Increasing the timeout to reduce the number of false-positive reports
2024-05-23 10:54:01 +02:00
Matthew R Kasun
5a1f8f13a2 use the next available port for wireguard (#2024)
check if WgPort is available, if not find the next free port
2024-05-22 18:42:56 +02:00
Viktor Liu
e71059d245 Add dummy ipv6 to macos interface (#2025) 2024-05-22 12:32:01 +02:00
Maycon Santos
91fa2e20a0 Store location information in peer event meta (#1994) 2024-05-22 12:31:16 +02:00
Zoltan Papp
61034aaf4d Gracefully conn worker shutdown (#2022)
Because the connWorker are operating with the e.peerConns list we must ensure all workers exited before we modify the content of the e.peerConns list.
If we do not do that the engine will start new connWorkers for the exists ones, and they start connection for the same peers in parallel.
2024-05-22 11:15:29 +02:00
Maycon Santos
b8717b8956 Update the GUI status when daemon unavailable (#2012)
in case we got no status we mark the GUI app as disconnected
2024-05-21 15:45:49 +02:00
pascal-fischer
50201d63c2 Increase garbage collection on ios (#1981) 2024-05-17 15:58:29 +02:00
pascal-fischer
d11b39282b Enable namserver deactivation if unresponsive on iOS (#1982) 2024-05-17 12:59:46 +02:00
Viktor Liu
bd58eea8ea Refactor network monitor to wait for stop (#1992) 2024-05-17 09:43:18 +02:00
Bethuel Mmbaga
a5811a2d7d Implement experimental PostgreSQL store (#1939)
* migrate sqlite store to
 generic sql store

* fix conflicts

* init postgres store

* Add postgres store tests

* Refactor postgres store engine name

* fix tests

* Run postgres store tests on linux only

* fix tests

* Refactor

* cascade policy rules on policy deletion

* fix tests

* run postgres cases in new db

* close store connection after tests

* refactor

* using testcontainers

* sync go sum

* remove postgres service

* remove store cleanup

* go mod tidy

* remove env

* use postgres as engine and initialize test store with testcontainer

---------

Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
2024-05-16 19:28:37 +03:00
Bethuel Mmbaga
a680f80ed9 Add installer support for Synology (#1984)
* add installer support for the synology

* skip ui installation for Synology

* Fix conflicts
2024-05-15 19:03:49 +03:00
Thorleif Jacobsen
10fbdc2c4a CentOS installations might have "apt" as "annotation processing tool", fixed so it checks for apt-get (#1955) 2024-05-15 16:33:12 +02:00
Viktor Liu
1444fbe104 Don't cancel proxy ctx on conn close (#1986) 2024-05-15 09:10:57 +02:00
140 changed files with 6607 additions and 2019 deletions

View File

@@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
arch: [ '386','amd64' ]
store: [ 'jsonfile', 'sqlite' ]
store: [ 'jsonfile', 'sqlite', 'postgres']
runs-on: ubuntu-latest
steps:
- name: Install Go

View File

@@ -130,3 +130,10 @@ issues:
- path: mock\.go
linters:
- nilnil
# Exclude specific deprecation warnings for grpc methods
- linters:
- staticcheck
text: "grpc.DialContext is deprecated"
- linters:
- staticcheck
text: "grpc.WithBlock is deprecated"

View File

@@ -5,7 +5,7 @@
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
identity and expression, level of experience, education, socioeconomic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.

View File

@@ -57,15 +57,17 @@ type Client struct {
ctxCancel context.CancelFunc
ctxCancelLock *sync.Mutex
deviceName string
uiVersion string
networkChangeListener listener.NetworkChangeListener
}
// NewClient instantiate a new Client
func NewClient(cfgFile, deviceName string, tunAdapter TunAdapter, iFaceDiscover IFaceDiscover, networkChangeListener NetworkChangeListener) *Client {
func NewClient(cfgFile, deviceName string, uiVersion string, tunAdapter TunAdapter, iFaceDiscover IFaceDiscover, networkChangeListener NetworkChangeListener) *Client {
net.SetAndroidProtectSocketFn(tunAdapter.ProtectSocket)
return &Client{
cfgFile: cfgFile,
deviceName: deviceName,
uiVersion: uiVersion,
tunAdapter: tunAdapter,
iFaceDiscover: iFaceDiscover,
recorder: peer.NewRecorder(""),
@@ -88,6 +90,9 @@ func (c *Client) Run(urlOpener URLOpener, dns *DNSList, dnsReadyListener DnsRead
var ctx context.Context
//nolint
ctxWithValues := context.WithValue(context.Background(), system.DeviceNameCtxKey, c.deviceName)
//nolint
ctxWithValues = context.WithValue(ctxWithValues, system.UiVersionCtxKey, c.uiVersion)
c.ctxCancelLock.Lock()
ctx, c.ctxCancel = context.WithCancel(ctxWithValues)
defer c.ctxCancel()

View File

@@ -3,13 +3,14 @@ package cmd
import (
"context"
"fmt"
"strings"
"time"
"github.com/spf13/cobra"
"google.golang.org/grpc/status"
"github.com/netbirdio/netbird/client/internal"
"github.com/netbirdio/netbird/client/proto"
"github.com/netbirdio/netbird/client/server"
)
var debugCmd = &cobra.Command{
@@ -86,7 +87,7 @@ func setLogLevel(cmd *cobra.Command, args []string) error {
defer conn.Close()
client := proto.NewDaemonServiceClient(conn)
level := parseLogLevel(args[0])
level := server.ParseLogLevel(args[0])
if level == proto.LogLevel_UNKNOWN {
return fmt.Errorf("unknown log level: %s. Available levels are: panic, fatal, error, warn, info, debug, trace\n", args[0])
}
@@ -102,27 +103,6 @@ func setLogLevel(cmd *cobra.Command, args []string) error {
return nil
}
func parseLogLevel(level string) proto.LogLevel {
switch strings.ToLower(level) {
case "panic":
return proto.LogLevel_PANIC
case "fatal":
return proto.LogLevel_FATAL
case "error":
return proto.LogLevel_ERROR
case "warn":
return proto.LogLevel_WARN
case "info":
return proto.LogLevel_INFO
case "debug":
return proto.LogLevel_DEBUG
case "trace":
return proto.LogLevel_TRACE
default:
return proto.LogLevel_UNKNOWN
}
}
func runForDuration(cmd *cobra.Command, args []string) error {
duration, err := time.ParseDuration(args[0])
if err != nil {
@@ -137,18 +117,33 @@ func runForDuration(cmd *cobra.Command, args []string) error {
client := proto.NewDaemonServiceClient(conn)
stat, err := client.Status(cmd.Context(), &proto.StatusRequest{})
if err != nil {
return fmt.Errorf("failed to get status: %v", status.Convert(err).Message())
}
restoreUp := stat.Status == string(internal.StatusConnected) || stat.Status == string(internal.StatusConnecting)
initialLogLevel, err := client.GetLogLevel(cmd.Context(), &proto.GetLogLevelRequest{})
if err != nil {
return fmt.Errorf("failed to get log level: %v", status.Convert(err).Message())
}
if _, err := client.Down(cmd.Context(), &proto.DownRequest{}); err != nil {
return fmt.Errorf("failed to down: %v", status.Convert(err).Message())
}
cmd.Println("Netbird down")
_, err = client.SetLogLevel(cmd.Context(), &proto.SetLogLevelRequest{
Level: proto.LogLevel_TRACE,
})
if err != nil {
return fmt.Errorf("failed to set log level to trace: %v", status.Convert(err).Message())
initialLevelTrace := initialLogLevel.GetLevel() >= proto.LogLevel_TRACE
if !initialLevelTrace {
_, err = client.SetLogLevel(cmd.Context(), &proto.SetLogLevelRequest{
Level: proto.LogLevel_TRACE,
})
if err != nil {
return fmt.Errorf("failed to set log level to TRACE: %v", status.Convert(err).Message())
}
cmd.Println("Log level set to trace.")
}
cmd.Println("Log level set to trace.")
time.Sleep(1 * time.Second)
@@ -175,10 +170,22 @@ func runForDuration(cmd *cobra.Command, args []string) error {
}
cmd.Println("Netbird down")
// TODO reset log level
time.Sleep(1 * time.Second)
if restoreUp {
if _, err := client.Up(cmd.Context(), &proto.UpRequest{}); err != nil {
return fmt.Errorf("failed to up: %v", status.Convert(err).Message())
}
cmd.Println("Netbird up")
}
if !initialLevelTrace {
if _, err := client.SetLogLevel(cmd.Context(), &proto.SetLogLevelRequest{Level: initialLogLevel.GetLevel()}); err != nil {
return fmt.Errorf("failed to restore log level: %v", status.Convert(err).Message())
}
cmd.Println("Log level restored to", initialLogLevel.GetLevel())
}
cmd.Println("Creating debug bundle...")
resp, err := client.DebugBundle(cmd.Context(), &proto.DebugBundleRequest{

View File

@@ -2,9 +2,10 @@ package cmd
import (
"context"
"github.com/netbirdio/netbird/util"
"time"
"github.com/netbirdio/netbird/util"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"

View File

@@ -14,6 +14,7 @@ import (
"google.golang.org/grpc"
"github.com/netbirdio/management-integrations/integrations"
clientProto "github.com/netbirdio/netbird/client/proto"
client "github.com/netbirdio/netbird/client/server"
mgmtProto "github.com/netbirdio/netbird/management/proto"
@@ -69,10 +70,11 @@ func startManagement(t *testing.T, config *mgmt.Config) (*grpc.Server, net.Liste
t.Fatal(err)
}
s := grpc.NewServer()
store, err := mgmt.NewStoreFromJson(config.Datadir, nil)
store, cleanUp, err := mgmt.NewTestStoreFromJson(config.Datadir)
if err != nil {
t.Fatal(err)
}
t.Cleanup(cleanUp)
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}

View File

@@ -30,3 +30,9 @@ func NewFirewall(context context.Context, iface IFaceMapper) (firewall.Manager,
}
return fm, nil
}
// Returns true if the current firewall implementation supports IPv6.
// Currently false for anything non-linux.
func SupportsIPv6() bool {
return false
}

View File

@@ -42,20 +42,20 @@ func NewFirewall(context context.Context, iface IFaceMapper) (firewall.Manager,
switch check() {
case IPTABLES:
log.Debug("creating an iptables firewall manager")
log.Info("creating an iptables firewall manager")
fm, errFw = nbiptables.Create(context, iface)
if errFw != nil {
log.Errorf("failed to create iptables manager: %s", errFw)
}
case NFTABLES:
log.Debug("creating an nftables firewall manager")
log.Info("creating an nftables firewall manager")
fm, errFw = nbnftables.Create(context, iface)
if errFw != nil {
log.Errorf("failed to create nftables manager: %s", errFw)
}
default:
errFw = fmt.Errorf("no firewall manager found")
log.Debug("no firewall manager found, try to use userspace packet filtering firewall")
log.Info("no firewall manager found, trying to use userspace packet filtering firewall")
}
if iface.IsUserspaceBind() {
@@ -70,6 +70,8 @@ func NewFirewall(context context.Context, iface IFaceMapper) (firewall.Manager,
return nil, errUsp
}
// Note for devs: When adding IPv6 support to userspace bind, the implementation of AllowNetbird() has to be
// adjusted accordingly.
if err := fm.AllowNetbird(); err != nil {
log.Errorf("failed to allow netbird interface traffic: %v", err)
}
@@ -83,18 +85,66 @@ func NewFirewall(context context.Context, iface IFaceMapper) (firewall.Manager,
return fm, nil
}
// Returns true if the current firewall implementation supports IPv6.
// Currently true if the firewall is nftables.
func SupportsIPv6() bool {
return check() == NFTABLES
}
// check returns the firewall type based on common lib checks. It returns UNKNOWN if no firewall is found.
func check() FWType {
nf := nftables.Conn{}
if _, err := nf.ListChains(); err == nil && os.Getenv(SKIP_NFTABLES_ENV) != "true" {
return NFTABLES
useIPTABLES := false
var iptablesChains []string
ip, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
if err == nil && isIptablesClientAvailable(ip) {
major, minor, _ := ip.GetIptablesVersion()
// use iptables when its version is lower than 1.8.0 which doesn't work well with our nftables manager
if major < 1 || (major == 1 && minor < 8) {
return IPTABLES
}
useIPTABLES = true
iptablesChains, err = ip.ListChains("filter")
if err != nil {
log.Errorf("failed to list iptables chains: %s", err)
useIPTABLES = false
}
}
ip, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
if err != nil {
return UNKNOWN
nf := nftables.Conn{}
if chains, err := nf.ListChains(); err == nil && os.Getenv(SKIP_NFTABLES_ENV) != "true" {
if !useIPTABLES {
return NFTABLES
}
// search for chains where table is filter
// if we find one, we assume that nftables manager can be used with iptables
for _, chain := range chains {
if chain.Table.Name == "filter" {
return NFTABLES
}
}
// check tables for the following constraints:
// 1. there is no chain in nftables for the filter table and there is at least one chain in iptables, we assume that nftables manager can not be used
// 2. there is no tables or more than one table, we assume that nftables manager can be used
// 3. there is only one table and its name is filter, we assume that nftables manager can not be used, since there was no chain in it
// 4. if we find an error we log and continue with iptables check
nbTablesList, err := nf.ListTables()
switch {
case err == nil && len(iptablesChains) > 0:
return IPTABLES
case err == nil && len(nbTablesList) != 1:
return NFTABLES
case err == nil && len(nbTablesList) == 1 && nbTablesList[0].Name == "filter":
return IPTABLES
case err != nil:
log.Errorf("failed to list nftables tables on fw manager discovery: %s", err)
}
}
if isIptablesClientAvailable(ip) {
if useIPTABLES {
return IPTABLES
}

View File

@@ -6,6 +6,7 @@ import "github.com/netbirdio/netbird/iface"
type IFaceMapper interface {
Name() string
Address() iface.WGAddress
Address6() *iface.WGAddress
IsUserspaceBind() bool
SetFilter(iface.PacketFilter) error
}

View File

@@ -24,6 +24,14 @@ type Manager struct {
router *routerManager
}
func (m *Manager) ResetV6Firewall() error {
return nil
}
func (m *Manager) V6Active() bool {
return false
}
// iFaceMapper defines subset methods of interface required for manager
type iFaceMapper interface {
Name() string

View File

@@ -73,6 +73,9 @@ func TestIptablesManager_InsertRoutingRules(t *testing.T) {
for _, testCase := range test.InsertRuleTestCases {
t.Run(testCase.Name, func(t *testing.T) {
if testCase.IsV6 {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
iptablesClient, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
require.NoError(t, err, "failed to init iptables client")
@@ -154,6 +157,9 @@ func TestIptablesManager_RemoveRoutingRules(t *testing.T) {
for _, testCase := range test.RemoveRuleTestCases {
t.Run(testCase.Name, func(t *testing.T) {
if testCase.IsV6 {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
iptablesClient, _ := iptables.NewWithProtocol(iptables.ProtocolIPv4)
manager, err := newRouterManager(context.TODO(), iptablesClient)

View File

@@ -76,6 +76,13 @@ type Manager interface {
// RemoveRoutingRules removes a routing firewall rule
RemoveRoutingRules(pair RouterPair) error
// ResetV6Firewall makes changes to the firewall to adapt to the IP address changes.
// It is expected that after calling this method ApplyFiltering will be called to re-add the firewall rules.
ResetV6Firewall() error
// V6Active returns whether IPv6 rules should/may be created by upper layers.
V6Active() bool
// Reset firewall to the default state
Reset() error

File diff suppressed because it is too large Load Diff

View File

@@ -36,17 +36,25 @@ func Create(context context.Context, wgIface iFaceMapper) (*Manager, error) {
wgIface: wgIface,
}
workTable, err := m.createWorkTable()
workTable, err := m.createWorkTable(nftables.TableFamilyIPv4)
if err != nil {
return nil, err
}
m.router, err = newRouter(context, workTable)
var workTable6 *nftables.Table
if wgIface.Address6() != nil {
workTable6, err = m.createWorkTable(nftables.TableFamilyIPv6)
if err != nil {
return nil, err
}
}
m.router, err = newRouter(context, workTable, workTable6)
if err != nil {
return nil, err
}
m.aclManager, err = newAclManager(workTable, wgIface, m.router.RouteingFwChainName())
m.aclManager, err = newAclManager(workTable, workTable6, wgIface, m.router.RouteingFwChainName())
if err != nil {
return nil, err
}
@@ -54,6 +62,54 @@ func Create(context context.Context, wgIface iFaceMapper) (*Manager, error) {
return m, nil
}
// Resets the IPv6 Firewall Table to adapt to changes in IP addresses
func (m *Manager) ResetV6Firewall() error {
// First, prepare reset by deleting all currently active rules.
workTable6, err := m.aclManager.PrepareV6Reset()
if err != nil {
return err
}
// Depending on whether we now have an IPv6 address, we now either have to create/empty an IPv6 table, or delete it.
if m.wgIface.Address6() != nil {
if workTable6 != nil {
m.rConn.FlushTable(workTable6)
} else {
workTable6, err = m.createWorkTable(nftables.TableFamilyIPv6)
if err != nil {
return err
}
}
} else {
m.rConn.DelTable(workTable6)
workTable6 = nil
}
err = m.rConn.Flush()
if err != nil {
return err
}
// Restore routing rules.
err = m.router.RestoreAfterV6Reset(workTable6)
if err != nil {
return err
}
// Restore basic firewall chains (needs to happen after routes because chains from router must exist).
// Does not restore rules (will be done later during the update, when UpdateFiltering will be called at some point)
err = m.aclManager.ReinitAfterV6Reset(workTable6)
if err != nil {
return err
}
return m.rConn.Flush()
}
func (m *Manager) V6Active() bool {
return m.aclManager.v6Active
}
// AddFiltering rule to the firewall
//
// If comment argument is empty firewall manager should set
@@ -72,7 +128,7 @@ func (m *Manager) AddFiltering(
defer m.mutex.Unlock()
rawIP := ip.To4()
if rawIP == nil {
if rawIP == nil && m.wgIface.Address6() == nil {
return nil, fmt.Errorf("unsupported IP version: %s", ip.String())
}
@@ -114,6 +170,8 @@ func (m *Manager) AllowNetbird() error {
m.mutex.Lock()
defer m.mutex.Unlock()
// Note for devs: When adding IPv6 support to uspfilter, the implementation of createDefaultAllowRules()
// must be adjusted to include IPv6 rules.
err := m.aclManager.createDefaultAllowRules()
if err != nil {
return fmt.Errorf("failed to create default allow rules: %v", err)
@@ -211,8 +269,8 @@ func (m *Manager) Flush() error {
return m.aclManager.Flush()
}
func (m *Manager) createWorkTable() (*nftables.Table, error) {
tables, err := m.rConn.ListTablesOfFamily(nftables.TableFamilyIPv4)
func (m *Manager) createWorkTable(tableFamily nftables.TableFamily) (*nftables.Table, error) {
tables, err := m.rConn.ListTablesOfFamily(tableFamily)
if err != nil {
return nil, fmt.Errorf("list of tables: %w", err)
}
@@ -223,7 +281,7 @@ func (m *Manager) createWorkTable() (*nftables.Table, error) {
}
}
table := m.rConn.AddTable(&nftables.Table{Name: tableName, Family: nftables.TableFamilyIPv4})
table := m.rConn.AddTable(&nftables.Table{Name: tableName, Family: tableFamily})
err = m.rConn.Flush()
return table, err
}

View File

@@ -19,8 +19,9 @@ import (
// iFaceMapper defines subset methods of interface required for manager
type iFaceMock struct {
NameFunc func() string
AddressFunc func() iface.WGAddress
NameFunc func() string
AddressFunc func() iface.WGAddress
Address6Func func() *iface.WGAddress
}
func (i *iFaceMock) Name() string {
@@ -37,6 +38,13 @@ func (i *iFaceMock) Address() iface.WGAddress {
panic("AddressFunc is not set")
}
func (i *iFaceMock) Address6() *iface.WGAddress {
if i.Address6Func != nil {
return i.Address6Func()
}
panic("AddressFunc is not set")
}
func (i *iFaceMock) IsUserspaceBind() bool { return false }
func TestNftablesManager(t *testing.T) {
@@ -53,6 +61,7 @@ func TestNftablesManager(t *testing.T) {
},
}
},
Address6Func: func() *iface.WGAddress { return nil },
}
// just check on the local interface
@@ -99,11 +108,9 @@ func TestNftablesManager(t *testing.T) {
Register: 1,
Data: ifname("lo"),
},
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: uint32(9),
Len: uint32(1),
&expr.Meta{
Key: expr.MetaKeyL4PROTO,
Register: 1,
},
&expr.Cmp{
Register: 1,
@@ -152,6 +159,370 @@ func TestNftablesManager(t *testing.T) {
require.NoError(t, err, "failed to reset")
}
func TestNftablesManager6Disabled(t *testing.T) {
mock := &iFaceMock{
NameFunc: func() string {
return "lo"
},
AddressFunc: func() iface.WGAddress {
return iface.WGAddress{
IP: net.ParseIP("100.96.0.1"),
Network: &net.IPNet{
IP: net.ParseIP("100.96.0.0"),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
}
},
Address6Func: func() *iface.WGAddress { return nil },
}
// just check on the local interface
manager, err := Create(context.Background(), mock)
require.NoError(t, err)
time.Sleep(time.Second * 3)
defer func() {
err = manager.Reset()
require.NoError(t, err, "failed to reset")
time.Sleep(time.Second)
}()
ip := net.ParseIP("2001:db8::fedc:ba09:8765:4321")
testClient := &nftables.Conn{}
_, err = manager.AddFiltering(
ip,
fw.ProtocolTCP,
nil,
&fw.Port{Values: []int{53}},
fw.RuleDirectionIN,
fw.ActionDrop,
"",
"",
)
require.Error(t, err, "IPv6 rule should not be added when IPv6 is disabled")
err = manager.Flush()
require.NoError(t, err, "failed to flush")
rules, err := testClient.GetRules(manager.aclManager.workTable, manager.aclManager.chainInputRules)
require.NoError(t, err, "failed to get rules")
require.Len(t, rules, 0, "expected no rules")
err = manager.Reset()
require.NoError(t, err, "failed to reset")
}
func TestNftablesManager6(t *testing.T) {
if !iface.SupportsIPv6() {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
mock := &iFaceMock{
NameFunc: func() string {
return "lo"
},
AddressFunc: func() iface.WGAddress {
return iface.WGAddress{
IP: net.ParseIP("100.96.0.1"),
Network: &net.IPNet{
IP: net.ParseIP("100.96.0.0"),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
}
},
Address6Func: func() *iface.WGAddress {
return &iface.WGAddress{
IP: net.ParseIP("2001:db8::0123:4567:890a:bcde"),
Network: &net.IPNet{
IP: net.ParseIP("2001:db8::"),
Mask: net.CIDRMask(64, 128),
},
}
},
}
// just check on the local interface
manager, err := Create(context.Background(), mock)
require.NoError(t, err)
time.Sleep(time.Second * 3)
defer func() {
err = manager.Reset()
require.NoError(t, err, "failed to reset")
time.Sleep(time.Second)
}()
require.True(t, manager.V6Active(), "IPv6 is not active even though it should be.")
ip := net.ParseIP("2001:db8::fedc:ba09:8765:4321")
testClient := &nftables.Conn{}
rule, err := manager.AddFiltering(
ip,
fw.ProtocolTCP,
nil,
&fw.Port{Values: []int{53}},
fw.RuleDirectionIN,
fw.ActionDrop,
"",
"",
)
require.NoError(t, err, "failed to add rule")
err = manager.Flush()
require.NoError(t, err, "failed to flush")
rules, err := testClient.GetRules(manager.aclManager.workTable6, manager.aclManager.chainInputRules6)
require.NoError(t, err, "failed to get rules")
require.Len(t, rules, 1, "expected 1 rules")
ipToAdd, _ := netip.AddrFromSlice(ip)
add := ipToAdd.Unmap()
expectedExprs := []expr.Any{
&expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: ifname("lo"),
},
&expr.Meta{
Key: expr.MetaKeyL4PROTO,
Register: 1,
},
&expr.Cmp{
Register: 1,
Op: expr.CmpOpEq,
Data: []byte{unix.IPPROTO_TCP},
},
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: 8,
Len: 16,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: add.AsSlice(),
},
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseTransportHeader,
Offset: 2,
Len: 2,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte{0, 53},
},
&expr.Verdict{Kind: expr.VerdictDrop},
}
require.ElementsMatch(t, rules[0].Exprs, expectedExprs, "expected the same expressions")
for _, r := range rule {
err = manager.DeleteRule(r)
require.NoError(t, err, "failed to delete rule")
}
err = manager.Flush()
require.NoError(t, err, "failed to flush")
rules, err = testClient.GetRules(manager.aclManager.workTable6, manager.aclManager.chainInputRules6)
require.NoError(t, err, "failed to get rules")
require.Len(t, rules, 0, "expected 0 rules after deletion")
err = manager.Reset()
require.NoError(t, err, "failed to reset")
}
func TestNftablesManagerAddressReset6(t *testing.T) {
if !iface.SupportsIPv6() {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
mock := &iFaceMock{
NameFunc: func() string {
return "lo"
},
AddressFunc: func() iface.WGAddress {
return iface.WGAddress{
IP: net.ParseIP("100.96.0.1"),
Network: &net.IPNet{
IP: net.ParseIP("100.96.0.0"),
Mask: net.IPv4Mask(255, 255, 255, 0),
},
}
},
Address6Func: func() *iface.WGAddress {
return &iface.WGAddress{
IP: net.ParseIP("2001:db8::0123:4567:890a:bcde"),
Network: &net.IPNet{
IP: net.ParseIP("2001:db8::"),
Mask: net.CIDRMask(64, 128),
},
}
},
}
// just check on the local interface
manager, err := Create(context.Background(), mock)
require.NoError(t, err)
time.Sleep(time.Second * 3)
defer func() {
err = manager.Reset()
require.NoError(t, err, "failed to reset")
time.Sleep(time.Second)
}()
require.True(t, manager.V6Active(), "IPv6 is not active even though it should be.")
ip := net.ParseIP("2001:db8::fedc:ba09:8765:4321")
testClient := &nftables.Conn{}
_, err = manager.AddFiltering(
ip,
fw.ProtocolTCP,
nil,
&fw.Port{Values: []int{53}},
fw.RuleDirectionIN,
fw.ActionDrop,
"",
"",
)
require.NoError(t, err, "failed to add rule")
err = manager.Flush()
require.NoError(t, err, "failed to flush")
rules, err := testClient.GetRules(manager.aclManager.workTable6, manager.aclManager.chainInputRules6)
require.NoError(t, err, "failed to get rules")
require.Len(t, rules, 1, "expected 1 rules")
mock.Address6Func = func() *iface.WGAddress {
return nil
}
err = manager.ResetV6Firewall()
require.NoError(t, err, "failed to reset IPv6 firewall")
err = manager.Flush()
require.NoError(t, err, "failed to flush")
require.False(t, manager.V6Active(), "IPv6 is active even though it shouldn't be.")
tables, err := testClient.ListTablesOfFamily(nftables.TableFamilyIPv6)
require.NoError(t, err, "failed to list IPv6 tables")
for _, table := range tables {
if table.Name == tableName {
t.Errorf("When IPv6 is disabled, the netbird table should not exist.")
}
}
mock.Address6Func = func() *iface.WGAddress {
return &iface.WGAddress{
IP: net.ParseIP("2001:db8::0123:4567:890a:bcdf"),
Network: &net.IPNet{
IP: net.ParseIP("2001:db8::"),
Mask: net.CIDRMask(64, 128),
},
}
}
err = manager.ResetV6Firewall()
require.NoError(t, err, "failed to reset IPv6 firewall")
require.True(t, manager.V6Active(), "IPv6 is not active even though it should be.")
rule, err := manager.AddFiltering(
ip,
fw.ProtocolTCP,
nil,
&fw.Port{Values: []int{53}},
fw.RuleDirectionIN,
fw.ActionDrop,
"",
"",
)
require.NoError(t, err, "failed to add rule")
err = manager.Flush()
require.NoError(t, err, "failed to flush")
rules, err = testClient.GetRules(manager.aclManager.workTable6, manager.aclManager.chainInputRules6)
require.NoError(t, err, "failed to get rules")
require.Len(t, rules, 1, "expected 1 rule")
ipToAdd, _ := netip.AddrFromSlice(ip)
add := ipToAdd.Unmap()
expectedExprs := []expr.Any{
&expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: ifname("lo"),
},
&expr.Meta{
Key: expr.MetaKeyL4PROTO,
Register: 1,
},
&expr.Cmp{
Register: 1,
Op: expr.CmpOpEq,
Data: []byte{unix.IPPROTO_TCP},
},
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: 8,
Len: 16,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: add.AsSlice(),
},
&expr.Payload{
DestRegister: 1,
Base: expr.PayloadBaseTransportHeader,
Offset: 2,
Len: 2,
},
&expr.Cmp{
Op: expr.CmpOpEq,
Register: 1,
Data: []byte{0, 53},
},
&expr.Verdict{Kind: expr.VerdictDrop},
}
require.ElementsMatch(t, rules[0].Exprs, expectedExprs, "expected the same expressions")
for _, r := range rule {
err = manager.DeleteRule(r)
require.NoError(t, err, "failed to delete rule")
}
err = manager.Flush()
require.NoError(t, err, "failed to flush")
rules, err = testClient.GetRules(manager.aclManager.workTable6, manager.aclManager.chainInputRules6)
require.NoError(t, err, "failed to get rules")
require.Len(t, rules, 0, "expected 0 rules after deletion")
err = manager.Reset()
require.NoError(t, err, "failed to reset")
}
func TestNFtablesCreatePerformance(t *testing.T) {
mock := &iFaceMock{
NameFunc: func() string {
@@ -166,6 +537,16 @@ func TestNFtablesCreatePerformance(t *testing.T) {
},
}
},
Address6Func: func() *iface.WGAddress {
v6addr, v6net, _ := net.ParseCIDR("fd00:1234:dead:beef::1/64")
return &iface.WGAddress{
IP: v6addr,
Network: &net.IPNet{
IP: v6net.IP,
Mask: v6net.Mask,
},
}
},
}
for _, testMax := range []int{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000} {

View File

@@ -26,7 +26,8 @@ const (
// some presets for building nftable rules
var (
zeroXor = binaryutil.NativeEndian.PutUint32(0)
zeroXor = binaryutil.NativeEndian.PutUint32(0)
zeroXor6 = []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
exprCounterAccept = []expr.Any{
&expr.Counter{},
@@ -39,48 +40,69 @@ var (
)
type router struct {
ctx context.Context
stop context.CancelFunc
conn *nftables.Conn
workTable *nftables.Table
filterTable *nftables.Table
chains map[string]*nftables.Chain
ctx context.Context
stop context.CancelFunc
conn *nftables.Conn
workTable *nftables.Table
workTable6 *nftables.Table
filterTable *nftables.Table
filterTable6 *nftables.Table
chains map[string]*nftables.Chain
chains6 map[string]*nftables.Chain
// rules is useful to avoid duplicates and to get missing attributes that we don't have when adding new rules
rules map[string]*nftables.Rule
isDefaultFwdRulesEnabled bool
rules map[string]*nftables.Rule
rules6 map[string]*nftables.Rule
isDefaultFwdRulesEnabled bool
isDefaultFwdRulesEnabled6 bool
}
func newRouter(parentCtx context.Context, workTable *nftables.Table) (*router, error) {
func newRouter(parentCtx context.Context, workTable *nftables.Table, workTable6 *nftables.Table) (*router, error) {
ctx, cancel := context.WithCancel(parentCtx)
r := &router{
ctx: ctx,
stop: cancel,
conn: &nftables.Conn{},
workTable: workTable,
chains: make(map[string]*nftables.Chain),
rules: make(map[string]*nftables.Rule),
ctx: ctx,
stop: cancel,
conn: &nftables.Conn{},
workTable: workTable,
workTable6: workTable6,
chains: make(map[string]*nftables.Chain),
chains6: make(map[string]*nftables.Chain),
rules: make(map[string]*nftables.Rule),
rules6: make(map[string]*nftables.Rule),
}
var err error
r.filterTable, err = r.loadFilterTable()
r.filterTable, r.filterTable6, err = r.loadFilterTables()
if err != nil {
if errors.Is(err, errFilterTableNotFound) {
log.Warnf("table 'filter' not found for forward rules")
log.Warnf("table 'filter' not found for forward rules for one of the supported address families-")
} else {
return nil, err
}
}
err = r.cleanUpDefaultForwardRules()
err = r.cleanUpDefaultForwardRules(false)
if err != nil {
log.Errorf("failed to clean up rules from FORWARD chain: %s", err)
}
err = r.createContainers()
err = r.cleanUpDefaultForwardRules(true)
if err != nil {
log.Errorf("failed to clean up rules from IPv6 FORWARD chain: %s", err)
}
err = r.createContainers(false)
if err != nil {
log.Errorf("failed to create containers for route: %s", err)
}
if r.workTable6 != nil {
err = r.createContainers(true)
if err != nil {
log.Errorf("failed to create v6 containers for route: %s", err)
}
}
return r, err
}
@@ -90,43 +112,99 @@ func (r *router) RouteingFwChainName() string {
// ResetForwardRules cleans existing nftables default forward rules from the system
func (r *router) ResetForwardRules() {
err := r.cleanUpDefaultForwardRules()
err := r.cleanUpDefaultForwardRules(false)
if err != nil {
log.Errorf("failed to reset forward rules: %s", err)
}
err = r.cleanUpDefaultForwardRules(true)
if err != nil {
log.Errorf("failed to reset forward rules: %s", err)
}
}
func (r *router) loadFilterTable() (*nftables.Table, error) {
func (r *router) RestoreAfterV6Reset(newWorktable6 *nftables.Table) error {
r.workTable6 = newWorktable6
if newWorktable6 != nil {
err := r.cleanUpDefaultForwardRules(true)
if err != nil {
log.Errorf("failed to clean up rules from IPv6 FORWARD chain: %s", err)
}
err = r.createContainers(true)
if err != nil {
return err
}
for name, rule := range r.rules6 {
rule = &nftables.Rule{
Table: r.workTable6,
Chain: r.chains6[rule.Chain.Name],
Exprs: rule.Exprs,
UserData: rule.UserData,
}
r.rules6[name] = r.conn.AddRule(rule)
}
}
return r.conn.Flush()
}
func (r *router) loadFilterTables() (*nftables.Table, *nftables.Table, error) {
tables, err := r.conn.ListTablesOfFamily(nftables.TableFamilyIPv4)
if err != nil {
return nil, fmt.Errorf("nftables: unable to list tables: %v", err)
return nil, nil, fmt.Errorf("nftables: unable to list tables: %v", err)
}
var table4 *nftables.Table = nil
for _, table := range tables {
if table.Name == "filter" {
return table, nil
table4 = table
break
}
}
return nil, errFilterTableNotFound
var table6 *nftables.Table = nil
tables, err = r.conn.ListTablesOfFamily(nftables.TableFamilyIPv6)
if err != nil {
return nil, nil, fmt.Errorf("nftables: unable to list tables: %v", err)
}
for _, table := range tables {
if table.Name == "filter" {
table6 = table
break
}
}
err = nil
if table4 == nil || table6 == nil {
err = errFilterTableNotFound
}
return table4, table6, err
}
func (r *router) createContainers() error {
func (r *router) createContainers(forV6 bool) error {
workTable := r.workTable
chainStorage := r.chains
if forV6 {
workTable = r.workTable6
chainStorage = r.chains6
}
r.chains[chainNameRouteingFw] = r.conn.AddChain(&nftables.Chain{
chainStorage[chainNameRouteingFw] = r.conn.AddChain(&nftables.Chain{
Name: chainNameRouteingFw,
Table: r.workTable,
Table: workTable,
})
r.chains[chainNameRoutingNat] = r.conn.AddChain(&nftables.Chain{
chainStorage[chainNameRoutingNat] = r.conn.AddChain(&nftables.Chain{
Name: chainNameRoutingNat,
Table: r.workTable,
Table: workTable,
Hooknum: nftables.ChainHookPostrouting,
Priority: nftables.ChainPriorityNATSource - 1,
Type: nftables.ChainTypeNAT,
})
err := r.refreshRulesMap()
err := r.refreshRulesMap(forV6)
if err != nil {
log.Errorf("failed to clean up rules from FORWARD chain: %s", err)
}
@@ -140,7 +218,13 @@ func (r *router) createContainers() error {
// InsertRoutingRules inserts a nftable rule pair to the forwarding chain and if enabled, to the nat chain
func (r *router) InsertRoutingRules(pair manager.RouterPair) error {
err := r.refreshRulesMap()
parsedIp, _, _ := net.ParseCIDR(pair.Source)
if parsedIp.To4() == nil && r.workTable6 == nil {
return fmt.Errorf("nftables: attempted to add IPv6 routing rule even though IPv6 is not enabled for this host")
}
err := r.refreshRulesMap(parsedIp.To4() == nil)
if err != nil {
return err
}
@@ -165,7 +249,11 @@ func (r *router) InsertRoutingRules(pair manager.RouterPair) error {
}
}
if r.filterTable != nil && !r.isDefaultFwdRulesEnabled {
filterTable := r.filterTable
if parsedIp.To4() == nil {
filterTable = r.filterTable6
}
if filterTable != nil && !r.isDefaultFwdRulesEnabled {
log.Debugf("add default accept forward rule")
r.acceptForwardRule(pair.Source)
}
@@ -191,7 +279,13 @@ func (r *router) insertRoutingRule(format, chainName string, pair manager.Router
ruleKey := manager.GenKey(format, pair.ID)
_, exists := r.rules[ruleKey]
parsedIp, _, _ := net.ParseCIDR(pair.Source)
rules := r.rules
if parsedIp.To4() == nil {
rules = r.rules6
}
_, exists := rules[ruleKey]
if exists {
err := r.removeRoutingRule(format, pair)
if err != nil {
@@ -199,18 +293,35 @@ func (r *router) insertRoutingRule(format, chainName string, pair manager.Router
}
}
r.rules[ruleKey] = r.conn.InsertRule(&nftables.Rule{
Table: r.workTable,
Chain: r.chains[chainName],
table, chain := r.workTable, r.chains[chainName]
if parsedIp.To4() == nil {
table, chain = r.workTable6, r.chains6[chainName]
}
newRule := r.conn.InsertRule(&nftables.Rule{
Table: table,
Chain: chain,
Exprs: expression,
UserData: []byte(ruleKey),
})
if parsedIp.To4() == nil {
r.rules[ruleKey] = newRule
} else {
r.rules6[ruleKey] = newRule
}
return nil
}
func (r *router) acceptForwardRule(sourceNetwork string) {
src := generateCIDRMatcherExpressions(true, sourceNetwork)
dst := generateCIDRMatcherExpressions(false, "0.0.0.0/0")
table := r.filterTable
parsedIp, _, _ := net.ParseCIDR(sourceNetwork)
if parsedIp.To4() == nil {
dst = generateCIDRMatcherExpressions(false, "::/0")
table = r.filterTable6
}
var exprs []expr.Any
exprs = append(src, append(dst, &expr.Verdict{ // nolint:gocritic
@@ -218,10 +329,10 @@ func (r *router) acceptForwardRule(sourceNetwork string) {
})...)
rule := &nftables.Rule{
Table: r.filterTable,
Table: table,
Chain: &nftables.Chain{
Name: "FORWARD",
Table: r.filterTable,
Table: table,
Type: nftables.ChainTypeFilter,
Hooknum: nftables.ChainHookForward,
Priority: nftables.ChainPriorityFilter,
@@ -233,6 +344,9 @@ func (r *router) acceptForwardRule(sourceNetwork string) {
r.conn.AddRule(rule)
src = generateCIDRMatcherExpressions(true, "0.0.0.0/0")
if parsedIp.To4() == nil {
src = generateCIDRMatcherExpressions(true, "::/0")
}
dst = generateCIDRMatcherExpressions(false, sourceNetwork)
exprs = append(src, append(dst, &expr.Verdict{ //nolint:gocritic
@@ -240,10 +354,10 @@ func (r *router) acceptForwardRule(sourceNetwork string) {
})...)
rule = &nftables.Rule{
Table: r.filterTable,
Table: table,
Chain: &nftables.Chain{
Name: "FORWARD",
Table: r.filterTable,
Table: table,
Type: nftables.ChainTypeFilter,
Hooknum: nftables.ChainHookForward,
Priority: nftables.ChainPriorityFilter,
@@ -252,12 +366,21 @@ func (r *router) acceptForwardRule(sourceNetwork string) {
UserData: []byte(userDataAcceptForwardRuleDst),
}
r.conn.AddRule(rule)
r.isDefaultFwdRulesEnabled = true
if parsedIp.To4() == nil {
r.isDefaultFwdRulesEnabled6 = true
} else {
r.isDefaultFwdRulesEnabled = true
}
}
// RemoveRoutingRules removes a nftable rule pair from forwarding and nat chains
func (r *router) RemoveRoutingRules(pair manager.RouterPair) error {
err := r.refreshRulesMap()
parsedIp, _, _ := net.ParseCIDR(pair.Source)
if parsedIp.To4() == nil && r.workTable6 == nil {
return fmt.Errorf("nftables: attempted to remove IPv6 routing rule even though IPv6 is not enabled for this host")
}
err := r.refreshRulesMap(parsedIp.To4() == nil)
if err != nil {
return err
}
@@ -282,8 +405,12 @@ func (r *router) RemoveRoutingRules(pair manager.RouterPair) error {
return err
}
if len(r.rules) == 0 {
err := r.cleanUpDefaultForwardRules()
rulesList := r.rules
if parsedIp.To4() == nil {
rulesList = r.rules6
}
if len(rulesList) == 0 {
err := r.cleanUpDefaultForwardRules(parsedIp.To4() == nil)
if err != nil {
log.Errorf("failed to clean up rules from FORWARD chain: %s", err)
}
@@ -301,7 +428,13 @@ func (r *router) RemoveRoutingRules(pair manager.RouterPair) error {
func (r *router) removeRoutingRule(format string, pair manager.RouterPair) error {
ruleKey := manager.GenKey(format, pair.ID)
rule, found := r.rules[ruleKey]
parsedIp, _, _ := net.ParseCIDR(pair.Source)
rules := r.rules
if parsedIp.To4() == nil {
rules = r.rules6
}
rule, found := rules[ruleKey]
if found {
ruleType := "forwarding"
if rule.Chain.Type == nftables.ChainTypeNAT {
@@ -315,49 +448,68 @@ func (r *router) removeRoutingRule(format string, pair manager.RouterPair) error
log.Debugf("nftables: removing %s rule for %s", ruleType, pair.Destination)
delete(r.rules, ruleKey)
delete(rules, ruleKey)
}
return nil
}
// refreshRulesMap refreshes the rule map with the latest rules. this is useful to avoid
// duplicates and to get missing attributes that we don't have when adding new rules
func (r *router) refreshRulesMap() error {
for _, chain := range r.chains {
func (r *router) refreshRulesMap(forV6 bool) error {
chainList := r.chains
if forV6 {
chainList = r.chains6
}
for _, chain := range chainList {
rules, err := r.conn.GetRules(chain.Table, chain)
if err != nil {
return fmt.Errorf("nftables: unable to list rules: %v", err)
}
for _, rule := range rules {
if len(rule.UserData) > 0 {
r.rules[string(rule.UserData)] = rule
if forV6 {
r.rules6[string(rule.UserData)] = rule
} else {
r.rules[string(rule.UserData)] = rule
}
}
}
}
return nil
}
func (r *router) cleanUpDefaultForwardRules() error {
if r.filterTable == nil {
r.isDefaultFwdRulesEnabled = false
func (r *router) cleanUpDefaultForwardRules(forV6 bool) error {
tableFamily := nftables.TableFamilyIPv4
filterTable := r.filterTable
if forV6 {
tableFamily = nftables.TableFamilyIPv6
filterTable = r.filterTable6
}
if filterTable == nil {
if forV6 {
r.isDefaultFwdRulesEnabled6 = false
} else {
r.isDefaultFwdRulesEnabled = false
}
return nil
}
chains, err := r.conn.ListChainsOfTableFamily(nftables.TableFamilyIPv4)
chains, err := r.conn.ListChainsOfTableFamily(tableFamily)
if err != nil {
return err
}
var rules []*nftables.Rule
for _, chain := range chains {
if chain.Table.Name != r.filterTable.Name {
if chain.Table.Name != filterTable.Name {
continue
}
if chain.Name != "FORWARD" {
continue
}
rules, err = r.conn.GetRules(r.filterTable, chain)
rules, err = r.conn.GetRules(filterTable, chain)
if err != nil {
return err
}
@@ -371,7 +523,12 @@ func (r *router) cleanUpDefaultForwardRules() error {
}
}
}
r.isDefaultFwdRulesEnabled = false
if forV6 {
r.isDefaultFwdRulesEnabled6 = false
} else {
r.isDefaultFwdRulesEnabled = false
}
return r.conn.Flush()
}
@@ -387,6 +544,18 @@ func generateCIDRMatcherExpressions(source bool, cidr string) []expr.Any {
} else {
offSet = 16 // dst offset
}
addrLen := uint32(4)
zeroXor := zeroXor
if ip.To4() == nil {
if source {
offSet = 8 // src offset
} else {
offSet = 24 // dst offset
}
addrLen = 16
zeroXor = zeroXor6
}
return []expr.Any{
// fetch src add
@@ -394,13 +563,13 @@ func generateCIDRMatcherExpressions(source bool, cidr string) []expr.Any {
DestRegister: 1,
Base: expr.PayloadBaseNetworkHeader,
Offset: offSet,
Len: 4,
Len: addrLen,
},
// net mask
&expr.Bitwise{
DestRegister: 1,
SourceRegister: 1,
Len: 4,
Len: addrLen,
Mask: network.Mask,
Xor: zeroXor,
},

View File

@@ -4,6 +4,7 @@ package nftables
import (
"context"
"github.com/netbirdio/netbird/iface"
"testing"
"github.com/coreos/go-iptables/iptables"
@@ -29,16 +30,19 @@ func TestNftablesManager_InsertRoutingRules(t *testing.T) {
t.Skip("nftables not supported on this OS")
}
table, err := createWorkTable()
table, table6, err := createWorkTables()
if err != nil {
t.Fatal(err)
}
defer deleteWorkTable()
defer deleteWorkTables()
for _, testCase := range test.InsertRuleTestCases {
t.Run(testCase.Name, func(t *testing.T) {
manager, err := newRouter(context.TODO(), table)
if testCase.IsV6 && table6 == nil {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
manager, err := newRouter(context.TODO(), table, table6)
require.NoError(t, err, "failed to create router")
nftablesTestingClient := &nftables.Conn{}
@@ -58,8 +62,13 @@ func TestNftablesManager_InsertRoutingRules(t *testing.T) {
testingExpression := append(sourceExp, destExp...) //nolint:gocritic
fwdRuleKey := firewall.GenKey(firewall.ForwardingFormat, testCase.InputPair.ID)
chains := manager.chains
if testCase.IsV6 {
chains = manager.chains6
}
found := 0
for _, chain := range manager.chains {
for _, chain := range chains {
rules, err := nftablesTestingClient.GetRules(chain.Table, chain)
require.NoError(t, err, "should list rules for %s table and %s chain", chain.Table.Name, chain.Name)
for _, rule := range rules {
@@ -75,7 +84,7 @@ func TestNftablesManager_InsertRoutingRules(t *testing.T) {
if testCase.InputPair.Masquerade {
natRuleKey := firewall.GenKey(firewall.NatFormat, testCase.InputPair.ID)
found := 0
for _, chain := range manager.chains {
for _, chain := range chains {
rules, err := nftablesTestingClient.GetRules(chain.Table, chain)
require.NoError(t, err, "should list rules for %s table and %s chain", chain.Table.Name, chain.Name)
for _, rule := range rules {
@@ -94,7 +103,7 @@ func TestNftablesManager_InsertRoutingRules(t *testing.T) {
inFwdRuleKey := firewall.GenKey(firewall.InForwardingFormat, testCase.InputPair.ID)
found = 0
for _, chain := range manager.chains {
for _, chain := range chains {
rules, err := nftablesTestingClient.GetRules(chain.Table, chain)
require.NoError(t, err, "should list rules for %s table and %s chain", chain.Table.Name, chain.Name)
for _, rule := range rules {
@@ -110,7 +119,7 @@ func TestNftablesManager_InsertRoutingRules(t *testing.T) {
if testCase.InputPair.Masquerade {
inNatRuleKey := firewall.GenKey(firewall.InNatFormat, testCase.InputPair.ID)
found := 0
for _, chain := range manager.chains {
for _, chain := range chains {
rules, err := nftablesTestingClient.GetRules(chain.Table, chain)
require.NoError(t, err, "should list rules for %s table and %s chain", chain.Table.Name, chain.Name)
for _, rule := range rules {
@@ -131,16 +140,19 @@ func TestNftablesManager_RemoveRoutingRules(t *testing.T) {
t.Skip("nftables not supported on this OS")
}
table, err := createWorkTable()
table, table6, err := createWorkTables()
if err != nil {
t.Fatal(err)
}
defer deleteWorkTable()
defer deleteWorkTables()
for _, testCase := range test.RemoveRuleTestCases {
t.Run(testCase.Name, func(t *testing.T) {
manager, err := newRouter(context.TODO(), table)
if testCase.IsV6 && table6 == nil {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
manager, err := newRouter(context.TODO(), table, table6)
require.NoError(t, err, "failed to create router")
nftablesTestingClient := &nftables.Conn{}
@@ -150,11 +162,18 @@ func TestNftablesManager_RemoveRoutingRules(t *testing.T) {
sourceExp := generateCIDRMatcherExpressions(true, testCase.InputPair.Source)
destExp := generateCIDRMatcherExpressions(false, testCase.InputPair.Destination)
chains := manager.chains
workTable := table
if testCase.IsV6 {
chains = manager.chains6
workTable = table6
}
forwardExp := append(sourceExp, append(destExp, exprCounterAccept...)...) //nolint:gocritic
forwardRuleKey := firewall.GenKey(firewall.ForwardingFormat, testCase.InputPair.ID)
insertedForwarding := nftablesTestingClient.InsertRule(&nftables.Rule{
Table: manager.workTable,
Chain: manager.chains[chainNameRouteingFw],
Table: workTable,
Chain: chains[chainNameRouteingFw],
Exprs: forwardExp,
UserData: []byte(forwardRuleKey),
})
@@ -163,8 +182,8 @@ func TestNftablesManager_RemoveRoutingRules(t *testing.T) {
natRuleKey := firewall.GenKey(firewall.NatFormat, testCase.InputPair.ID)
insertedNat := nftablesTestingClient.InsertRule(&nftables.Rule{
Table: manager.workTable,
Chain: manager.chains[chainNameRoutingNat],
Table: workTable,
Chain: chains[chainNameRoutingNat],
Exprs: natExp,
UserData: []byte(natRuleKey),
})
@@ -175,8 +194,8 @@ func TestNftablesManager_RemoveRoutingRules(t *testing.T) {
forwardExp = append(sourceExp, append(destExp, exprCounterAccept...)...) //nolint:gocritic
inForwardRuleKey := firewall.GenKey(firewall.InForwardingFormat, testCase.InputPair.ID)
insertedInForwarding := nftablesTestingClient.InsertRule(&nftables.Rule{
Table: manager.workTable,
Chain: manager.chains[chainNameRouteingFw],
Table: workTable,
Chain: chains[chainNameRouteingFw],
Exprs: forwardExp,
UserData: []byte(inForwardRuleKey),
})
@@ -185,8 +204,8 @@ func TestNftablesManager_RemoveRoutingRules(t *testing.T) {
inNatRuleKey := firewall.GenKey(firewall.InNatFormat, testCase.InputPair.ID)
insertedInNat := nftablesTestingClient.InsertRule(&nftables.Rule{
Table: manager.workTable,
Chain: manager.chains[chainNameRoutingNat],
Table: workTable,
Chain: chains[chainNameRoutingNat],
Exprs: natExp,
UserData: []byte(inNatRuleKey),
})
@@ -199,7 +218,7 @@ func TestNftablesManager_RemoveRoutingRules(t *testing.T) {
err = manager.RemoveRoutingRules(testCase.InputPair)
require.NoError(t, err, "shouldn't return error")
for _, chain := range manager.chains {
for _, chain := range chains {
rules, err := nftablesTestingClient.GetRules(chain.Table, chain)
require.NoError(t, err, "should list rules for %s table and %s chain", chain.Table.Name, chain.Name)
for _, rule := range rules {
@@ -238,30 +257,39 @@ func isIptablesClientAvailable(client *iptables.IPTables) bool {
return err == nil
}
func createWorkTable() (*nftables.Table, error) {
func createWorkTables() (*nftables.Table, *nftables.Table, error) {
sConn, err := nftables.New(nftables.AsLasting())
if err != nil {
return nil, err
return nil, nil, err
}
tables, err := sConn.ListTablesOfFamily(nftables.TableFamilyIPv4)
if err != nil {
return nil, err
return nil, nil, err
}
for _, t := range tables {
tables6, err := sConn.ListTablesOfFamily(nftables.TableFamilyIPv6)
if err != nil {
return nil, nil, err
}
for _, t := range append(tables, tables6...) {
if t.Name == tableName {
sConn.DelTable(t)
}
}
table := sConn.AddTable(&nftables.Table{Name: tableName, Family: nftables.TableFamilyIPv4})
var table6 *nftables.Table
if iface.SupportsIPv6() {
table6 = sConn.AddTable(&nftables.Table{Name: tableName, Family: nftables.TableFamilyIPv6})
}
err = sConn.Flush()
return table, err
return table, table6, err
}
func deleteWorkTable() {
func deleteWorkTables() {
sConn, err := nftables.New(nftables.AsLasting())
if err != nil {
return
@@ -272,6 +300,12 @@ func deleteWorkTable() {
return
}
tables6, err := sConn.ListTablesOfFamily(nftables.TableFamilyIPv6)
if err != nil {
return
}
tables = append(tables, tables6...)
for _, t := range tables {
if t.Name == tableName {
sConn.DelTable(t)

View File

@@ -8,6 +8,7 @@ var (
InsertRuleTestCases = []struct {
Name string
InputPair firewall.RouterPair
IsV6 bool
}{
{
Name: "Insert Forwarding IPV4 Rule",
@@ -27,12 +28,32 @@ var (
Masquerade: true,
},
},
{
Name: "Insert Forwarding IPV6 Rule",
InputPair: firewall.RouterPair{
ID: "zxa",
Source: "2001:db8:0123:4567::1/128",
Destination: "2001:db8:0123:abcd::/64",
Masquerade: false,
},
IsV6: true,
},
{
Name: "Insert Forwarding And Nat IPV6 Rules",
InputPair: firewall.RouterPair{
ID: "zxa",
Source: "2001:db8:0123:4567::1/128",
Destination: "2001:db8:0123:abcd::/64",
Masquerade: true,
},
IsV6: true,
},
}
RemoveRuleTestCases = []struct {
Name string
InputPair firewall.RouterPair
IpVersion string
IsV6 bool
}{
{
Name: "Remove Forwarding And Nat IPV4 Rules",
@@ -43,5 +64,15 @@ var (
Masquerade: true,
},
},
{
Name: "Remove Forwarding And Nat IPV6 Rules",
InputPair: firewall.RouterPair{
ID: "zxa",
Source: "2001:db8:0123:4567::1/128",
Destination: "2001:db8:0123:abcd::/64",
Masquerade: true,
},
IsV6: true,
},
}
)

View File

@@ -24,6 +24,7 @@ var (
type IFaceMapper interface {
SetFilter(iface.PacketFilter) error
Address() iface.WGAddress
Address6() *iface.WGAddress
}
// RuleSet is a set of rules grouped by a string key
@@ -69,6 +70,14 @@ func CreateWithNativeFirewall(iface IFaceMapper, nativeFirewall firewall.Manager
return mgr, nil
}
func (m *Manager) ResetV6Firewall() error {
return nil
}
func (m *Manager) V6Active() bool {
return false
}
func create(iface IFaceMapper) (*Manager, error) {
m := &Manager{
decoders: sync.Pool{

View File

@@ -33,6 +33,10 @@ func (i *IFaceMock) Address() iface.WGAddress {
return i.AddressFunc()
}
func (i *IFaceMock) Address6() *iface.WGAddress {
return nil
}
func TestManagerCreate(t *testing.T) {
ifaceMock := &IFaceMock{
SetFilterFunc: func(iface.PacketFilter) error { return nil },

View File

@@ -16,9 +16,10 @@ import (
mgmProto "github.com/netbirdio/netbird/management/proto"
)
// Manager is a ACL rules manager
// Manager is an ACL rules manager
type Manager interface {
ApplyFiltering(networkMap *mgmProto.NetworkMap)
ResetV6Acl() error
}
// DefaultManager uses firewall manager to handle
@@ -26,16 +27,36 @@ type DefaultManager struct {
firewall firewall.Manager
ipsetCounter int
rulesPairs map[string][]firewall.Rule
rulesPairs6 map[string][]firewall.Rule
mutex sync.Mutex
}
func NewDefaultManager(fm firewall.Manager) *DefaultManager {
return &DefaultManager{
firewall: fm,
rulesPairs: make(map[string][]firewall.Rule),
firewall: fm,
rulesPairs: make(map[string][]firewall.Rule),
rulesPairs6: make(map[string][]firewall.Rule),
}
}
func (d *DefaultManager) ResetV6Acl() error {
for _, rules := range d.rulesPairs6 {
for _, r := range rules {
err := d.firewall.DeleteRule(r)
if err != nil {
return err
}
}
}
err := d.firewall.ResetV6Firewall()
if err != nil {
return err
}
d.rulesPairs6 = make(map[string][]firewall.Rule)
return nil
}
// ApplyFiltering firewall rules to the local firewall manager processed by ACL policy.
//
// If allowByDefault is true it appends allow ALL traffic rules to input and output chains.
@@ -83,6 +104,7 @@ func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
if enableSSH {
rules = append(rules, &mgmProto.FirewallRule{
PeerIP: "0.0.0.0",
PeerIP6: "::",
Direction: mgmProto.FirewallRule_IN,
Action: mgmProto.FirewallRule_ACCEPT,
Protocol: mgmProto.FirewallRule_TCP,
@@ -97,12 +119,14 @@ func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
rules = append(rules,
&mgmProto.FirewallRule{
PeerIP: "0.0.0.0",
PeerIP6: "::",
Direction: mgmProto.FirewallRule_IN,
Action: mgmProto.FirewallRule_ACCEPT,
Protocol: mgmProto.FirewallRule_ALL,
},
&mgmProto.FirewallRule{
PeerIP: "0.0.0.0",
PeerIP6: "::",
Direction: mgmProto.FirewallRule_OUT,
Action: mgmProto.FirewallRule_ACCEPT,
Protocol: mgmProto.FirewallRule_ALL,
@@ -111,6 +135,7 @@ func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
}
newRulePairs := make(map[string][]firewall.Rule)
newRulePairs6 := make(map[string][]firewall.Rule)
ipsetByRuleSelectors := make(map[string]string)
for _, r := range rules {
@@ -123,7 +148,7 @@ func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
ipsetName = fmt.Sprintf("nb%07d", d.ipsetCounter)
ipsetByRuleSelectors[selector] = ipsetName
}
pairID, rulePair, err := d.protoRuleToFirewallRule(r, ipsetName)
pairID, rulePair, rulePair6, err := d.protoRuleToFirewallRule(r, ipsetName)
if err != nil {
log.Errorf("failed to apply firewall rule: %+v, %v", r, err)
d.rollBack(newRulePairs)
@@ -132,6 +157,8 @@ func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
if len(rules) > 0 {
d.rulesPairs[pairID] = rulePair
newRulePairs[pairID] = rulePair
d.rulesPairs6[pairID] = rulePair6
newRulePairs6[pairID] = rulePair6
}
}
@@ -146,59 +173,104 @@ func (d *DefaultManager) ApplyFiltering(networkMap *mgmProto.NetworkMap) {
delete(d.rulesPairs, pairID)
}
}
for pairID, rules := range d.rulesPairs6 {
if _, ok := newRulePairs6[pairID]; !ok {
for _, rule := range rules {
if err := d.firewall.DeleteRule(rule); err != nil {
log.Errorf("failed to delete firewall rule: %v", err)
continue
}
}
delete(d.rulesPairs6, pairID)
}
}
d.rulesPairs = newRulePairs
d.rulesPairs6 = newRulePairs6
}
func (d *DefaultManager) protoRuleToFirewallRule(
r *mgmProto.FirewallRule,
ipsetName string,
) (string, []firewall.Rule, error) {
) (string, []firewall.Rule, []firewall.Rule, error) {
ip := net.ParseIP(r.PeerIP)
if ip == nil {
return "", nil, fmt.Errorf("invalid IP address, skipping firewall rule")
return "", nil, nil, fmt.Errorf("invalid IP address, skipping firewall rule")
}
var ip6 *net.IP = nil
if d.firewall.V6Active() && r.PeerIP6 != "" {
ip6tmp := net.ParseIP(r.PeerIP6)
if ip6tmp == nil {
return "", nil, nil, fmt.Errorf("invalid IP address, skipping firewall rule")
}
ip6 = &ip6tmp
}
protocol, err := convertToFirewallProtocol(r.Protocol)
if err != nil {
return "", nil, fmt.Errorf("skipping firewall rule: %s", err)
return "", nil, nil, fmt.Errorf("skipping firewall rule: %s", err)
}
action, err := convertFirewallAction(r.Action)
if err != nil {
return "", nil, fmt.Errorf("skipping firewall rule: %s", err)
return "", nil, nil, fmt.Errorf("skipping firewall rule: %s", err)
}
var port *firewall.Port
if r.Port != "" {
value, err := strconv.Atoi(r.Port)
if err != nil {
return "", nil, fmt.Errorf("invalid port, skipping firewall rule")
return "", nil, nil, fmt.Errorf("invalid port, skipping firewall rule")
}
port = &firewall.Port{
Values: []int{value},
}
}
ruleID := d.getRuleID(ip, protocol, int(r.Direction), port, action, "")
var rules []firewall.Rule
var rules6 []firewall.Rule
ruleID := d.getRuleID(ip, ip6, protocol, int(r.Direction), port, action, "")
if rulesPair, ok := d.rulesPairs[ruleID]; ok {
return ruleID, rulesPair, nil
rules = rulesPair
}
if rulesPair6, ok := d.rulesPairs6[ruleID]; d.firewall.V6Active() && ok && ip6 != nil {
rules6 = rulesPair6
}
var rules []firewall.Rule
switch r.Direction {
case mgmProto.FirewallRule_IN:
rules, err = d.addInRules(ip, protocol, port, action, ipsetName, "")
case mgmProto.FirewallRule_OUT:
rules, err = d.addOutRules(ip, protocol, port, action, ipsetName, "")
default:
return "", nil, fmt.Errorf("invalid direction, skipping firewall rule")
if rules == nil {
switch r.Direction {
case mgmProto.FirewallRule_IN:
rules, err = d.addInRules(ip, protocol, port, action, ipsetName, "")
case mgmProto.FirewallRule_OUT:
rules, err = d.addOutRules(ip, protocol, port, action, ipsetName, "")
default:
return "", nil, nil, fmt.Errorf("invalid direction, skipping firewall rule")
}
}
if err != nil {
return "", nil, err
return "", nil, nil, err
}
return ruleID, rules, nil
if d.firewall.V6Active() && ip6 != nil && rules6 == nil {
switch r.Direction {
case mgmProto.FirewallRule_IN:
rules6, err = d.addInRules(*ip6, protocol, port, action, ipsetName, "")
case mgmProto.FirewallRule_OUT:
rules6, err = d.addOutRules(*ip6, protocol, port, action, ipsetName, "")
default:
return "", nil, nil, fmt.Errorf("invalid direction, skipping firewall rule")
}
}
if err != nil && err.Error() != "failed to add firewall rule: attempted to configure filtering for IPv6 address even though IPv6 is not active" {
return "", rules, nil, err
}
return ruleID, rules, rules6, nil
}
func (d *DefaultManager) addInRules(
@@ -226,8 +298,9 @@ func (d *DefaultManager) addInRules(
if err != nil {
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
}
rules = append(rules, rule...)
return append(rules, rule...), nil
return rules, nil
}
func (d *DefaultManager) addOutRules(
@@ -255,20 +328,26 @@ func (d *DefaultManager) addOutRules(
if err != nil {
return nil, fmt.Errorf("failed to add firewall rule: %v", err)
}
rules = append(rules, rule...)
return append(rules, rule...), nil
return rules, nil
}
// getRuleID() returns unique ID for the rule based on its parameters.
func (d *DefaultManager) getRuleID(
ip net.IP,
ip6 *net.IP,
proto firewall.Protocol,
direction int,
port *firewall.Port,
action firewall.Action,
comment string,
) string {
idStr := ip.String() + string(proto) + strconv.Itoa(direction) + strconv.Itoa(int(action)) + comment
ip6Str := ""
if ip6 != nil {
ip6Str = ip6.String()
}
idStr := ip.String() + ip6Str + string(proto) + strconv.Itoa(direction) + strconv.Itoa(int(action)) + comment
if port != nil {
idStr += port.String()
}
@@ -321,6 +400,8 @@ func (d *DefaultManager) squashAcceptRules(
// it means that rules for that protocol was already optimized on the
// management side
if r.PeerIP == "0.0.0.0" {
// I don't _think_ that IPv6 is relevant here, as any optimization that has r.PeerIP6 == "::" should also
// implicitly have r.PeerIP == "0.0.0.0".
squashedRules = append(squashedRules, r)
squashedProtocols[r.Protocol] = struct{}{}
return
@@ -364,6 +445,7 @@ func (d *DefaultManager) squashAcceptRules(
// add special rule 0.0.0.0 which allows all IP's in our firewall implementations
squashedRules = append(squashedRules, &mgmProto.FirewallRule{
PeerIP: "0.0.0.0",
PeerIP6: "::",
Direction: direction,
Action: mgmProto.FirewallRule_ACCEPT,
Protocol: protocol,

View File

@@ -19,6 +19,7 @@ func TestDefaultManager(t *testing.T) {
FirewallRules: []*mgmProto.FirewallRule{
{
PeerIP: "10.93.0.1",
PeerIP6: "2001:db8::fedc:ba09:8765:0001",
Direction: mgmProto.FirewallRule_OUT,
Action: mgmProto.FirewallRule_ACCEPT,
Protocol: mgmProto.FirewallRule_TCP,
@@ -26,6 +27,7 @@ func TestDefaultManager(t *testing.T) {
},
{
PeerIP: "10.93.0.2",
PeerIP6: "2001:db8::fedc:ba09:8765:0002",
Direction: mgmProto.FirewallRule_OUT,
Action: mgmProto.FirewallRule_DROP,
Protocol: mgmProto.FirewallRule_UDP,
@@ -50,6 +52,14 @@ func TestDefaultManager(t *testing.T) {
IP: ip,
Network: network,
}).AnyTimes()
ip6, network6, err := net.ParseCIDR("2001:db8::fedc:ba09:8765:4321/64")
if err != nil {
t.Fatalf("failed to parse IP address: %v", err)
}
ifaceMock.EXPECT().Address6().Return(&iface.WGAddress{
IP: ip6,
Network: network6,
}).AnyTimes()
// we receive one rule from the management so for testing purposes ignore it
fw, err := firewall.NewFirewall(context.Background(), ifaceMock)
@@ -83,6 +93,7 @@ func TestDefaultManager(t *testing.T) {
networkMap.FirewallRules,
&mgmProto.FirewallRule{
PeerIP: "10.93.0.3",
PeerIP6: "2001:db8::fedc:ba09:8765:0003",
Direction: mgmProto.FirewallRule_IN,
Action: mgmProto.FirewallRule_DROP,
Protocol: mgmProto.FirewallRule_ICMP,
@@ -343,6 +354,7 @@ func TestDefaultManagerEnableSSHRules(t *testing.T) {
IP: ip,
Network: network,
}).AnyTimes()
ifaceMock.EXPECT().Address6().Return(nil).AnyTimes()
// we receive one rule from the management so for testing purposes ignore it
fw, err := firewall.NewFirewall(context.Background(), ifaceMock)

View File

@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/netbirdio/netbird/client/internal/acl (interfaces: IFaceMapper)
// Source: ./client/firewall/iface.go
// Package mocks is a generated GoMock package.
package mocks
@@ -48,6 +48,20 @@ func (mr *MockIFaceMapperMockRecorder) Address() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Address", reflect.TypeOf((*MockIFaceMapper)(nil).Address))
}
// Address6 mocks base method.
func (m *MockIFaceMapper) Address6() *iface.WGAddress {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Address6")
ret0, _ := ret[0].(*iface.WGAddress)
return ret0
}
// Address6 indicates an expected call of Address6.
func (mr *MockIFaceMapperMockRecorder) Address6() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Address6", reflect.TypeOf((*MockIFaceMapper)(nil).Address6))
}
// IsUserspaceBind mocks base method.
func (m *MockIFaceMapper) IsUserspaceBind() bool {
m.ctrl.T.Helper()

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net"
"runtime"
"runtime/debug"
"strings"
@@ -91,6 +92,9 @@ func (c *ConnectClient) RunOniOS(
networkChangeListener listener.NetworkChangeListener,
dnsManager dns.IosDnsManager,
) error {
// Set GC percent to 5% to reduce memory usage as iOS only allows 50MB of memory for the extension.
debug.SetGCPercent(5)
mobileDependency := MobileDependency{
FileDescriptor: fileDescriptor,
NetworkChangeListener: networkChangeListener,
@@ -306,6 +310,7 @@ func createEngineConfig(key wgtypes.Key, config *Config, peerConfig *mgmProto.Pe
engineConf := &EngineConfig{
WgIfaceName: config.WgIface,
WgAddr: peerConfig.Address,
WgAddr6: peerConfig.Address6,
IFaceBlackList: config.IFaceBlackList,
DisableIPv6Discovery: config.DisableIPv6Discovery,
WgPrivateKey: key,
@@ -327,6 +332,15 @@ func createEngineConfig(key wgtypes.Key, config *Config, peerConfig *mgmProto.Pe
engineConf.PreSharedKey = &preSharedKey
}
port, err := freePort(config.WgPort)
if err != nil {
return nil, err
}
if port != config.WgPort {
log.Infof("using %d as wireguard port: %d is in use", port, config.WgPort)
}
engineConf.WgPort = port
return engineConf, nil
}
@@ -376,3 +390,20 @@ func statusRecorderToSignalConnStateNotifier(statusRecorder *peer.Status) signal
notifier, _ := sri.(signal.ConnStateNotifier)
return notifier
}
func freePort(start int) (int, error) {
addr := net.UDPAddr{}
if start == 0 {
start = iface.DefaultWgPort
}
for x := start; x <= 65535; x++ {
addr.Port = x
conn, err := net.ListenUDP("udp", &addr)
if err != nil {
continue
}
conn.Close()
return x, nil
}
return 0, errors.New("no free ports")
}

View File

@@ -0,0 +1,57 @@
package internal
import (
"net"
"testing"
)
func Test_freePort(t *testing.T) {
tests := []struct {
name string
port int
want int
wantErr bool
}{
{
name: "available",
port: 51820,
want: 51820,
wantErr: false,
},
{
name: "notavailable",
port: 51830,
want: 51831,
wantErr: false,
},
{
name: "noports",
port: 65535,
want: 0,
wantErr: true,
},
}
for _, tt := range tests {
c1, err := net.ListenUDP("udp", &net.UDPAddr{Port: 51830})
if err != nil {
t.Errorf("freePort error = %v", err)
}
c2, err := net.ListenUDP("udp", &net.UDPAddr{Port: 65535})
if err != nil {
t.Errorf("freePort error = %v", err)
}
t.Run(tt.name, func(t *testing.T) {
got, err := freePort(tt.port)
if (err != nil) != tt.wantErr {
t.Errorf("freePort() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("freePort() = %v, want %v", got, tt.want)
}
})
c1.Close()
c2.Close()
}
}

View File

@@ -485,7 +485,11 @@ func (s *DefaultServer) updateLocalResolver(update map[string]nbdns.SimpleRecord
}
func getNSHostPort(ns nbdns.NameServer) string {
return fmt.Sprintf("%s:%d", ns.IP.String(), ns.Port)
if ns.IP.Is4() {
return fmt.Sprintf("%s:%d", ns.IP.String(), ns.Port)
} else {
return fmt.Sprintf("[%s]:%d", ns.IP.String(), ns.Port)
}
}
// upstreamCallbacks returns two functions, the first one is used to deactivate

View File

@@ -38,6 +38,13 @@ func (w *mocWGIface) Address() iface.WGAddress {
Network: network,
}
}
func (w *mocWGIface) Address6() *iface.WGAddress {
ip, network, _ := net.ParseCIDR("fd00:1234:dead:beef::/64")
return &iface.WGAddress{
IP: ip,
Network: network,
}
}
func (w *mocWGIface) GetFilter() iface.PacketFilter {
return w.filter
@@ -261,7 +268,7 @@ func TestUpdateDNSServer(t *testing.T) {
if err != nil {
t.Fatal(err)
}
wgIface, err := iface.NewWGIFace(fmt.Sprintf("utun230%d", n), fmt.Sprintf("100.66.100.%d/32", n+1), 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
wgIface, err := iface.NewWGIFace(fmt.Sprintf("utun230%d", n), fmt.Sprintf("100.66.100.%d/32", n+1), fmt.Sprintf("fd00:1234:dead:beef::%d/128", n+1), 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
@@ -339,7 +346,7 @@ func TestDNSFakeResolverHandleUpdates(t *testing.T) {
}
privKey, _ := wgtypes.GeneratePrivateKey()
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.1/32", 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.1/32", "", 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
if err != nil {
t.Errorf("build interface wireguard: %v", err)
return
@@ -595,7 +602,7 @@ func TestDNSServerUpstreamDeactivateCallback(t *testing.T) {
}
func TestDNSPermanent_updateHostDNS_emptyUpstream(t *testing.T) {
wgIFace, err := createWgInterfaceWithBind(t)
wgIFace, err := createWgInterfaceWithBind(t, false)
if err != nil {
t.Fatal("failed to initialize wg interface")
}
@@ -621,7 +628,7 @@ func TestDNSPermanent_updateHostDNS_emptyUpstream(t *testing.T) {
}
func TestDNSPermanent_updateUpstream(t *testing.T) {
wgIFace, err := createWgInterfaceWithBind(t)
wgIFace, err := createWgInterfaceWithBind(t, false)
if err != nil {
t.Fatal("failed to initialize wg interface")
}
@@ -713,7 +720,7 @@ func TestDNSPermanent_updateUpstream(t *testing.T) {
}
func TestDNSPermanent_matchOnly(t *testing.T) {
wgIFace, err := createWgInterfaceWithBind(t)
wgIFace, err := createWgInterfaceWithBind(t, false)
if err != nil {
t.Fatal("failed to initialize wg interface")
}
@@ -784,7 +791,7 @@ func TestDNSPermanent_matchOnly(t *testing.T) {
}
}
func createWgInterfaceWithBind(t *testing.T) (*iface.WGIface, error) {
func createWgInterfaceWithBind(t *testing.T, enableV6 bool) (*iface.WGIface, error) {
t.Helper()
ov := os.Getenv("NB_WG_KERNEL_DISABLED")
defer t.Setenv("NB_WG_KERNEL_DISABLED", ov)
@@ -797,7 +804,11 @@ func createWgInterfaceWithBind(t *testing.T) (*iface.WGIface, error) {
}
privKey, _ := wgtypes.GeneratePrivateKey()
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.2/24", 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
v6Addr := ""
if enableV6 {
v6Addr = "fd00:1234:dead:beef::1/128"
}
wgIface, err := iface.NewWGIFace("utun2301", "100.66.100.2/24", v6Addr, 33100, privKey.String(), iface.DefaultMTU, newNet, nil)
if err != nil {
t.Fatalf("build interface wireguard: %v", err)
return nil, err

View File

@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"net"
"runtime"
"sync"
"sync/atomic"
"time"
@@ -260,13 +259,10 @@ func (u *upstreamResolverBase) disable(err error) {
return
}
// todo test the deactivation logic, it seems to affect the client
if runtime.GOOS != "ios" {
log.Warnf("Upstream resolving is Disabled for %v", reactivatePeriod)
u.deactivate(err)
u.disabled = true
go u.waitUntilResponse()
}
log.Warnf("Upstream resolving is Disabled for %v", reactivatePeriod)
u.deactivate(err)
u.disabled = true
go u.waitUntilResponse()
}
func (u *upstreamResolverBase) testNameserver(server string) error {

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"maps"
"math/rand"
"net"
"net/netip"
@@ -57,7 +58,8 @@ type EngineConfig struct {
WgIfaceName string
// WgAddr is a Wireguard local address (Netbird Network IP)
WgAddr string
WgAddr string
WgAddr6 string
// WgPrivateKey is a Wireguard private key of our peer (it MUST never leave the machine)
WgPrivateKey wgtypes.Key
@@ -117,7 +119,8 @@ type Engine struct {
TURNs []*stun.URI
// clientRoutes is the most recent list of clientRoutes received from the Management Service
clientRoutes route.HAMap
clientRoutes route.HAMap
clientRoutesMu sync.RWMutex
clientCtx context.Context
clientCancel context.CancelFunc
@@ -133,7 +136,7 @@ type Engine struct {
// networkSerial is the latest CurrentSerial (state ID) of the network sent by the Management service
networkSerial uint64
networkWatcher *networkmonitor.NetworkWatcher
networkMonitor *networkmonitor.NetworkMonitor
sshServerFunc func(hostKeyPEM []byte, addr string) (nbssh.Server, error)
sshServer nbssh.Server
@@ -150,6 +153,8 @@ type Engine struct {
signalProbe *Probe
relayProbe *Probe
wgProbe *Probe
wgConnWorker sync.WaitGroup
}
// Peer is an instance of the Connection Peer
@@ -212,7 +217,6 @@ func NewEngineWithProbes(
networkSerial: 0,
sshServerFunc: nbssh.DefaultSSHServer,
statusRecorder: statusRecorder,
networkWatcher: networkmonitor.New(),
mgmProbe: mgmProbe,
signalProbe: signalProbe,
relayProbe: relayProbe,
@@ -229,20 +233,26 @@ func (e *Engine) Stop() error {
}
// stopping network monitor first to avoid starting the engine again
e.networkWatcher.Stop()
if e.networkMonitor != nil {
e.networkMonitor.Stop()
}
log.Info("Network monitor: stopped")
err := e.removeAllPeers()
if err != nil {
return err
}
e.clientRoutesMu.Lock()
e.clientRoutes = nil
e.clientRoutesMu.Unlock()
// very ugly but we want to remove peers from the WireGuard interface first before removing interface.
// Removing peers happens in the conn.Close() asynchronously
time.Sleep(500 * time.Millisecond)
e.close()
e.wgConnWorker.Wait()
log.Infof("stopped Netbird Engine")
return nil
}
@@ -259,7 +269,7 @@ func (e *Engine) Start() error {
}
e.ctx, e.cancel = context.WithCancel(e.clientCtx)
e.wgProxyFactory = wgproxy.NewFactory(e.clientCtx, e.config.WgPort)
e.wgProxyFactory = wgproxy.NewFactory(e.ctx, e.config.WgPort)
wgIface, err := e.newWgIface()
if err != nil {
@@ -344,20 +354,8 @@ func (e *Engine) Start() error {
e.receiveManagementEvents()
e.receiveProbeEvents()
if e.config.NetworkMonitor {
// starting network monitor at the very last to avoid disruptions
go e.networkWatcher.Start(e.ctx, func() {
log.Infof("Network monitor detected network change, restarting engine")
if err := e.Stop(); err != nil {
log.Errorf("Failed to stop engine: %v", err)
}
if err := e.Start(); err != nil {
log.Errorf("Failed to start engine: %v", err)
}
})
} else {
log.Infof("Network monitor is disabled, not starting")
}
// starting network monitor at the very last to avoid disruptions
e.startNetworkMonitor()
return nil
}
@@ -606,6 +604,32 @@ func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error {
log.Infof("updated peer address from %s to %s", oldAddr, conf.Address)
}
if e.wgInterface.Address6() == nil && conf.Address6 != "" ||
e.wgInterface.Address6() != nil && e.wgInterface.Address6().String() != conf.Address6 {
oldAddr := "none"
if e.wgInterface.Address6() != nil {
oldAddr = e.wgInterface.Address6().String()
}
newAddr := "none"
if conf.Address6 != "" {
newAddr = conf.Address6
}
log.Debugf("updating peer IPv6 address from %s to %s", oldAddr, newAddr)
err := e.wgInterface.UpdateAddr6(conf.Address6)
if err != nil {
return err
}
e.config.WgAddr6 = conf.Address6
err = e.acl.ResetV6Acl()
if err != nil {
return err
}
e.routeManager.ResetV6Routes()
log.Infof("updated peer IPv6 address from %s to %s", oldAddr, conf.Address6)
}
if conf.GetSshConfig() != nil {
err := e.updateSSH(conf.GetSshConfig())
if err != nil {
@@ -615,6 +639,7 @@ func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error {
e.statusRecorder.UpdateLocalPeerState(peer.LocalPeerState{
IP: e.config.WgAddr,
IP6: e.config.WgAddr6,
PubKey: e.config.WgPrivateKey.PublicKey().String(),
KernelInterface: iface.WireGuardModuleIsLoaded(),
FQDN: conf.GetFqdn(),
@@ -745,7 +770,9 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
log.Errorf("failed to update clientRoutes, err: %v", err)
}
e.clientRoutesMu.Lock()
e.clientRoutes = clientRoutes
e.clientRoutesMu.Unlock()
protoDNSConfig := networkMap.GetDNSConfig()
if protoDNSConfig == nil {
@@ -879,18 +906,25 @@ func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig) error {
log.Warnf("error adding peer %s to status recorder, got error: %v", peerKey, err)
}
e.wgConnWorker.Add(1)
go e.connWorker(conn, peerKey)
}
return nil
}
func (e *Engine) connWorker(conn *peer.Conn, peerKey string) {
defer e.wgConnWorker.Done()
for {
// randomize starting time a bit
min := 500
max := 2000
time.Sleep(time.Duration(rand.Intn(max-min)+min) * time.Millisecond)
duration := time.Duration(rand.Intn(max-min)+min) * time.Millisecond
select {
case <-e.ctx.Done():
return
case <-time.After(duration):
}
// if peer has been removed -> give up
if !e.peerExists(peerKey) {
@@ -1088,7 +1122,8 @@ func (e *Engine) receiveSignalEvents() {
log.Errorf("failed on parsing remote candidate %s -> %s", candidate, err)
return err
}
conn.OnRemoteCandidate(candidate)
conn.OnRemoteCandidate(candidate, e.GetClientRoutes())
case sProto.Body_MODE:
}
@@ -1231,7 +1266,7 @@ func (e *Engine) newWgIface() (*iface.WGIface, error) {
default:
}
return iface.NewWGIFace(e.config.WgIfaceName, e.config.WgAddr, e.config.WgPort, e.config.WgPrivateKey.String(), iface.DefaultMTU, transportNet, mArgs)
return iface.NewWGIFace(e.config.WgIfaceName, e.config.WgAddr, e.config.WgAddr6, e.config.WgPort, e.config.WgPrivateKey.String(), iface.DefaultMTU, transportNet, mArgs)
}
func (e *Engine) wgInterfaceCreate() (err error) {
@@ -1282,11 +1317,17 @@ func (e *Engine) newDnsServer() ([]*route.Route, dns.Server, error) {
// GetClientRoutes returns the current routes from the route map
func (e *Engine) GetClientRoutes() route.HAMap {
return e.clientRoutes
e.clientRoutesMu.RLock()
defer e.clientRoutesMu.RUnlock()
return maps.Clone(e.clientRoutes)
}
// GetClientRoutesWithNetID returns the current routes from the route map, but the keys consist of the network ID only
func (e *Engine) GetClientRoutesWithNetID() map[route.NetID][]*route.Route {
e.clientRoutesMu.RLock()
defer e.clientRoutesMu.RUnlock()
routes := make(map[route.NetID][]*route.Route, len(e.clientRoutes))
for id, v := range e.clientRoutes {
routes[id.NetID()] = v
@@ -1399,3 +1440,26 @@ func (e *Engine) probeSTUNs() []relay.ProbeResult {
func (e *Engine) probeTURNs() []relay.ProbeResult {
return relay.ProbeAll(e.ctx, relay.ProbeTURN, e.TURNs)
}
func (e *Engine) startNetworkMonitor() {
if !e.config.NetworkMonitor {
log.Infof("Network monitor is disabled, not starting")
return
}
e.networkMonitor = networkmonitor.New()
go func() {
err := e.networkMonitor.Start(e.ctx, func() {
log.Infof("Network monitor detected network change, restarting engine")
if err := e.Stop(); err != nil {
log.Errorf("Failed to stop engine: %v", err)
}
if err := e.Start(); err != nil {
log.Errorf("Failed to start engine: %v", err)
}
})
if err != nil && !errors.Is(err, networkmonitor.ErrStopped) {
log.Errorf("Network monitor: %v", err)
}
}()
}

View File

@@ -216,7 +216,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
if err != nil {
t.Fatal(err)
}
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", engine.config.WgPort, key.String(), iface.DefaultMTU, newNet, nil)
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", "", engine.config.WgPort, key.String(), iface.DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
@@ -229,6 +229,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
t.Fatal(err)
}
engine.udpMux = bind.NewUniversalUDPMuxDefault(bind.UniversalUDPMuxParams{UDPConn: conn})
engine.ctx = ctx
type testCase struct {
name string
@@ -408,6 +409,7 @@ func TestEngine_Sync(t *testing.T) {
WgPrivateKey: key,
WgPort: 33100,
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
engine.ctx = ctx
engine.dnsServer = &dns.MockServer{
UpdateDNSServerFunc: func(serial uint64, update nbdns.Config) error { return nil },
@@ -563,14 +565,16 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, &EngineConfig{
WgIfaceName: wgIfaceName,
WgAddr: wgAddr,
WgAddr6: "",
WgPrivateKey: key,
WgPort: 33100,
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
engine.ctx = ctx
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, engine.config.WgPort, key.String(), iface.DefaultMTU, newNet, nil)
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, engine.config.WgAddr6, engine.config.WgPort, key.String(), iface.DefaultMTU, newNet, nil)
assert.NoError(t, err, "shouldn't return error")
input := struct {
inputSerial uint64
@@ -732,14 +736,17 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) {
engine := NewEngine(ctx, cancel, &signal.MockClient{}, &mgmt.MockClient{}, &EngineConfig{
WgIfaceName: wgIfaceName,
WgAddr: wgAddr,
WgAddr6: "",
WgPrivateKey: key,
WgPort: 33100,
}, MobileDependency{}, peer.NewRecorder("https://mgm"))
engine.ctx = ctx
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, 33100, key.String(), iface.DefaultMTU, newNet, nil)
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, engine.config.WgAddr6, 33100, key.String(), iface.DefaultMTU, newNet, nil)
assert.NoError(t, err, "shouldn't return error")
mockRouteManager := &routemanager.MockManager{
@@ -1003,7 +1010,9 @@ func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey strin
WgPort: wgPort,
}
return NewEngine(ctx, cancel, signalClient, mgmtClient, conf, MobileDependency{}, peer.NewRecorder("https://mgm")), nil
e, err := NewEngine(ctx, cancel, signalClient, mgmtClient, conf, MobileDependency{}, peer.NewRecorder("https://mgm")), nil
e.ctx = ctx
return e, err
}
func startSignal() (*grpc.Server, string, error) {
@@ -1042,7 +1051,7 @@ func startManagement(dataDir string) (*grpc.Server, string, error) {
return nil, "", err
}
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
store, err := server.NewStoreFromJson(config.Datadir, nil)
store, _, err := server.NewTestStoreFromJson(config.Datadir)
if err != nil {
return nil, "", err
}

View File

@@ -2,14 +2,20 @@ package networkmonitor
import (
"context"
"errors"
"sync"
)
// NetworkWatcher watches for changes in network configuration.
type NetworkWatcher struct {
var ErrStopped = errors.New("monitor has been stopped")
// NetworkMonitor watches for changes in network configuration.
type NetworkMonitor struct {
cancel context.CancelFunc
wg sync.WaitGroup
mu sync.Mutex
}
// New creates a new network monitor.
func New() *NetworkWatcher {
return &NetworkWatcher{}
func New() *NetworkMonitor {
return &NetworkMonitor{}
}

View File

@@ -31,7 +31,7 @@ func checkChange(ctx context.Context, nexthopv4 netip.Addr, intfv4 *net.Interfac
for {
select {
case <-ctx.Done():
return ctx.Err()
return ErrStopped
default:
buf := make([]byte, 2048)
n, err := unix.Read(fd, buf)
@@ -63,7 +63,7 @@ func checkChange(ctx context.Context, nexthopv4 netip.Addr, intfv4 *net.Interfac
}
log.Infof("Network monitor: monitored interface (%s) is down.", ifinfo.Name)
callback()
go callback()
// handle route changes
case unix.RTM_ADD, syscall.RTM_DELETE:
@@ -84,11 +84,11 @@ func checkChange(ctx context.Context, nexthopv4 netip.Addr, intfv4 *net.Interfac
switch msg.Type {
case unix.RTM_ADD:
log.Infof("Network monitor: default route changed: via %s, interface %s", route.Gw, intf)
callback()
go callback()
case unix.RTM_DELETE:
if intfv4 != nil && route.Gw.Compare(nexthopv4) == 0 || intfv6 != nil && route.Gw.Compare(nexthopv6) == 0 {
log.Infof("Network monitor: default route removed: via %s, interface %s", route.Gw, intf)
callback()
go callback()
}
}
}

View File

@@ -5,6 +5,7 @@ package networkmonitor
import (
"context"
"errors"
"fmt"
"net"
"net/netip"
"runtime/debug"
@@ -15,20 +16,18 @@ import (
"github.com/netbirdio/netbird/client/internal/routemanager"
)
// Start begins watching for network changes and calls the callback function and stops when a change is detected.
func (nw *NetworkWatcher) Start(ctx context.Context, callback func()) {
if nw.cancel != nil {
log.Warn("Network monitor: already running, stopping previous watcher")
nw.Stop()
}
// Start begins monitoring network changes. When a change is detected, it calls the callback asynchronously and returns.
func (nw *NetworkMonitor) Start(ctx context.Context, callback func()) (err error) {
if ctx.Err() != nil {
log.Info("Network monitor: not starting, context is already cancelled")
return
return ctx.Err()
}
nw.mu.Lock()
ctx, nw.cancel = context.WithCancel(ctx)
defer nw.Stop()
nw.mu.Unlock()
nw.wg.Add(1)
defer nw.wg.Done()
var nexthop4, nexthop6 netip.Addr
var intf4, intf6 *net.Interface
@@ -56,27 +55,30 @@ func (nw *NetworkWatcher) Start(ctx context.Context, callback func()) {
expBackOff := backoff.WithContext(backoff.NewExponentialBackOff(), ctx)
if err := backoff.Retry(operation, expBackOff); err != nil {
log.Errorf("Network monitor: failed to get default next hops: %v", err)
return
return fmt.Errorf("failed to get default next hops: %w", err)
}
// recover in case sys ops panic
defer func() {
if r := recover(); r != nil {
log.Errorf("Network monitor: panic occurred: %v, stack trace: %s", r, string(debug.Stack()))
err = fmt.Errorf("panic occurred: %v, stack trace: %s", r, string(debug.Stack()))
}
}()
if err := checkChange(ctx, nexthop4, intf4, nexthop6, intf6, callback); err != nil && !errors.Is(err, context.Canceled) {
log.Errorf("Network monitor: failed to start: %v", err)
if err := checkChange(ctx, nexthop4, intf4, nexthop6, intf6, callback); err != nil {
return fmt.Errorf("check change: %w", err)
}
return nil
}
// Stop stops the network monitor.
func (nw *NetworkWatcher) Stop() {
func (nw *NetworkMonitor) Stop() {
nw.mu.Lock()
defer nw.mu.Unlock()
if nw.cancel != nil {
nw.cancel()
nw.cancel = nil
log.Info("Network monitor: stopped")
nw.wg.Wait()
}
}

View File

@@ -36,7 +36,7 @@ func checkChange(ctx context.Context, nexthopv4 netip.Addr, intfv4 *net.Interfac
for {
select {
case <-ctx.Done():
return ctx.Err()
return ErrStopped
// handle interface state changes
case update := <-linkChan:
@@ -47,12 +47,12 @@ func checkChange(ctx context.Context, nexthopv4 netip.Addr, intfv4 *net.Interfac
switch update.Header.Type {
case syscall.RTM_DELLINK:
log.Infof("Network monitor: monitored interface (%s) is gone", update.Link.Attrs().Name)
callback()
go callback()
return nil
case syscall.RTM_NEWLINK:
if (update.IfInfomsg.Flags&syscall.IFF_RUNNING) == 0 && update.Link.Attrs().OperState == netlink.OperDown {
log.Infof("Network monitor: monitored interface (%s) is down.", update.Link.Attrs().Name)
callback()
go callback()
return nil
}
}
@@ -67,12 +67,12 @@ func checkChange(ctx context.Context, nexthopv4 netip.Addr, intfv4 *net.Interfac
// triggered on added/replaced routes
case syscall.RTM_NEWROUTE:
log.Infof("Network monitor: default route changed: via %s, interface %d", route.Gw, route.LinkIndex)
callback()
go callback()
return nil
case syscall.RTM_DELROUTE:
if intfv4 != nil && route.Gw.Equal(nexthopv4.AsSlice()) || intfv6 != nil && route.Gw.Equal(nexthop6.AsSlice()) {
log.Infof("Network monitor: default route removed: via %s, interface %d", route.Gw, route.LinkIndex)
callback()
go callback()
return nil
}
}

View File

@@ -4,8 +4,9 @@ package networkmonitor
import "context"
func (nw *NetworkWatcher) Start(context.Context, func()) {
func (nw *NetworkMonitor) Start(context.Context, func()) error {
return nil
}
func (nw *NetworkWatcher) Stop() {
func (nw *NetworkMonitor) Stop() {
}

View File

@@ -48,10 +48,10 @@ func checkChange(ctx context.Context, nexthopv4 netip.Addr, intfv4 *net.Interfac
for {
select {
case <-ctx.Done():
return ctx.Err()
return ErrStopped
case <-ticker.C:
if changed(nexthopv4, intfv4, neighborv4, nexthopv6, intfv6, neighborv6) {
callback()
go callback()
return nil
}
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net"
"net/netip"
"runtime"
"strings"
"sync"
@@ -18,6 +19,7 @@ import (
"github.com/netbirdio/netbird/client/internal/wgproxy"
"github.com/netbirdio/netbird/iface"
"github.com/netbirdio/netbird/iface/bind"
"github.com/netbirdio/netbird/route"
signal "github.com/netbirdio/netbird/signal/client"
sProto "github.com/netbirdio/netbird/signal/proto"
nbnet "github.com/netbirdio/netbird/util/net"
@@ -353,7 +355,7 @@ func (conn *Conn) Open(ctx context.Context) error {
err = conn.agent.GatherCandidates()
if err != nil {
return err
return fmt.Errorf("gather candidates: %v", err)
}
// will block until connection succeeded
@@ -370,7 +372,7 @@ func (conn *Conn) Open(ctx context.Context) error {
return err
}
// dynamically set remote WireGuard port is other side specified a different one from the default one
// dynamically set remote WireGuard port if other side specified a different one from the default one
remoteWgPort := iface.DefaultWgPort
if remoteOfferAnswer.WgListenPort != 0 {
remoteWgPort = remoteOfferAnswer.WgListenPort
@@ -482,11 +484,15 @@ func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int, rem
log.Warnf("unable to save peer's state, got error: %v", err)
}
_, ipNet, err := net.ParseCIDR(conn.config.WgConfig.AllowedIps)
_, ipNet, err := net.ParseCIDR(strings.Split(conn.config.WgConfig.AllowedIps, ",")[0])
if err != nil {
return nil, err
}
if runtime.GOOS == "ios" {
runtime.GC()
}
if conn.onConnected != nil {
conn.onConnected(conn.config.Key, remoteRosenpassPubKey, ipNet.IP.String(), remoteRosenpassAddr)
}
@@ -775,7 +781,7 @@ func (conn *Conn) OnRemoteAnswer(answer OfferAnswer) bool {
}
// OnRemoteCandidate Handles ICE connection Candidate provided by the remote peer.
func (conn *Conn) OnRemoteCandidate(candidate ice.Candidate) {
func (conn *Conn) OnRemoteCandidate(candidate ice.Candidate, haRoutes route.HAMap) {
log.Debugf("OnRemoteCandidate from peer %s -> %s", conn.config.Key, candidate.String())
go func() {
conn.mu.Lock()
@@ -785,6 +791,10 @@ func (conn *Conn) OnRemoteCandidate(candidate ice.Candidate) {
return
}
if candidateViaRoutes(candidate, haRoutes) {
return
}
err := conn.agent.AddRemoteCandidate(candidate)
if err != nil {
log.Errorf("error while handling remote candidate from peer %s", conn.config.Key)
@@ -802,3 +812,31 @@ func (conn *Conn) RegisterProtoSupportMeta(support []uint32) {
protoSupport := signal.ParseFeaturesSupported(support)
conn.meta.protoSupport = protoSupport
}
func candidateViaRoutes(candidate ice.Candidate, clientRoutes route.HAMap) bool {
var routePrefixes []netip.Prefix
for _, routes := range clientRoutes {
if len(routes) > 0 && routes[0] != nil {
routePrefixes = append(routePrefixes, routes[0].Network)
}
}
addr, err := netip.ParseAddr(candidate.Address())
if err != nil {
log.Errorf("Failed to parse IP address %s: %v", candidate.Address(), err)
return false
}
for _, prefix := range routePrefixes {
// default route is
if prefix.Bits() == 0 {
continue
}
if prefix.Contains(addr) {
log.Debugf("Ignoring candidate [%s], its address is part of routed network %s", candidate.String(), prefix)
return true
}
}
return false
}

View File

@@ -68,6 +68,7 @@ func (s *State) GetRoutes() map[string]struct{} {
// LocalPeerState contains the latest state of the local peer
type LocalPeerState struct {
IP string
IP6 string
PubKey string
KernelInterface bool
FQDN string

View File

@@ -170,7 +170,7 @@ func ProbeAll(
var wg sync.WaitGroup
for i, uri := range relays {
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
wg.Add(1)

View File

@@ -38,6 +38,7 @@ type clientNetwork struct {
peerStateUpdate chan struct{}
routePeersNotifiers map[string]chan struct{}
chosenRoute *route.Route
chosenIP *net.IP
network netip.Prefix
updateSerial uint64
}
@@ -221,6 +222,7 @@ func (c *clientNetwork) removeRouteFromWireguardPeer(peerKey string) error {
func (c *clientNetwork) removeRouteFromPeerAndSystem() error {
if c.chosenRoute != nil {
// TODO IPv6 (pass wgInterface)
if err := removeVPNRoute(c.network, c.getAsInterface()); err != nil {
return fmt.Errorf("remove route %s from system, err: %v", c.network, err)
}
@@ -261,10 +263,20 @@ func (c *clientNetwork) recalculateRouteAndUpdatePeerAndSystem() error {
return fmt.Errorf("remove route from peer: %v", err)
}
} else {
// TODO recheck IPv6
gwAddr := c.wgInterface.Address().IP
c.chosenIP = &gwAddr
if c.network.Addr().Is6() {
if c.wgInterface.Address6() == nil {
return fmt.Errorf("Could not assign IPv6 route %s for peer %s because no IPv6 address is assigned",
c.network.String(), c.wgInterface.Address().IP.String())
}
c.chosenIP = &c.wgInterface.Address6().IP
}
// otherwise add the route to the system
if err := addVPNRoute(c.network, c.getAsInterface()); err != nil {
return fmt.Errorf("route %s couldn't be added for peer %s, err: %v",
c.network.String(), c.wgInterface.Address().IP.String(), err)
c.network.String(), c.chosenIP.String(), err)
}
}

View File

@@ -34,6 +34,7 @@ type Manager interface {
GetRouteSelector() *routeselector.RouteSelector
SetRouteChangeListener(listener listener.NetworkChangeListener)
InitialRouteRange() []string
ResetV6Routes()
EnableServerRouter(firewall firewall.Manager) error
Stop()
}
@@ -148,6 +149,18 @@ func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Ro
}
}
// ResetV6Routes deletes all IPv6 routes (necessary if IPv6 address changes).
// It is expected that UpdateRoute is called afterwards to recreate the routing table.
func (m *DefaultManager) ResetV6Routes() {
for id, client := range m.clientNetworks {
if client.network.Addr().Is6() {
log.Debugf("stopping client network watcher due to IPv6 address change, %s", id)
client.stop()
delete(m.clientNetworks, id)
}
}
}
// SetRouteChangeListener set RouteListener for route change notifier
func (m *DefaultManager) SetRouteChangeListener(listener listener.NetworkChangeListener) {
m.notifier.setListener(listener)

View File

@@ -36,6 +36,7 @@ func TestManagerUpdateRoutes(t *testing.T) {
serverRoutesExpected int
clientNetworkWatchersExpected int
clientNetworkWatchersExpectedAllowed int
isV6 bool
}{
{
name: "Should create 2 client networks",
@@ -65,6 +66,35 @@ func TestManagerUpdateRoutes(t *testing.T) {
inputSerial: 1,
clientNetworkWatchersExpected: 2,
},
{
name: "Should create 2 client networks (IPv6)",
inputInitRoutes: []*route.Route{},
inputRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeB",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8::7890:abcd/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputSerial: 1,
clientNetworkWatchersExpected: 2,
isV6: true,
},
{
name: "Should Create 2 Server Routes",
inputRoutes: []*route.Route{
@@ -93,6 +123,34 @@ func TestManagerUpdateRoutes(t *testing.T) {
serverRoutesExpected: 2,
clientNetworkWatchersExpected: 0,
},
{
name: "Should Create 2 Server Routes (IPv6)",
inputRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: localPeerKey,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeB",
Peer: localPeerKey,
Network: netip.MustParsePrefix("2001:db8::7890:abcd/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputSerial: 1,
serverRoutesExpected: 2,
clientNetworkWatchersExpected: 0,
},
{
name: "Should Create 1 Route For Client And Server",
inputRoutes: []*route.Route{
@@ -121,6 +179,84 @@ func TestManagerUpdateRoutes(t *testing.T) {
serverRoutesExpected: 1,
clientNetworkWatchersExpected: 1,
},
{
name: "Should Create 1 Route For Client And Server (IPv6)",
inputRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: localPeerKey,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeB",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8::7890:abcd/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputSerial: 1,
serverRoutesExpected: 1,
clientNetworkWatchersExpected: 1,
isV6: true,
},
{
name: "Should Create 1 Route For Client And Server for each IP version",
inputRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: localPeerKey,
Network: netip.MustParsePrefix("100.64.30.250/30"),
NetworkType: route.IPv4Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeB",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("8.8.9.9/32"),
NetworkType: route.IPv4Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "a",
NetID: "routeA",
Peer: localPeerKey,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeB",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8::7890:abcd/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputSerial: 1,
serverRoutesExpected: 2,
clientNetworkWatchersExpected: 2,
isV6: true,
},
{
name: "Should Create 1 Route For Client and Skip Server Route On Empty Server Router",
inputRoutes: []*route.Route{
@@ -150,6 +286,36 @@ func TestManagerUpdateRoutes(t *testing.T) {
serverRoutesExpected: 0,
clientNetworkWatchersExpected: 1,
},
{
name: "Should Create 1 Route For Client and Skip Server Route On Empty Server Router (IPv6)",
inputRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: localPeerKey,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeB",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8::7890:abcd/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputSerial: 1,
removeSrvRouter: true,
serverRoutesExpected: 0,
clientNetworkWatchersExpected: 1,
isV6: true,
},
{
name: "Should Create 1 HA Route and 1 Standalone",
inputRoutes: []*route.Route{
@@ -187,6 +353,44 @@ func TestManagerUpdateRoutes(t *testing.T) {
inputSerial: 1,
clientNetworkWatchersExpected: 2,
},
{
name: "Should Create 1 HA Route and 1 Standalone (IPv6)",
inputRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeA",
Peer: remotePeerKey2,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "c",
NetID: "routeB",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8::7890:abcd/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputSerial: 1,
clientNetworkWatchersExpected: 2,
isV6: true,
},
{
name: "No Small Client Route Should Be Added",
inputRoutes: []*route.Route{
@@ -205,6 +409,25 @@ func TestManagerUpdateRoutes(t *testing.T) {
clientNetworkWatchersExpected: 0,
clientNetworkWatchersExpectedAllowed: 1,
},
{
name: "No Small Client Route Should Be Added (IPv6)",
inputRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("::/0"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputSerial: 1,
clientNetworkWatchersExpected: 0,
clientNetworkWatchersExpectedAllowed: 1,
isV6: true,
},
{
name: "Remove 1 Client Route",
inputInitRoutes: []*route.Route{
@@ -244,6 +467,46 @@ func TestManagerUpdateRoutes(t *testing.T) {
inputSerial: 1,
clientNetworkWatchersExpected: 1,
},
{
name: "Remove 1 Client Route (IPv6)",
inputInitRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeB",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8::abcd:7890/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputSerial: 1,
clientNetworkWatchersExpected: 1,
isV6: true,
},
{
name: "Update Route to HA",
inputInitRoutes: []*route.Route{
@@ -293,6 +556,56 @@ func TestManagerUpdateRoutes(t *testing.T) {
inputSerial: 1,
clientNetworkWatchersExpected: 1,
},
{
name: "Update Route to HA (IPv6)",
inputInitRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeB",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8::abcd:7890/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeA",
Peer: remotePeerKey2,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputSerial: 1,
clientNetworkWatchersExpected: 1,
isV6: true,
},
{
name: "Remove Client Routes",
inputInitRoutes: []*route.Route{
@@ -321,6 +634,35 @@ func TestManagerUpdateRoutes(t *testing.T) {
inputSerial: 1,
clientNetworkWatchersExpected: 0,
},
{
name: "Remove Client Routes (IPv6)",
inputInitRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeB",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8::abcd:7890/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputRoutes: []*route.Route{},
inputSerial: 1,
clientNetworkWatchersExpected: 0,
isV6: true,
},
{
name: "Remove All Routes",
inputInitRoutes: []*route.Route{
@@ -350,6 +692,36 @@ func TestManagerUpdateRoutes(t *testing.T) {
serverRoutesExpected: 0,
clientNetworkWatchersExpected: 0,
},
{
name: "Remove All Routes (IPv6)",
inputInitRoutes: []*route.Route{
{
ID: "a",
NetID: "routeA",
Peer: localPeerKey,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "b",
NetID: "routeB",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8::abcd:7890/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputRoutes: []*route.Route{},
inputSerial: 1,
serverRoutesExpected: 0,
clientNetworkWatchersExpected: 0,
isV6: true,
},
{
name: "HA server should not register routes from the same HA group",
inputRoutes: []*route.Route{
@@ -398,16 +770,74 @@ func TestManagerUpdateRoutes(t *testing.T) {
serverRoutesExpected: 2,
clientNetworkWatchersExpected: 1,
},
{
name: "HA server should not register routes from the same HA group (IPv6)",
inputRoutes: []*route.Route{
{
ID: "l1",
NetID: "routeA",
Peer: localPeerKey,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "l2",
NetID: "routeA",
Peer: localPeerKey,
Network: netip.MustParsePrefix("2001:db8::abcd:7890/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "r1",
NetID: "routeA",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
{
ID: "r2",
NetID: "routeC",
Peer: remotePeerKey1,
Network: netip.MustParsePrefix("2001:db8::abcd:789f/128"),
NetworkType: route.IPv6Network,
Metric: 9999,
Masquerade: false,
Enabled: true,
},
},
inputSerial: 1,
serverRoutesExpected: 2,
clientNetworkWatchersExpected: 1,
isV6: true,
},
}
for n, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
v6Addr := ""
//goland:noinspection GoBoolExpressions
if !iface.SupportsIPv6() && testCase.isV6 {
t.Skip("Platform does not support IPv6, skipping IPv6 test...")
} else if testCase.isV6 {
v6Addr = "2001:db8::4242:4711/128"
}
peerPrivateKey, _ := wgtypes.GeneratePrivateKey()
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun43%d", n), "100.65.65.2/24", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun43%d", n), "100.65.65.2/24", v6Addr, 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
require.NoError(t, err, "should create testing WGIface interface")
defer wgInterface.Close()

View File

@@ -64,6 +64,10 @@ func (m *MockManager) EnableServerRouter(firewall firewall.Manager) error {
panic("implement me")
}
func (m *MockManager) ResetV6Routes() {
panic("implement me")
}
// Stop mock implementation of Stop from Manager interface
func (m *MockManager) Stop() {
if m.StopFunc != nil {

View File

@@ -70,7 +70,7 @@ func (m *defaultServerRouter) updateRoutes(routesMap map[route.ID]*route.Route)
}
if len(m.routes) > 0 {
err := enableIPForwarding()
err := enableIPForwarding(m.wgInterface.Address6() != nil)
if err != nil {
return err
}
@@ -79,7 +79,7 @@ func (m *defaultServerRouter) updateRoutes(routesMap map[route.ID]*route.Route)
return nil
}
func (m *defaultServerRouter) removeFromServerNetwork(route *route.Route) error {
func (m *defaultServerRouter) removeFromServerNetwork(rt *route.Route) error {
select {
case <-m.ctx.Done():
log.Infof("Not removing from server network because context is done")
@@ -87,28 +87,32 @@ func (m *defaultServerRouter) removeFromServerNetwork(route *route.Route) error
default:
m.mux.Lock()
defer m.mux.Unlock()
routerPair, err := routeToRouterPair(m.wgInterface.Address().Masked().String(), route)
routingAddress := m.wgInterface.Address().Masked().String()
if rt.NetworkType == route.IPv6Network {
if m.wgInterface.Address6() == nil {
return fmt.Errorf("attempted to add route for IPv6 even though device has no v6 address")
}
routingAddress = m.wgInterface.Address6().Masked().String()
}
routerPair, err := routeToRouterPair(routingAddress, rt)
if err != nil {
return fmt.Errorf("parse prefix: %w", err)
}
err = m.firewall.RemoveRoutingRules(routerPair)
if err != nil {
return fmt.Errorf("remove routing rules: %w", err)
return err
}
delete(m.routes, route.ID)
delete(m.routes, rt.ID)
state := m.statusRecorder.GetLocalPeerState()
delete(state.Routes, route.Network.String())
delete(state.Routes, rt.Network.String())
m.statusRecorder.UpdateLocalPeerState(state)
return nil
}
}
func (m *defaultServerRouter) addToServerNetwork(route *route.Route) error {
func (m *defaultServerRouter) addToServerNetwork(rt *route.Route) error {
select {
case <-m.ctx.Done():
log.Infof("Not adding to server network because context is done")
@@ -116,8 +120,15 @@ func (m *defaultServerRouter) addToServerNetwork(route *route.Route) error {
default:
m.mux.Lock()
defer m.mux.Unlock()
routingAddress := m.wgInterface.Address().Masked().String()
if rt.NetworkType == route.IPv6Network {
if m.wgInterface.Address6() == nil {
return fmt.Errorf("attempted to add route for IPv6 even though device has no v6 address")
}
routingAddress = m.wgInterface.Address6().Masked().String()
}
routerPair, err := routeToRouterPair(m.wgInterface.Address().Masked().String(), route)
routerPair, err := routeToRouterPair(routingAddress, rt)
if err != nil {
return fmt.Errorf("parse prefix: %w", err)
}
@@ -127,13 +138,13 @@ func (m *defaultServerRouter) addToServerNetwork(route *route.Route) error {
return fmt.Errorf("insert routing rules: %w", err)
}
m.routes[route.ID] = route
m.routes[rt.ID] = rt
state := m.statusRecorder.GetLocalPeerState()
if state.Routes == nil {
state.Routes = map[string]struct{}{}
}
state.Routes[route.Network.String()] = struct{}{}
state.Routes[rt.Network.String()] = struct{}{}
m.statusRecorder.UpdateLocalPeerState(state)
return nil
@@ -144,10 +155,17 @@ func (m *defaultServerRouter) cleanUp() {
m.mux.Lock()
defer m.mux.Unlock()
for _, r := range m.routes {
routerPair, err := routeToRouterPair(m.wgInterface.Address().Masked().String(), r)
routingAddress := m.wgInterface.Address().Masked().String()
if r.NetworkType == route.IPv6Network {
if m.wgInterface.Address6() == nil {
log.Errorf("attempted to remove route for IPv6 even though device has no v6 address")
continue
}
routingAddress = m.wgInterface.Address6().Masked().String()
}
routerPair, err := routeToRouterPair(routingAddress, r)
if err != nil {
log.Errorf("Failed to convert route to router pair: %v", err)
continue
log.Errorf("parse prefix: %v", err)
}
err = m.firewall.RemoveRoutingRules(routerPair)

View File

@@ -122,6 +122,16 @@ func ipToAddr(ip net.IP, intf *net.Interface) (netip.Addr, error) {
}
func existsInRouteTable(prefix netip.Prefix) (bool, error) {
linkLocalPrefix, err := netip.ParsePrefix("fe80::/10")
if err != nil {
return false, err
}
if prefix.Addr().Is6() && linkLocalPrefix.Contains(prefix.Addr()) {
// The link local prefix is not explicitly part of the routing table, but should be considered as such.
return true, nil
}
routes, err := getRoutesFromTable()
if err != nil {
return false, fmt.Errorf("get routes from table: %w", err)

View File

@@ -43,11 +43,6 @@ func routeCmd(action string, prefix netip.Prefix, nexthop netip.Addr, intf *net.
}
if prefix.Addr().Is6() {
inet = "-inet6"
// Special case for IPv6 split default route, pointing to the wg interface fails
// TODO: Remove once we have IPv6 support on the interface
if prefix.Bits() == 1 {
intf = &net.Interface{Name: "lo0"}
}
}
args := []string{"-n", action, inet, network}

View File

@@ -19,7 +19,7 @@ func cleanupRouting() error {
return nil
}
func enableIPForwarding() error {
func enableIPForwarding(includeV6 bool) error {
log.Infof("Enable IP forwarding is not implemented on %s", runtime.GOOS)
return nil
}

View File

@@ -184,12 +184,6 @@ func addVPNRoute(prefix netip.Prefix, intf *net.Interface) error {
// No need to check if routes exist as main table takes precedence over the VPN table via Rule 1
// TODO remove this once we have ipv6 support
if prefix == defaultv4 {
if err := addUnreachableRoute(defaultv6, NetbirdVPNTableID); err != nil {
return fmt.Errorf("add blackhole: %w", err)
}
}
if err := addRoute(prefix, netip.Addr{}, intf, NetbirdVPNTableID); err != nil {
return fmt.Errorf("add route: %w", err)
}
@@ -201,12 +195,6 @@ func removeVPNRoute(prefix netip.Prefix, intf *net.Interface) error {
return genericRemoveVPNRoute(prefix, intf)
}
// TODO remove this once we have ipv6 support
if prefix == defaultv4 {
if err := removeUnreachableRoute(defaultv6, NetbirdVPNTableID); err != nil {
return fmt.Errorf("remove unreachable route: %w", err)
}
}
if err := removeRoute(prefix, netip.Addr{}, intf, NetbirdVPNTableID); err != nil {
return fmt.Errorf("remove route: %w", err)
}
@@ -282,6 +270,9 @@ func addRoute(prefix netip.Prefix, addr netip.Addr, intf *net.Interface, tableID
// addUnreachableRoute adds an unreachable route for the specified IP family and routing table.
// ipFamily should be netlink.FAMILY_V4 for IPv4 or netlink.FAMILY_V6 for IPv6.
// tableID specifies the routing table to which the unreachable route will be added.
// TODO should this be kept in for future use? If so, the linter needs to be told that this unreachable function should
//
// be kept
func addUnreachableRoute(prefix netip.Prefix, tableID int) error {
_, ipNet, err := net.ParseCIDR(prefix.String())
if err != nil {
@@ -302,6 +293,9 @@ func addUnreachableRoute(prefix netip.Prefix, tableID int) error {
return nil
}
// TODO should this be kept in for future use? If so, the linter needs to be told that this unreachable function should
//
// be kept
func removeUnreachableRoute(prefix netip.Prefix, tableID int) error {
_, ipNet, err := net.ParseCIDR(prefix.String())
if err != nil {
@@ -376,8 +370,14 @@ func flushRoutes(tableID, family int) error {
return result.ErrorOrNil()
}
func enableIPForwarding() error {
func enableIPForwarding(includeV6 bool) error {
_, err := setSysctl(ipv4ForwardingPath, 1, false)
if err != nil {
return err
}
if includeV6 {
_, err = setSysctl(ipv4ForwardingPath, 1, false)
}
return err
}

View File

@@ -10,7 +10,7 @@ import (
log "github.com/sirupsen/logrus"
)
func enableIPForwarding() error {
func enableIPForwarding(includeV6 bool) error {
log.Infof("Enable IP forwarding is not implemented on %s", runtime.GOOS)
return nil
}

View File

@@ -6,6 +6,8 @@ import (
"bytes"
"context"
"fmt"
"github.com/google/gopacket/routing"
"github.com/netbirdio/netbird/client/firewall"
"net"
"net/netip"
"os"
@@ -46,18 +48,39 @@ func TestAddRemoveRoutes(t *testing.T) {
shouldRouteToWireguard: false,
shouldBeRemoved: false,
},
{
name: "Should Add And Remove Route 2001:db8:1234:5678::/64",
prefix: netip.MustParsePrefix("2001:db8:1234:5678::/64"),
shouldRouteToWireguard: true,
shouldBeRemoved: true,
},
{
name: "Should Not Add Or Remove Route ::1/128",
prefix: netip.MustParsePrefix("::1/128"),
shouldRouteToWireguard: false,
shouldBeRemoved: false,
},
}
for n, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
t.Setenv("NB_DISABLE_ROUTE_CACHE", "true")
v6Addr := ""
hasV6DefaultRoute, err := EnvironmentHasIPv6DefaultRoute()
//goland:noinspection GoBoolExpressions
if (!iface.SupportsIPv6() || !firewall.SupportsIPv6() || !hasV6DefaultRoute || err != nil) && testCase.prefix.Addr().Is6() {
t.Skip("Platform does not support IPv6, skipping IPv6 test...")
} else if testCase.prefix.Addr().Is6() {
v6Addr = "2001:db8::4242:4711/128"
}
peerPrivateKey, _ := wgtypes.GeneratePrivateKey()
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", v6Addr, 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
require.NoError(t, err, "should create testing WGIface interface")
defer wgInterface.Close()
@@ -92,6 +115,10 @@ func TestAddRemoveRoutes(t *testing.T) {
internetGateway, _, err := GetNextHop(netip.MustParseAddr("0.0.0.0"))
require.NoError(t, err)
if testCase.prefix.Addr().Is6() {
internetGateway, _, err = GetNextHop(netip.MustParseAddr("::/0"))
}
require.NoError(t, err)
if testCase.shouldBeRemoved {
require.Equal(t, internetGateway, prefixGateway, "route should be pointing to default internet gateway")
@@ -151,6 +178,18 @@ func TestAddExistAndRemoveRoute(t *testing.T) {
if err != nil {
t.Fatal("shouldn't return error when fetching the gateway: ", err)
}
var defaultGateway6 *netip.Addr
hasV6DefaultRoute, err := EnvironmentHasIPv6DefaultRoute()
//goland:noinspection GoBoolExpressions
if iface.SupportsIPv6() && firewall.SupportsIPv6() && hasV6DefaultRoute && err == nil {
gw6, _, err := GetNextHop(netip.MustParseAddr("::"))
gw6 = gw6.WithZone("")
defaultGateway6 = &gw6
t.Log("defaultGateway6: ", defaultGateway6)
if err != nil {
t.Fatal("shouldn't return error when fetching the IPv6 gateway: ", err)
}
}
testCases := []struct {
name string
prefix netip.Prefix
@@ -185,6 +224,43 @@ func TestAddExistAndRemoveRoute(t *testing.T) {
preExistingPrefix: netip.MustParsePrefix("100.100.0.0/16"),
shouldAddRoute: false,
},
{
name: "Should Add And Remove random Route (IPv6)",
prefix: netip.MustParsePrefix("2001:db8::abcd/128"),
shouldAddRoute: true,
},
{
name: "Should Add Route if bigger network exists (IPv6)",
prefix: netip.MustParsePrefix("2001:db8:b14d:abcd:1234::/96"),
preExistingPrefix: netip.MustParsePrefix("2001:db8:b14d:abcd::/64"),
shouldAddRoute: true,
},
{
name: "Should Add Route if smaller network exists (IPv6)",
prefix: netip.MustParsePrefix("2001:db8:b14d::/48"),
preExistingPrefix: netip.MustParsePrefix("2001:db8:b14d:abcd::/64"),
shouldAddRoute: true,
},
{
name: "Should Not Add Route if same network exists (IPv6)",
prefix: netip.MustParsePrefix("2001:db8:b14d:abcd::/64"),
preExistingPrefix: netip.MustParsePrefix("2001:db8:b14d:abcd::/64"),
shouldAddRoute: false,
},
}
if defaultGateway6 != nil {
testCases = append(testCases, []struct {
name string
prefix netip.Prefix
preExistingPrefix netip.Prefix
shouldAddRoute bool
}{
{
name: "Should Not Add Route if overlaps with default gateway (IPv6)",
prefix: netip.MustParsePrefix(defaultGateway6.String() + "/127"),
shouldAddRoute: false,
},
}...)
}
for n, testCase := range testCases {
@@ -198,12 +274,19 @@ func TestAddExistAndRemoveRoute(t *testing.T) {
t.Setenv("NB_USE_LEGACY_ROUTING", "true")
t.Setenv("NB_DISABLE_ROUTE_CACHE", "true")
v6Addr := ""
if testCase.prefix.Addr().Is6() && defaultGateway6 == nil {
t.Skip("Platform does not support IPv6, skipping IPv6 test...")
} else if testCase.prefix.Addr().Is6() {
v6Addr = "2001:db8::4242:4711/128"
}
peerPrivateKey, _ := wgtypes.GeneratePrivateKey()
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
wgInterface, err := iface.NewWGIFace(fmt.Sprintf("utun53%d", n), "100.65.75.2/24", v6Addr, 33100, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
require.NoError(t, err, "should create testing WGIface interface")
defer wgInterface.Close()
@@ -249,6 +332,11 @@ func TestAddExistAndRemoveRoute(t *testing.T) {
}
func TestIsSubRange(t *testing.T) {
// Note: This test may fail for IPv6 in some environments, where there actually exists another route that the
// determined prefix is a sub-range of.
hasV6DefaultRoute, err := EnvironmentHasIPv6DefaultRoute()
shouldIncludeV6Routes := iface.SupportsIPv6() && firewall.SupportsIPv6() && hasV6DefaultRoute && err == nil
addresses, err := net.InterfaceAddrs()
if err != nil {
t.Fatal("shouldn't return error when fetching interface addresses: ", err)
@@ -258,7 +346,7 @@ func TestIsSubRange(t *testing.T) {
var nonSubRangeAddressPrefixes []netip.Prefix
for _, address := range addresses {
p := netip.MustParsePrefix(address.String())
if !p.Addr().IsLoopback() && p.Addr().Is4() && p.Bits() < 32 {
if !p.Addr().IsLoopback() && (p.Addr().Is4() && p.Bits() < 32) || (p.Addr().Is6() && shouldIncludeV6Routes && p.Bits() < 128) {
p2 := netip.PrefixFrom(p.Masked().Addr(), p.Bits()+1)
subRangeAddressPrefixes = append(subRangeAddressPrefixes, p2)
nonSubRangeAddressPrefixes = append(nonSubRangeAddressPrefixes, p.Masked())
@@ -286,16 +374,37 @@ func TestIsSubRange(t *testing.T) {
}
}
func EnvironmentHasIPv6DefaultRoute() (bool, error) {
//goland:noinspection GoBoolExpressions
if runtime.GOOS != "linux" {
// TODO when implementing IPv6 for other operating systems, this should be replaced with code that determines
// whether a default route for IPv6 exists (routing.Router panics on non-linux).
return false, nil
}
router, err := routing.New()
if err != nil {
return false, err
}
routeIface, _, _, err := router.Route(netip.MustParsePrefix("::/0").Addr().AsSlice())
if err != nil {
return false, err
}
return routeIface != nil, nil
}
func TestExistsInRouteTable(t *testing.T) {
addresses, err := net.InterfaceAddrs()
if err != nil {
t.Fatal("shouldn't return error when fetching interface addresses: ", err)
}
hasV6DefaultRoute, err := EnvironmentHasIPv6DefaultRoute()
shouldIncludeV6Routes := iface.SupportsIPv6() && firewall.SupportsIPv6() && hasV6DefaultRoute && err == nil
var addressPrefixes []netip.Prefix
for _, address := range addresses {
p := netip.MustParsePrefix(address.String())
if p.Addr().Is6() {
if p.Addr().Is6() && !shouldIncludeV6Routes {
continue
}
// Windows sometimes has hidden interface link local addrs that don't turn up on any interface
@@ -321,7 +430,7 @@ func TestExistsInRouteTable(t *testing.T) {
}
}
func createWGInterface(t *testing.T, interfaceName, ipAddressCIDR string, listenPort int) *iface.WGIface {
func createWGInterface(t *testing.T, interfaceName, ipAddressCIDR string, ipAddress6CIDR string, listenPort int) *iface.WGIface {
t.Helper()
peerPrivateKey, err := wgtypes.GeneratePrivateKey()
@@ -330,7 +439,7 @@ func createWGInterface(t *testing.T, interfaceName, ipAddressCIDR string, listen
newNet, err := stdnet.NewNet()
require.NoError(t, err)
wgInterface, err := iface.NewWGIFace(interfaceName, ipAddressCIDR, listenPort, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
wgInterface, err := iface.NewWGIFace(interfaceName, ipAddressCIDR, ipAddress6CIDR, listenPort, peerPrivateKey.String(), iface.DefaultMTU, newNet, nil)
require.NoError(t, err, "should create testing WireGuard interface")
err = wgInterface.Create()
@@ -348,12 +457,21 @@ func setupTestEnv(t *testing.T) {
setupDummyInterfacesAndRoutes(t)
wgIface := createWGInterface(t, expectedVPNint, "100.64.0.1/24", 51820)
v6Addr := ""
hasV6DefaultRoute, err := EnvironmentHasIPv6DefaultRoute()
//goland:noinspection GoBoolExpressions
if !iface.SupportsIPv6() || !firewall.SupportsIPv6() || !hasV6DefaultRoute || err != nil {
t.Skip("Platform does not support IPv6, skipping IPv6 test...")
} else {
v6Addr = "2001:db8::4242:4711/128"
}
wgIface := createWGInterface(t, expectedVPNint, "100.64.0.1/24", v6Addr, 51820)
t.Cleanup(func() {
assert.NoError(t, wgIface.Close())
})
_, _, err := setupRouting(nil, wgIface)
_, _, err = setupRouting(nil, wgIface)
require.NoError(t, err, "setupRouting should not return err")
t.Cleanup(func() {
assert.NoError(t, cleanupRouting())
@@ -412,9 +530,15 @@ func assertWGOutInterface(t *testing.T, prefix netip.Prefix, wgIface *iface.WGIf
prefixGateway, _, err := GetNextHop(prefix.Addr())
require.NoError(t, err, "GetNextHop should not return err")
nexthop := wgIface.Address().IP.String()
if prefix.Addr().Is6() {
nexthop = wgIface.Address6().IP.String()
}
if invert {
assert.NotEqual(t, wgIface.Address().IP.String(), prefixGateway.String(), "route should not point to wireguard interface IP")
assert.NotEqual(t, nexthop, prefixGateway.String(), "route should not point to wireguard interface IP")
} else {
assert.Equal(t, wgIface.Address().IP.String(), prefixGateway.String(), "route should point to wireguard interface IP")
assert.Equal(t, nexthop, prefixGateway.String(), "route should point to wireguard interface IP")
}
}

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<style>
body {
display: flex;
@@ -50,16 +50,17 @@
color: black;
}
</style>
<title>NetBird Login Successful</title>
</head>
<body>
<div class="container">
<div class="logo">
<img src="https://img.mailinblue.com/6211297/images/content_library/original/64bd4ce82e1ea753e439b6a2.png">
<img alt="netbird_logo" src="https://img.mailinblue.com/6211297/images/content_library/original/64bd4ce82e1ea753e439b6a2.png">
</div>
<br>
{{ if .Error }}
<svg xmlns="http://www.w3.org/2000/svg" height="50" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="none" stroke="red" stroke-width="3"/>
<svg height="50" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" fill="none" r="45" stroke="red" stroke-width="3"/>
<path d="M30 30 L70 70 M30 70 L70 30" fill="none" stroke="red" stroke-width="3"/>
</svg>
<div class="content">
@@ -69,8 +70,8 @@
{{ .Error }}.
</div>
{{ else }}
<svg xmlns="http://www.w3.org/2000/svg" height="50" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="none" stroke="#5cb85c" stroke-width="3"/>
<svg height="50" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" fill="none" r="45" stroke="#5cb85c" stroke-width="3"/>
<path d="M30 50 L45 65 L70 35" fill="none" stroke="#5cb85c" stroke-width="5"/>
</svg>
<div class="content">

View File

@@ -109,7 +109,6 @@ func (p *WGEBPFProxy) AddTurnConn(turnConn net.Conn) (net.Addr, error) {
// CloseConn doing nothing because this type of proxy implementation does not store the connection
func (p *WGEBPFProxy) CloseConn() error {
p.cancel()
return nil
}

View File

@@ -1806,6 +1806,91 @@ func (x *DebugBundleResponse) GetPath() string {
return ""
}
type GetLogLevelRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *GetLogLevelRequest) Reset() {
*x = GetLogLevelRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_daemon_proto_msgTypes[26]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetLogLevelRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetLogLevelRequest) ProtoMessage() {}
func (x *GetLogLevelRequest) ProtoReflect() protoreflect.Message {
mi := &file_daemon_proto_msgTypes[26]
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 GetLogLevelRequest.ProtoReflect.Descriptor instead.
func (*GetLogLevelRequest) Descriptor() ([]byte, []int) {
return file_daemon_proto_rawDescGZIP(), []int{26}
}
type GetLogLevelResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Level LogLevel `protobuf:"varint,1,opt,name=level,proto3,enum=daemon.LogLevel" json:"level,omitempty"`
}
func (x *GetLogLevelResponse) Reset() {
*x = GetLogLevelResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_daemon_proto_msgTypes[27]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetLogLevelResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetLogLevelResponse) ProtoMessage() {}
func (x *GetLogLevelResponse) ProtoReflect() protoreflect.Message {
mi := &file_daemon_proto_msgTypes[27]
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 GetLogLevelResponse.ProtoReflect.Descriptor instead.
func (*GetLogLevelResponse) Descriptor() ([]byte, []int) {
return file_daemon_proto_rawDescGZIP(), []int{27}
}
func (x *GetLogLevelResponse) GetLevel() LogLevel {
if x != nil {
return x.Level
}
return LogLevel_UNKNOWN
}
type SetLogLevelRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1817,7 +1902,7 @@ type SetLogLevelRequest struct {
func (x *SetLogLevelRequest) Reset() {
*x = SetLogLevelRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_daemon_proto_msgTypes[26]
mi := &file_daemon_proto_msgTypes[28]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1830,7 +1915,7 @@ func (x *SetLogLevelRequest) String() string {
func (*SetLogLevelRequest) ProtoMessage() {}
func (x *SetLogLevelRequest) ProtoReflect() protoreflect.Message {
mi := &file_daemon_proto_msgTypes[26]
mi := &file_daemon_proto_msgTypes[28]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1843,7 +1928,7 @@ func (x *SetLogLevelRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use SetLogLevelRequest.ProtoReflect.Descriptor instead.
func (*SetLogLevelRequest) Descriptor() ([]byte, []int) {
return file_daemon_proto_rawDescGZIP(), []int{26}
return file_daemon_proto_rawDescGZIP(), []int{28}
}
func (x *SetLogLevelRequest) GetLevel() LogLevel {
@@ -1862,7 +1947,7 @@ type SetLogLevelResponse struct {
func (x *SetLogLevelResponse) Reset() {
*x = SetLogLevelResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_daemon_proto_msgTypes[27]
mi := &file_daemon_proto_msgTypes[29]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1875,7 +1960,7 @@ func (x *SetLogLevelResponse) String() string {
func (*SetLogLevelResponse) ProtoMessage() {}
func (x *SetLogLevelResponse) ProtoReflect() protoreflect.Message {
mi := &file_daemon_proto_msgTypes[27]
mi := &file_daemon_proto_msgTypes[29]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1888,7 +1973,7 @@ func (x *SetLogLevelResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use SetLogLevelResponse.ProtoReflect.Descriptor instead.
func (*SetLogLevelResponse) Descriptor() ([]byte, []int) {
return file_daemon_proto_rawDescGZIP(), []int{27}
return file_daemon_proto_rawDescGZIP(), []int{29}
}
var File_daemon_proto protoreflect.FileDescriptor
@@ -2138,67 +2223,77 @@ var file_daemon_proto_rawDesc = []byte{
0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22,
0x29, 0x0a, 0x13, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x3c, 0x0a, 0x12, 0x53, 0x65,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65,
0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65,
0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x4c,
0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a,
0x62, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55,
0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x4e, 0x49,
0x43, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x09,
0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52,
0x4e, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x05, 0x12, 0x09, 0x0a,
0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43,
0x45, 0x10, 0x07, 0x32, 0xee, 0x05, 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, 0x12, 0x45, 0x0a,
0x0a, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, 0x61,
0x22, 0x3d, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22,
0x3c, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f,
0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x15, 0x0a,
0x13, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x62, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c,
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a,
0x05, 0x50, 0x41, 0x4e, 0x49, 0x43, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x41, 0x54, 0x41,
0x4c, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x08,
0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f,
0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x06, 0x12, 0x09, 0x0a,
0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x07, 0x32, 0xb8, 0x06, 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, 0x12, 0x45, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73,
0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f,
0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61,
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f,
0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65,
0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63,
0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x65, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75,
0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c,
0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74,
0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12,
0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75,
0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61,
0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x53, 0x65,
0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d,
0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53,
0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 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, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x53, 0x65, 0x6c,
0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d,
0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e,
0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x65, 0x73, 0x65, 0x6c, 0x65,
0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53,
0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75,
0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65,
0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42,
0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x48, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a,
0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65,
0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65,
0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x53, 0x65, 0x74,
0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f,
0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65,
0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 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 (
@@ -2214,7 +2309,7 @@ func file_daemon_proto_rawDescGZIP() []byte {
}
var file_daemon_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 28)
var file_daemon_proto_msgTypes = make([]protoimpl.MessageInfo, 30)
var file_daemon_proto_goTypes = []interface{}{
(LogLevel)(0), // 0: daemon.LogLevel
(*LoginRequest)(nil), // 1: daemon.LoginRequest
@@ -2243,16 +2338,18 @@ var file_daemon_proto_goTypes = []interface{}{
(*Route)(nil), // 24: daemon.Route
(*DebugBundleRequest)(nil), // 25: daemon.DebugBundleRequest
(*DebugBundleResponse)(nil), // 26: daemon.DebugBundleResponse
(*SetLogLevelRequest)(nil), // 27: daemon.SetLogLevelRequest
(*SetLogLevelResponse)(nil), // 28: daemon.SetLogLevelResponse
(*timestamp.Timestamp)(nil), // 29: google.protobuf.Timestamp
(*duration.Duration)(nil), // 30: google.protobuf.Duration
(*GetLogLevelRequest)(nil), // 27: daemon.GetLogLevelRequest
(*GetLogLevelResponse)(nil), // 28: daemon.GetLogLevelResponse
(*SetLogLevelRequest)(nil), // 29: daemon.SetLogLevelRequest
(*SetLogLevelResponse)(nil), // 30: daemon.SetLogLevelResponse
(*timestamp.Timestamp)(nil), // 31: google.protobuf.Timestamp
(*duration.Duration)(nil), // 32: google.protobuf.Duration
}
var file_daemon_proto_depIdxs = []int32{
19, // 0: daemon.StatusResponse.fullStatus:type_name -> daemon.FullStatus
29, // 1: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp
29, // 2: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp
30, // 3: daemon.PeerState.latency:type_name -> google.protobuf.Duration
31, // 1: daemon.PeerState.connStatusUpdate:type_name -> google.protobuf.Timestamp
31, // 2: daemon.PeerState.lastWireguardHandshake:type_name -> google.protobuf.Timestamp
32, // 3: daemon.PeerState.latency:type_name -> google.protobuf.Duration
16, // 4: daemon.FullStatus.managementState:type_name -> daemon.ManagementState
15, // 5: daemon.FullStatus.signalState:type_name -> daemon.SignalState
14, // 6: daemon.FullStatus.localPeerState:type_name -> daemon.LocalPeerState
@@ -2260,34 +2357,37 @@ var file_daemon_proto_depIdxs = []int32{
17, // 8: daemon.FullStatus.relays:type_name -> daemon.RelayState
18, // 9: daemon.FullStatus.dns_servers:type_name -> daemon.NSGroupState
24, // 10: daemon.ListRoutesResponse.routes:type_name -> daemon.Route
0, // 11: daemon.SetLogLevelRequest.level:type_name -> daemon.LogLevel
1, // 12: daemon.DaemonService.Login:input_type -> daemon.LoginRequest
3, // 13: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest
5, // 14: daemon.DaemonService.Up:input_type -> daemon.UpRequest
7, // 15: daemon.DaemonService.Status:input_type -> daemon.StatusRequest
9, // 16: daemon.DaemonService.Down:input_type -> daemon.DownRequest
11, // 17: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest
20, // 18: daemon.DaemonService.ListRoutes:input_type -> daemon.ListRoutesRequest
22, // 19: daemon.DaemonService.SelectRoutes:input_type -> daemon.SelectRoutesRequest
22, // 20: daemon.DaemonService.DeselectRoutes:input_type -> daemon.SelectRoutesRequest
25, // 21: daemon.DaemonService.DebugBundle:input_type -> daemon.DebugBundleRequest
27, // 22: daemon.DaemonService.SetLogLevel:input_type -> daemon.SetLogLevelRequest
2, // 23: daemon.DaemonService.Login:output_type -> daemon.LoginResponse
4, // 24: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse
6, // 25: daemon.DaemonService.Up:output_type -> daemon.UpResponse
8, // 26: daemon.DaemonService.Status:output_type -> daemon.StatusResponse
10, // 27: daemon.DaemonService.Down:output_type -> daemon.DownResponse
12, // 28: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse
21, // 29: daemon.DaemonService.ListRoutes:output_type -> daemon.ListRoutesResponse
23, // 30: daemon.DaemonService.SelectRoutes:output_type -> daemon.SelectRoutesResponse
23, // 31: daemon.DaemonService.DeselectRoutes:output_type -> daemon.SelectRoutesResponse
26, // 32: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse
28, // 33: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse
23, // [23:34] is the sub-list for method output_type
12, // [12:23] is the sub-list for method input_type
12, // [12:12] is the sub-list for extension type_name
12, // [12:12] is the sub-list for extension extendee
0, // [0:12] is the sub-list for field type_name
0, // 11: daemon.GetLogLevelResponse.level:type_name -> daemon.LogLevel
0, // 12: daemon.SetLogLevelRequest.level:type_name -> daemon.LogLevel
1, // 13: daemon.DaemonService.Login:input_type -> daemon.LoginRequest
3, // 14: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest
5, // 15: daemon.DaemonService.Up:input_type -> daemon.UpRequest
7, // 16: daemon.DaemonService.Status:input_type -> daemon.StatusRequest
9, // 17: daemon.DaemonService.Down:input_type -> daemon.DownRequest
11, // 18: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest
20, // 19: daemon.DaemonService.ListRoutes:input_type -> daemon.ListRoutesRequest
22, // 20: daemon.DaemonService.SelectRoutes:input_type -> daemon.SelectRoutesRequest
22, // 21: daemon.DaemonService.DeselectRoutes:input_type -> daemon.SelectRoutesRequest
25, // 22: daemon.DaemonService.DebugBundle:input_type -> daemon.DebugBundleRequest
27, // 23: daemon.DaemonService.GetLogLevel:input_type -> daemon.GetLogLevelRequest
29, // 24: daemon.DaemonService.SetLogLevel:input_type -> daemon.SetLogLevelRequest
2, // 25: daemon.DaemonService.Login:output_type -> daemon.LoginResponse
4, // 26: daemon.DaemonService.WaitSSOLogin:output_type -> daemon.WaitSSOLoginResponse
6, // 27: daemon.DaemonService.Up:output_type -> daemon.UpResponse
8, // 28: daemon.DaemonService.Status:output_type -> daemon.StatusResponse
10, // 29: daemon.DaemonService.Down:output_type -> daemon.DownResponse
12, // 30: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse
21, // 31: daemon.DaemonService.ListRoutes:output_type -> daemon.ListRoutesResponse
23, // 32: daemon.DaemonService.SelectRoutes:output_type -> daemon.SelectRoutesResponse
23, // 33: daemon.DaemonService.DeselectRoutes:output_type -> daemon.SelectRoutesResponse
26, // 34: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse
28, // 35: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse
30, // 36: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse
25, // [25:37] is the sub-list for method output_type
13, // [13:25] is the sub-list for method input_type
13, // [13:13] is the sub-list for extension type_name
13, // [13:13] is the sub-list for extension extendee
0, // [0:13] is the sub-list for field type_name
}
func init() { file_daemon_proto_init() }
@@ -2609,7 +2709,7 @@ func file_daemon_proto_init() {
}
}
file_daemon_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SetLogLevelRequest); i {
switch v := v.(*GetLogLevelRequest); i {
case 0:
return &v.state
case 1:
@@ -2621,6 +2721,30 @@ func file_daemon_proto_init() {
}
}
file_daemon_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetLogLevelResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_daemon_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SetLogLevelRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_daemon_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SetLogLevelResponse); i {
case 0:
return &v.state
@@ -2640,7 +2764,7 @@ func file_daemon_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_daemon_proto_rawDesc,
NumEnums: 1,
NumMessages: 28,
NumMessages: 30,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -40,6 +40,9 @@ service DaemonService {
// DebugBundle creates a debug bundle
rpc DebugBundle(DebugBundleRequest) returns (DebugBundleResponse) {}
// GetLogLevel gets the log level of the daemon
rpc GetLogLevel(GetLogLevelRequest) returns (GetLogLevelResponse) {}
// SetLogLevel sets the log level of the daemon
rpc SetLogLevel(SetLogLevelRequest) returns (SetLogLevelResponse) {}
};
@@ -256,6 +259,13 @@ enum LogLevel {
TRACE = 7;
}
message GetLogLevelRequest {
}
message GetLogLevelResponse {
LogLevel level = 1;
}
message SetLogLevelRequest {
LogLevel level = 1;
}

View File

@@ -39,6 +39,8 @@ type DaemonServiceClient interface {
DeselectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error)
// DebugBundle creates a debug bundle
DebugBundle(ctx context.Context, in *DebugBundleRequest, opts ...grpc.CallOption) (*DebugBundleResponse, error)
// GetLogLevel gets the log level of the daemon
GetLogLevel(ctx context.Context, in *GetLogLevelRequest, opts ...grpc.CallOption) (*GetLogLevelResponse, error)
// SetLogLevel sets the log level of the daemon
SetLogLevel(ctx context.Context, in *SetLogLevelRequest, opts ...grpc.CallOption) (*SetLogLevelResponse, error)
}
@@ -141,6 +143,15 @@ func (c *daemonServiceClient) DebugBundle(ctx context.Context, in *DebugBundleRe
return out, nil
}
func (c *daemonServiceClient) GetLogLevel(ctx context.Context, in *GetLogLevelRequest, opts ...grpc.CallOption) (*GetLogLevelResponse, error) {
out := new(GetLogLevelResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/GetLogLevel", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *daemonServiceClient) SetLogLevel(ctx context.Context, in *SetLogLevelRequest, opts ...grpc.CallOption) (*SetLogLevelResponse, error) {
out := new(SetLogLevelResponse)
err := c.cc.Invoke(ctx, "/daemon.DaemonService/SetLogLevel", in, out, opts...)
@@ -175,6 +186,8 @@ type DaemonServiceServer interface {
DeselectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error)
// DebugBundle creates a debug bundle
DebugBundle(context.Context, *DebugBundleRequest) (*DebugBundleResponse, error)
// GetLogLevel gets the log level of the daemon
GetLogLevel(context.Context, *GetLogLevelRequest) (*GetLogLevelResponse, error)
// SetLogLevel sets the log level of the daemon
SetLogLevel(context.Context, *SetLogLevelRequest) (*SetLogLevelResponse, error)
mustEmbedUnimplementedDaemonServiceServer()
@@ -214,6 +227,9 @@ func (UnimplementedDaemonServiceServer) DeselectRoutes(context.Context, *SelectR
func (UnimplementedDaemonServiceServer) DebugBundle(context.Context, *DebugBundleRequest) (*DebugBundleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DebugBundle not implemented")
}
func (UnimplementedDaemonServiceServer) GetLogLevel(context.Context, *GetLogLevelRequest) (*GetLogLevelResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLogLevel not implemented")
}
func (UnimplementedDaemonServiceServer) SetLogLevel(context.Context, *SetLogLevelRequest) (*SetLogLevelResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetLogLevel not implemented")
}
@@ -410,6 +426,24 @@ func _DaemonService_DebugBundle_Handler(srv interface{}, ctx context.Context, de
return interceptor(ctx, in, info, handler)
}
func _DaemonService_GetLogLevel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetLogLevelRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DaemonServiceServer).GetLogLevel(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/daemon.DaemonService/GetLogLevel",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DaemonServiceServer).GetLogLevel(ctx, req.(*GetLogLevelRequest))
}
return interceptor(ctx, in, info, handler)
}
func _DaemonService_SetLogLevel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetLogLevelRequest)
if err := dec(in); err != nil {
@@ -475,6 +509,10 @@ var DaemonService_ServiceDesc = grpc.ServiceDesc{
MethodName: "DebugBundle",
Handler: _DaemonService_DebugBundle_Handler,
},
{
MethodName: "GetLogLevel",
Handler: _DaemonService_GetLogLevel_Handler,
},
{
MethodName: "SetLogLevel",
Handler: _DaemonService_SetLogLevel_Handler,

View File

@@ -121,6 +121,12 @@ func (s *Server) anonymize(reader io.Reader, writer io.WriteCloser, errChan chan
}
}
// GetLogLevel gets the current logging level for the server.
func (s *Server) GetLogLevel(_ context.Context, _ *proto.GetLogLevelRequest) (*proto.GetLogLevelResponse, error) {
level := ParseLogLevel(log.GetLevel().String())
return &proto.GetLogLevelResponse{Level: level}, nil
}
// SetLogLevel sets the logging level for the server.
func (s *Server) SetLogLevel(_ context.Context, req *proto.SetLogLevelRequest) (*proto.SetLogLevelResponse, error) {
level, err := log.ParseLevel(req.Level.String())

28
client/server/log.go Normal file
View File

@@ -0,0 +1,28 @@
package server
import (
"strings"
"github.com/netbirdio/netbird/client/proto"
)
func ParseLogLevel(level string) proto.LogLevel {
switch strings.ToLower(level) {
case "panic":
return proto.LogLevel_PANIC
case "fatal":
return proto.LogLevel_FATAL
case "error":
return proto.LogLevel_ERROR
case "warn":
return proto.LogLevel_WARN
case "info":
return proto.LogLevel_INFO
case "debug":
return proto.LogLevel_DEBUG
case "trace":
return proto.LogLevel_TRACE
default:
return proto.LogLevel_UNKNOWN
}
}

View File

@@ -36,7 +36,7 @@ const (
maxRetryIntervalVar = "NB_CONN_MAX_RETRY_INTERVAL_TIME"
maxRetryTimeVar = "NB_CONN_MAX_RETRY_TIME_TIME"
retryMultiplierVar = "NB_CONN_RETRY_MULTIPLIER"
defaultInitialRetryTime = 14 * 24 * time.Hour
defaultInitialRetryTime = 30 * time.Minute
defaultMaxRetryInterval = 60 * time.Minute
defaultMaxRetryTime = 14 * 24 * time.Hour
defaultRetryMultiplier = 1.7

View File

@@ -106,10 +106,11 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve
return nil, "", err
}
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
store, err := server.NewStoreFromJson(config.Datadir, nil)
store, cleanUp, err := server.NewTestStoreFromJson(config.Datadir)
if err != nil {
return nil, "", err
}
t.Cleanup(cleanUp)
peersUpdateManager := server.NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}

View File

@@ -20,6 +20,9 @@ const OsVersionCtxKey = "OsVersion"
// OsNameCtxKey context key for operating system name
const OsNameCtxKey = "OsName"
// UiVersionCtxKey context key for user UI version
const UiVersionCtxKey = "user-agent"
type NetworkAddress struct {
NetIP netip.Prefix
Mac string
@@ -48,6 +51,7 @@ type Info struct {
SystemProductName string
SystemManufacturer string
Environment Environment
Ipv6Supported bool
}
// extractUserAgent extracts Netbird's agent (client) name and version from the outgoing context

View File

@@ -28,10 +28,19 @@ func GetInfo(ctx context.Context) *Info {
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)
gio := &Info{
GoOS: runtime.GOOS,
Kernel: kernel,
Platform: "unknown",
OS: "android",
OSVersion: osVersion(),
Hostname: extractDeviceName(ctx, "android"),
CPUs: runtime.NumCPU(),
WiretrusteeVersion: version.NetbirdVersion(),
UIVersion: extractUIVersion(ctx),
KernelVersion: kernelVersion,
Ipv6Supported: false,
}
return gio
}
@@ -45,6 +54,14 @@ func osVersion() string {
return run("/system/bin/getprop", "ro.build.version.release")
}
func extractUIVersion(ctx context.Context) string {
v, ok := ctx.Value(UiVersionCtxKey).(string)
if !ok {
return ""
}
return v
}
func run(name string, arg ...string) string {
cmd := exec.Command(name, arg...)
cmd.Stdin = strings.NewReader("some")

View File

@@ -61,6 +61,7 @@ func GetInfo(ctx context.Context) *Info {
SystemProductName: prodName,
SystemManufacturer: manufacturer,
Environment: env,
Ipv6Supported: false,
}
systemHostname, _ := os.Hostname()

View File

@@ -31,7 +31,7 @@ func GetInfo(ctx context.Context) *Info {
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}
gio := &Info{Kernel: osInfo[0], Platform: runtime.GOARCH, OS: osInfo[2], GoOS: runtime.GOOS, CPUs: runtime.NumCPU(), KernelVersion: osInfo[1], Environment: env, Ipv6Supported: false}
systemHostname, _ := os.Hostname()
gio.Hostname = extractDeviceName(ctx, systemHostname)

View File

@@ -6,6 +6,8 @@ package system
import (
"bytes"
"context"
"github.com/netbirdio/netbird/client/firewall"
"github.com/netbirdio/netbird/iface"
"os"
"os/exec"
"runtime"
@@ -84,6 +86,7 @@ func GetInfo(ctx context.Context) *Info {
SystemProductName: prodName,
SystemManufacturer: manufacturer,
Environment: env,
Ipv6Supported: _checkIPv6Support(),
}
return gio
@@ -122,3 +125,8 @@ func sysInfo() (serialNumber string, productName string, manufacturer string) {
si.GetSysInfo()
return si.Chassis.Serial, si.Product.Name, si.Product.Vendor
}
func _checkIPv6Support() bool {
return firewall.SupportsIPv6() &&
iface.SupportsIPv6()
}

View File

@@ -75,6 +75,7 @@ func GetInfo(ctx context.Context) *Info {
SystemProductName: prodName,
SystemManufacturer: manufacturer,
Environment: env,
Ipv6Supported: false,
}
systemHostname, _ := os.Hostname()

View File

@@ -399,6 +399,7 @@ func (s *serviceClient) updateStatus() error {
status, err := conn.Status(s.ctx, &proto.StatusRequest{})
if err != nil {
log.Errorf("get service status: %v", err)
s.setDisconnectedStatus()
return err
}
@@ -426,17 +427,7 @@ func (s *serviceClient) updateStatus() error {
s.mRoutes.Enable()
systrayIconState = true
} else if status.Status != string(internal.StatusConnected) && s.mUp.Disabled() {
s.connected = false
if s.isUpdateIconActive {
systray.SetIcon(s.icUpdateDisconnected)
} else {
systray.SetIcon(s.icDisconnected)
}
systray.SetTooltip("NetBird (Disconnected)")
s.mStatus.SetTitle("Disconnected")
s.mDown.Disable()
s.mUp.Enable()
s.mRoutes.Disable()
s.setDisconnectedStatus()
systrayIconState = false
}
@@ -481,6 +472,20 @@ func (s *serviceClient) updateStatus() error {
return nil
}
func (s *serviceClient) setDisconnectedStatus() {
s.connected = false
if s.isUpdateIconActive {
systray.SetIcon(s.icUpdateDisconnected)
} else {
systray.SetIcon(s.icDisconnected)
}
systray.SetTooltip("NetBird (Disconnected)")
s.mStatus.SetTitle("Disconnected")
s.mDown.Disable()
s.mUp.Enable()
s.mRoutes.Disable()
}
func (s *serviceClient) onTrayReady() {
systray.SetIcon(s.icDisconnected)
systray.SetTooltip("NetBird")

141
go.mod
View File

@@ -1,33 +1,31 @@
module github.com/netbirdio/netbird
go 1.21
toolchain go1.21.0
go 1.21.0
require (
cunicu.li/go-rosenpass v0.4.0
github.com/cenkalti/backoff/v4 v4.1.3
github.com/cenkalti/backoff/v4 v4.3.0
github.com/cloudflare/circl v1.3.3 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/golang/protobuf v1.5.3
github.com/golang/protobuf v1.5.4
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.0
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.18.1
github.com/onsi/gomega v1.23.0
github.com/pion/ice/v3 v3.0.2
github.com/rs/cors v1.8.0
github.com/sirupsen/logrus v1.9.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0
github.com/spf13/pflag v1.0.5
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
golang.org/x/crypto v0.21.0
golang.org/x/sys v0.18.0
github.com/vishvananda/netlink v1.2.1-beta.2
golang.org/x/crypto v0.23.0
golang.org/x/sys v0.20.0
golang.zx2c4.com/wireguard v0.0.0-20230704135630-469159ecf7d1
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
golang.zx2c4.com/wireguard/windows v0.5.3
google.golang.org/grpc v1.56.3
google.golang.org/protobuf v1.31.0
google.golang.org/grpc v1.64.0
google.golang.org/protobuf v1.34.1
gopkg.in/natefinch/lumberjack.v2 v2.0.0
)
@@ -35,7 +33,7 @@ require (
fyne.io/fyne/v2 v2.1.4
github.com/TheJumpCloud/jcapi-go v3.0.0+incompatible
github.com/c-robinson/iplib v1.0.3
github.com/cilium/ebpf v0.11.0
github.com/cilium/ebpf v0.15.0
github.com/coreos/go-iptables v0.7.0
github.com/creack/pty v1.1.18
github.com/eko/gocache/v3 v3.1.1
@@ -44,7 +42,7 @@ require (
github.com/gliderlabs/ssh v0.3.4
github.com/godbus/dbus/v5 v5.1.0
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.9
github.com/google/go-cmp v0.6.0
github.com/google/gopacket v1.1.19
github.com/google/martian/v3 v3.0.0
github.com/google/nftables v0.0.0-20220808154552-2eca00135732
@@ -54,13 +52,13 @@ require (
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.1
github.com/magiconair/properties v1.8.5
github.com/magiconair/properties v1.8.7
github.com/mattn/go-sqlite3 v1.14.19
github.com/mdlayher/socket v0.4.1
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/integrations v0.0.0-20240415094251-369eb33c9b01
github.com/netbirdio/management-integrations/integrations v0.0.0-20240524104853-69c6d89826cd
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
@@ -68,42 +66,58 @@ require (
github.com/pion/stun/v2 v2.0.0
github.com/pion/transport/v3 v3.0.1
github.com/pion/turn/v3 v3.0.1
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/client_golang v1.19.1
github.com/rs/xid v1.3.0
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
github.com/stretchr/testify v1.8.4
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.31.0
github.com/testcontainers/testcontainers-go/modules/postgres v0.31.0
github.com/things-go/go-socks5 v0.0.4
github.com/yusufpapurcu/wmi v1.2.3
github.com/yusufpapurcu/wmi v1.2.4
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
go.opentelemetry.io/otel/sdk/metric v0.33.0
go.opentelemetry.io/otel v1.26.0
go.opentelemetry.io/otel/exporters/prometheus v0.48.0
go.opentelemetry.io/otel/metric v1.26.0
go.opentelemetry.io/otel/sdk/metric v1.26.0
goauthentik.io/api/v3 v3.2023051.3
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028
golang.org/x/net v0.23.0
golang.org/x/oauth2 v0.8.0
golang.org/x/sync v0.3.0
golang.org/x/term v0.18.0
google.golang.org/api v0.126.0
golang.org/x/net v0.25.0
golang.org/x/oauth2 v0.19.0
golang.org/x/sync v0.7.0
golang.org/x/term v0.20.0
google.golang.org/api v0.177.0
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/postgres v1.5.7
gorm.io/driver/sqlite v1.5.3
gorm.io/gorm v1.25.4
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde
)
require (
cloud.google.com/go/compute v1.19.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
cloud.google.com/go/auth v0.3.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
cloud.google.com/go/compute/metadata v0.3.0 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.12.3 // indirect
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/containerd v1.7.16 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v26.1.3+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect
@@ -113,59 +127,78 @@ require (
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/s2a-go v0.1.4 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.10.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/kelseyhightower/envconfig v1.4.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/klauspost/compress v1.17.8 // indirect
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
github.com/pegasus-kv/thrift v0.13.0 // indirect
github.com/pion/dtls/v2 v2.2.10 // indirect
github.com/pion/mdns v0.0.12 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/transport/v2 v2.2.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.53.0 // indirect
github.com/prometheus/procfs v0.15.0 // indirect
github.com/shirou/gopsutil/v3 v3.24.4 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/yuin/goldmark v1.4.13 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.11.1 // indirect
go.opentelemetry.io/otel/trace v1.11.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
go.opentelemetry.io/otel/sdk v1.26.0 // indirect
go.opentelemetry.io/otel/trace v1.26.0 // indirect
golang.org/x/image v0.10.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240509183442-62759503f434 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240515191416-fc5f0ca64291 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect
k8s.io/apimachinery v0.23.16 // indirect
k8s.io/apimachinery v0.26.2 // indirect
)
replace github.com/kardianos/service => github.com/netbirdio/service v0.0.0-20230215170314-b923b89432b0

653
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -48,6 +48,11 @@ func (w *WGIface) Address() WGAddress {
return w.tun.WgAddress()
}
// Address6 returns the IPv6 interface address
func (w *WGIface) Address6() *WGAddress {
return w.tun.WgAddress6()
}
// Up configures a Wireguard interface
// The interface must exist before calling this method (e.g. call interface.Create() before)
func (w *WGIface) Up() (*bind.UniversalUDPMuxDefault, error) {
@@ -70,6 +75,23 @@ func (w *WGIface) UpdateAddr(newAddr string) error {
return w.tun.UpdateAddr(addr)
}
// UpdateAddr6 updates the IPv6 address of the interface
func (w *WGIface) UpdateAddr6(newAddr6 string) error {
w.mu.Lock()
defer w.mu.Unlock()
var addr *WGAddress
if newAddr6 != "" {
parsedAddr, err := parseWGAddress(newAddr6)
if err != nil {
return err
}
addr = &parsedAddr
}
return w.tun.UpdateAddr6(addr)
}
// UpdatePeer updates existing Wireguard Peer or creates a new one if doesn't exist
// Endpoint is optional
func (w *WGIface) UpdatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {

View File

@@ -3,11 +3,16 @@ package iface
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/pion/transport/v3"
)
// NewWGIFace Creates a new WireGuard interface instance
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
func NewWGIFace(iFaceName string, address string, address6 string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
if address6 != "" {
log.Errorf("Attempted to configure IPv6 address %s on unsupported operating system", address6)
}
wgAddress, err := parseWGAddress(address)
if err != nil {
return nil, err
@@ -38,3 +43,7 @@ func (w *WGIface) CreateOnAndroid(routes []string, dns string, searchDomains []s
func (w *WGIface) Create() error {
return fmt.Errorf("this function has not implemented on this platform")
}
func SupportsIPv6() bool {
return false
}

View File

@@ -6,13 +6,18 @@ package iface
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/pion/transport/v3"
"github.com/netbirdio/netbird/iface/netstack"
)
// NewWGIFace Creates a new WireGuard interface instance
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
func NewWGIFace(iFaceName string, address string, address6 string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
if address6 != "" {
log.Errorf("Attempted to configure IPv6 address %s on unsupported operating system", address6)
}
wgAddress, err := parseWGAddress(address)
if err != nil {
return nil, err
@@ -36,3 +41,7 @@ func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string,
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
return fmt.Errorf("this function has not implemented on this platform")
}
func SupportsIPv6() bool {
return false
}

View File

@@ -5,12 +5,17 @@ package iface
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/pion/transport/v3"
)
// NewWGIFace Creates a new WireGuard interface instance
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
func NewWGIFace(iFaceName string, address string, address6 string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
if address6 != "" {
log.Errorf("Attempted to configure IPv6 address %s on unsupported operating system", address6)
}
wgAddress, err := parseWGAddress(address)
if err != nil {
return nil, err
@@ -27,3 +32,7 @@ func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string,
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
return fmt.Errorf("this function has not implemented on this platform")
}
func SupportsIPv6() bool {
return false
}

View File

@@ -5,14 +5,14 @@ package iface
import (
"fmt"
"github.com/pion/transport/v3"
"github.com/netbirdio/netbird/iface/netstack"
"github.com/pion/transport/v3"
log "github.com/sirupsen/logrus"
"golang.org/x/net/nettest"
)
// NewWGIFace Creates a new WireGuard interface instance
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
func NewWGIFace(iFaceName string, address string, address6 string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
wgAddress, err := parseWGAddress(address)
if err != nil {
return nil, err
@@ -20,6 +20,10 @@ func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string,
wgIFace := &WGIface{}
if netstack.IsEnabled() || !WireGuardModuleIsLoaded() && address6 != "" {
log.Errorf("Attempted to configure IPv6 address %s on unsupported device implementation (netstack or tun).", address6)
}
// move the kernel/usp/netstack preference evaluation to upper layer
if netstack.IsEnabled() {
wgIFace.tun = newTunNetstackDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet, netstack.ListenAddr())
@@ -28,7 +32,16 @@ func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string,
}
if WireGuardModuleIsLoaded() {
wgIFace.tun = newTunDevice(iFaceName, wgAddress, wgPort, wgPrivKey, mtu, transportNet)
var wgAddress6 *WGAddress = nil
if address6 != "" {
tmpWgAddress6, err := parseWGAddress(address6)
wgAddress6 = &tmpWgAddress6
if err != nil {
return wgIFace, err
}
}
wgIFace.tun = newTunDevice(iFaceName, wgAddress, wgAddress6, wgPort, wgPrivKey, mtu, transportNet)
wgIFace.userspaceBind = false
return wgIFace, nil
}
@@ -45,3 +58,7 @@ func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string,
func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
return fmt.Errorf("this function has not implemented on this platform")
}
func SupportsIPv6() bool {
return nettest.SupportsIPv6() && WireGuardModuleIsLoaded() && !netstack.IsEnabled()
}

View File

@@ -3,6 +3,7 @@ package iface
import (
"fmt"
"net"
"net/netip"
"testing"
"time"
@@ -40,7 +41,7 @@ func TestWGIface_UpdateAddr(t *testing.T) {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, addr, wgPort, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, addr, "", wgPort, key, DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
@@ -79,8 +80,77 @@ func TestWGIface_UpdateAddr(t *testing.T) {
t.Error(err)
}
var found bool
for _, a := range addrs {
prefix, err := netip.ParsePrefix(a.String())
assert.NoError(t, err)
if prefix.Addr().Is4() {
found = true
assert.Equal(t, addr, prefix.String())
}
}
if !found {
t.Fatal("v4 address not found")
}
}
func TestWGIface_UpdateAddr6(t *testing.T) {
if !SupportsIPv6() {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+4)
addr := "100.64.0.1/8"
addr6 := "2001:db8:1234:abcd::42/64"
wgPort := 33100
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, addr, addr6, wgPort, key, DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
err = iface.Create()
if err != nil {
t.Fatal(err)
}
defer func() {
err = iface.Close()
if err != nil {
t.Error(err)
}
}()
_, err = iface.Up()
if err != nil {
t.Fatal(err)
}
addrs, err := getIfaceAddrs(ifaceName)
if err != nil {
t.Error(err)
}
assert.Equal(t, addr, addrs[0].String())
//update WireGuard address
addr = "100.64.0.2/8"
err = iface.UpdateAddr(addr)
if err != nil {
t.Fatal(err)
}
addrs, err = getIfaceAddrs(ifaceName)
if err != nil {
t.Error(err)
}
assert.Equal(t, addr6, addrs[1].String())
}
func getIfaceAddrs(ifaceName string) ([]net.Addr, error) {
@@ -102,7 +172,45 @@ func Test_CreateInterface(t *testing.T) {
if err != nil {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, wgIP, "", 33100, key, DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
err = iface.Create()
if err != nil {
t.Fatal(err)
}
defer func() {
err = iface.Close()
if err != nil {
t.Error(err)
}
}()
wg, err := wgctrl.New()
if err != nil {
t.Fatal(err)
}
defer func() {
err = wg.Close()
if err != nil {
t.Error(err)
}
}()
}
func Test_CreateInterface6(t *testing.T) {
if !SupportsIPv6() {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+1)
wgIP := "10.99.99.1/32"
wgIP6 := "2001:db8:1234:abcd::43/64"
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, wgIP6, 33100, key, DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
@@ -137,7 +245,46 @@ func Test_Close(t *testing.T) {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, wgPort, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, wgIP, "", wgPort, key, DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
err = iface.Create()
if err != nil {
t.Fatal(err)
}
wg, err := wgctrl.New()
if err != nil {
t.Fatal(err)
}
defer func() {
err = wg.Close()
if err != nil {
t.Error(err)
}
}()
err = iface.Close()
if err != nil {
t.Fatal(err)
}
}
func Test_Close6(t *testing.T) {
if !SupportsIPv6() {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+2)
wgIP := "10.99.99.2/32"
wgIP6 := "2001:db8:1234:abcd::44/64"
wgPort := 33100
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, wgIP6, wgPort, key, DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
@@ -170,7 +317,7 @@ func Test_ConfigureInterface(t *testing.T) {
if err != nil {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, wgPort, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, wgIP, "", wgPort, key, DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
@@ -218,7 +365,7 @@ func Test_UpdatePeer(t *testing.T) {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, wgIP, "", 33100, key, DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
@@ -271,6 +418,73 @@ func Test_UpdatePeer(t *testing.T) {
}
}
func Test_UpdatePeer6(t *testing.T) {
if !SupportsIPv6() {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+4)
wgIP := "10.99.99.9/30"
wgIP6 := "2001:db8:1234:abcd::45/64"
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, wgIP6, 33100, key, DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
err = iface.Create()
if err != nil {
t.Fatal(err)
}
defer func() {
err = iface.Close()
if err != nil {
t.Error(err)
}
}()
_, err = iface.Up()
if err != nil {
t.Fatal(err)
}
keepAlive := 15 * time.Second
allowedIP := "10.99.99.10/32"
allowedIP6 := "2001:db8:1234:abcd::46/128"
endpoint, err := net.ResolveUDPAddr("udp", "127.0.0.1:9900")
if err != nil {
t.Fatal(err)
}
err = iface.UpdatePeer(peerPubKey, allowedIP+","+allowedIP6, keepAlive, endpoint, nil)
if err != nil {
t.Fatal(err)
}
peer, err := getPeer(ifaceName, peerPubKey)
if err != nil {
t.Fatal(err)
}
if peer.PersistentKeepaliveInterval != keepAlive {
t.Fatal("configured peer with mismatched keepalive interval value")
}
if peer.Endpoint.String() != endpoint.String() {
t.Fatal("configured peer with mismatched endpoint")
}
var foundAllowedIP bool
for _, aip := range peer.AllowedIPs {
if aip.String() == allowedIP6 {
foundAllowedIP = true
break
}
}
if !foundAllowedIP {
t.Fatal("configured peer with mismatched Allowed IPs")
}
}
func Test_RemovePeer(t *testing.T) {
ifaceName := fmt.Sprintf("utun%d", WgIntNumber+4)
wgIP := "10.99.99.13/30"
@@ -279,7 +493,7 @@ func Test_RemovePeer(t *testing.T) {
t.Fatal(err)
}
iface, err := NewWGIFace(ifaceName, wgIP, 33100, key, DefaultMTU, newNet, nil)
iface, err := NewWGIFace(ifaceName, wgIP, "", 33100, key, DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
@@ -333,7 +547,7 @@ func Test_ConnectPeers(t *testing.T) {
t.Fatal(err)
}
iface1, err := NewWGIFace(peer1ifaceName, peer1wgIP, peer1wgPort, peer1Key.String(), DefaultMTU, newNet, nil)
iface1, err := NewWGIFace(peer1ifaceName, peer1wgIP, "", peer1wgPort, peer1Key.String(), DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
@@ -356,7 +570,113 @@ func Test_ConnectPeers(t *testing.T) {
if err != nil {
t.Fatal(err)
}
iface2, err := NewWGIFace(peer2ifaceName, peer2wgIP, peer2wgPort, peer2Key.String(), DefaultMTU, newNet, nil)
iface2, err := NewWGIFace(peer2ifaceName, peer2wgIP, "", peer2wgPort, peer2Key.String(), DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
err = iface2.Create()
if err != nil {
t.Fatal(err)
}
_, err = iface2.Up()
if err != nil {
t.Fatal(err)
}
peer2endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer2wgPort))
if err != nil {
t.Fatal(err)
}
defer func() {
err = iface1.Close()
if err != nil {
t.Error(err)
}
err = iface2.Close()
if err != nil {
t.Error(err)
}
}()
err = iface1.UpdatePeer(peer2Key.PublicKey().String(), peer2wgIP, keepAlive, peer2endpoint, nil)
if err != nil {
t.Fatal(err)
}
err = iface2.UpdatePeer(peer1Key.PublicKey().String(), peer1wgIP, keepAlive, peer1endpoint, nil)
if err != nil {
t.Fatal(err)
}
// todo: investigate why in some tests execution we need 30s
timeout := 30 * time.Second
timeoutChannel := time.After(timeout)
for {
select {
case <-timeoutChannel:
t.Fatalf("waiting for peer handshake timeout after %s", timeout.String())
default:
}
peer, gpErr := getPeer(peer1ifaceName, peer2Key.PublicKey().String())
if gpErr != nil {
t.Fatal(gpErr)
}
if !peer.LastHandshakeTime.IsZero() {
t.Log("peers successfully handshake")
break
}
}
}
func Test_ConnectPeers6(t *testing.T) {
if !SupportsIPv6() {
t.Skip("Environment does not support IPv6, skipping IPv6 test...")
}
peer1ifaceName := fmt.Sprintf("utun%d", WgIntNumber+400)
peer1wgIP := "10.99.99.17/30"
peer1wgIP6 := "2001:db8:1234:abcd::47/64"
peer1Key, _ := wgtypes.GeneratePrivateKey()
peer1wgPort := 33100
peer2ifaceName := "utun500"
peer2wgIP := "10.99.99.18/30"
peer2wgIP6 := "2001:db8:1234:abcd::48/64"
peer2Key, _ := wgtypes.GeneratePrivateKey()
peer2wgPort := 33200
keepAlive := 1 * time.Second
newNet, err := stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
iface1, err := NewWGIFace(peer1ifaceName, peer1wgIP, peer1wgIP6, peer1wgPort, peer1Key.String(), DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}
err = iface1.Create()
if err != nil {
t.Fatal(err)
}
_, err = iface1.Up()
if err != nil {
t.Fatal(err)
}
peer1endpoint, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", peer1wgPort))
if err != nil {
t.Fatal(err)
}
newNet, err = stdnet.NewNet()
if err != nil {
t.Fatal(err)
}
iface2, err := NewWGIFace(peer2ifaceName, peer2wgIP, peer2wgIP6, peer2wgPort, peer2Key.String(), DefaultMTU, newNet, nil)
if err != nil {
t.Fatal(err)
}

View File

@@ -3,13 +3,18 @@ package iface
import (
"fmt"
log "github.com/sirupsen/logrus"
"github.com/pion/transport/v3"
"github.com/netbirdio/netbird/iface/netstack"
)
// NewWGIFace Creates a new WireGuard interface instance
func NewWGIFace(iFaceName string, address string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
func NewWGIFace(iFaceName string, address string, address6 string, wgPort int, wgPrivKey string, mtu int, transportNet transport.Net, args *MobileIFaceArguments) (*WGIface, error) {
if address6 != "" {
log.Errorf("Attempted to configure IPv6 address %s on unsupported operating system", address6)
}
wgAddress, err := parseWGAddress(address)
if err != nil {
return nil, err
@@ -37,3 +42,7 @@ func (w *WGIface) CreateOnAndroid([]string, string, []string) error {
func (w *WGIface) GetInterfaceGUIDString() (string, error) {
return w.tun.(*tunDevice).getInterfaceGUIDString()
}
func SupportsIPv6() bool {
return false
}

View File

@@ -12,6 +12,8 @@ type wgTunDevice interface {
Up() (*bind.UniversalUDPMuxDefault, error)
UpdateAddr(address WGAddress) error
WgAddress() WGAddress
UpdateAddr6(addr6 *WGAddress) error
WgAddress6() *WGAddress
DeviceName() string
Close() error
Wrapper() *DeviceWrapper // todo eliminate this function

View File

@@ -4,6 +4,7 @@
package iface
import (
"fmt"
"strings"
"github.com/pion/transport/v3"
@@ -98,6 +99,13 @@ func (t *wgTunDevice) UpdateAddr(addr WGAddress) error {
return nil
}
func (t *wgTunDevice) UpdateAddr6(addr6 *WGAddress) error {
if addr6 == nil {
return nil
}
return fmt.Errorf("IPv6 is not supported on this operating system")
}
func (t *wgTunDevice) Close() error {
if t.configurer != nil {
t.configurer.close()
@@ -127,6 +135,10 @@ func (t *wgTunDevice) WgAddress() WGAddress {
return t.address
}
func (t *wgTunDevice) WgAddress6() *WGAddress {
return nil
}
func (t *wgTunDevice) Wrapper() *DeviceWrapper {
return t.wrapper
}

View File

@@ -1,9 +1,9 @@
//go:build !ios
// +build !ios
package iface
import (
"fmt"
"os/exec"
"github.com/pion/transport/v3"
@@ -89,6 +89,13 @@ func (t *tunDevice) UpdateAddr(address WGAddress) error {
return t.assignAddr()
}
func (t *tunDevice) UpdateAddr6(address6 *WGAddress) error {
if address6 == nil {
return nil
}
return fmt.Errorf("IPv6 is not supported on this operating system")
}
func (t *tunDevice) Close() error {
if t.configurer != nil {
t.configurer.close()
@@ -109,6 +116,10 @@ func (t *tunDevice) WgAddress() WGAddress {
return t.address
}
func (t *tunDevice) WgAddress6() *WGAddress {
return nil
}
func (t *tunDevice) DeviceName() string {
return t.name
}
@@ -121,13 +132,19 @@ func (t *tunDevice) Wrapper() *DeviceWrapper {
func (t *tunDevice) assignAddr() error {
cmd := exec.Command("ifconfig", t.name, "inet", t.address.IP.String(), t.address.IP.String())
if out, err := cmd.CombinedOutput(); err != nil {
log.Infof(`adding address command "%v" failed with output %s and error: `, cmd.String(), out)
log.Errorf("adding address command '%v' failed with output: %s", cmd.String(), out)
return err
}
// dummy ipv6 so routing works
cmd = exec.Command("ifconfig", t.name, "inet6", "fe80::/64")
if out, err := cmd.CombinedOutput(); err != nil {
log.Debugf("adding address command '%v' failed with output: %s", cmd.String(), out)
}
routeCmd := exec.Command("route", "add", "-net", t.address.Network.String(), "-interface", t.name)
if out, err := routeCmd.CombinedOutput(); err != nil {
log.Printf(`adding route command "%v" failed with output %s and error: `, routeCmd.String(), out)
log.Errorf("adding route command '%v' failed with output: %s", routeCmd.String(), out)
return err
}
return nil

View File

@@ -4,6 +4,7 @@
package iface
import (
"fmt"
"os"
"github.com/pion/transport/v3"
@@ -123,11 +124,22 @@ func (t *tunDevice) WgAddress() WGAddress {
return t.address
}
func (t *tunDevice) WgAddress6() *WGAddress {
return nil
}
func (t *tunDevice) UpdateAddr(addr WGAddress) error {
// todo implement
return nil
}
func (t *tunDevice) UpdateAddr6(address6 *WGAddress) error {
if address6 == nil {
return nil
}
return fmt.Errorf("IPv6 is not supported on this operating system")
}
func (t *tunDevice) Wrapper() *DeviceWrapper {
return t.wrapper
}

View File

@@ -19,6 +19,7 @@ import (
type tunKernelDevice struct {
name string
address WGAddress
address6 *WGAddress
wgPort int
key string
mtu int
@@ -31,13 +32,14 @@ type tunKernelDevice struct {
udpMux *bind.UniversalUDPMuxDefault
}
func newTunDevice(name string, address WGAddress, wgPort int, key string, mtu int, transportNet transport.Net) wgTunDevice {
func newTunDevice(name string, address WGAddress, address6 *WGAddress, wgPort int, key string, mtu int, transportNet transport.Net) wgTunDevice {
ctx, cancel := context.WithCancel(context.Background())
return &tunKernelDevice{
ctx: ctx,
ctxCancel: cancel,
name: name,
address: address,
address6: address6,
wgPort: wgPort,
key: key,
mtu: mtu,
@@ -136,6 +138,11 @@ func (t *tunKernelDevice) UpdateAddr(address WGAddress) error {
return t.assignAddr()
}
func (t *tunKernelDevice) UpdateAddr6(address6 *WGAddress) error {
t.address6 = address6
return t.assignAddr()
}
func (t *tunKernelDevice) Close() error {
if t.link == nil {
return nil
@@ -168,6 +175,10 @@ func (t *tunKernelDevice) WgAddress() WGAddress {
return t.address
}
func (t *tunKernelDevice) WgAddress6() *WGAddress {
return t.address6
}
func (t *tunKernelDevice) DeviceName() string {
return t.name
}
@@ -203,6 +214,19 @@ func (t *tunKernelDevice) assignAddr() error {
} else if err != nil {
return err
}
// Configure the optional additional IPv6 address if available.
if t.address6 != nil {
log.Debugf("adding IPv6 address %s to interface: %s", t.address6.String(), t.name)
addr6, _ := netlink.ParseAddr(t.address6.String())
err = netlink.AddrAdd(link, addr6)
if os.IsExist(err) {
log.Infof("interface %s already has the address: %s", t.name, t.address.String())
} else if err != nil {
return err
}
}
// On linux, the link must be brought up
err = netlink.LinkSetUp(link)
return err

View File

@@ -91,6 +91,13 @@ func (t *tunNetstackDevice) UpdateAddr(WGAddress) error {
return nil
}
func (t *tunNetstackDevice) UpdateAddr6(address6 *WGAddress) error {
if address6 == nil {
return nil
}
return fmt.Errorf("IPv6 is not supported on this operating system")
}
func (t *tunNetstackDevice) Close() error {
if t.configurer != nil {
t.configurer.close()
@@ -110,6 +117,10 @@ func (t *tunNetstackDevice) WgAddress() WGAddress {
return t.address
}
func (t *tunNetstackDevice) WgAddress6() *WGAddress {
return nil
}
func (t *tunNetstackDevice) DeviceName() string {
return t.name
}

View File

@@ -98,6 +98,13 @@ func (t *tunUSPDevice) UpdateAddr(address WGAddress) error {
return t.assignAddr()
}
func (t *tunUSPDevice) UpdateAddr6(address6 *WGAddress) error {
if address6 == nil {
return nil
}
return fmt.Errorf("IPv6 is not supported on this operating system")
}
func (t *tunUSPDevice) Close() error {
if t.configurer != nil {
t.configurer.close()
@@ -117,6 +124,10 @@ func (t *tunUSPDevice) WgAddress() WGAddress {
return t.address
}
func (t *tunUSPDevice) WgAddress6() *WGAddress {
return nil
}
func (t *tunUSPDevice) DeviceName() string {
return t.name
}

View File

@@ -106,6 +106,13 @@ func (t *tunDevice) UpdateAddr(address WGAddress) error {
return t.assignAddr()
}
func (t *tunDevice) UpdateAddr6(address6 *WGAddress) error {
if address6 == nil {
return nil
}
return fmt.Errorf("IPv6 is not supported on this operating system")
}
func (t *tunDevice) Close() error {
if t.configurer != nil {
t.configurer.close()
@@ -126,6 +133,10 @@ func (t *tunDevice) WgAddress() WGAddress {
return t.address
}
func (t *tunDevice) WgAddress6() *WGAddress {
return nil
}
func (t *tunDevice) DeviceName() string {
return t.name
}

View File

@@ -5,6 +5,7 @@ package iface
import (
"fmt"
"net"
"strings"
"time"
log "github.com/sirupsen/logrus"
@@ -46,9 +47,13 @@ func (c *wgKernelConfigurer) configureInterface(privateKey string, port int) err
func (c *wgKernelConfigurer) updatePeer(peerKey string, allowedIps string, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {
// parse allowed ips
_, ipNet, err := net.ParseCIDR(allowedIps)
if err != nil {
return err
var allowedIpNets []net.IPNet
for _, allowedIp := range strings.Split(allowedIps, ",") {
_, ipNet, err := net.ParseCIDR(allowedIp)
allowedIpNets = append(allowedIpNets, *ipNet)
if err != nil {
return err
}
}
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
@@ -58,7 +63,7 @@ func (c *wgKernelConfigurer) updatePeer(peerKey string, allowedIps string, keepA
peer := wgtypes.PeerConfig{
PublicKey: peerKeyParsed,
ReplaceAllowedIPs: true,
AllowedIPs: []net.IPNet{*ipNet},
AllowedIPs: allowedIpNets,
PersistentKeepaliveInterval: &keepAlive,
Endpoint: endpoint,
PresharedKey: preSharedKey,

View File

@@ -62,10 +62,11 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
t.Fatal(err)
}
s := grpc.NewServer()
store, err := mgmt.NewStoreFromJson(config.Datadir, nil)
store, cleanUp, err := mgmt.NewTestStoreFromJson(config.Datadir)
if err != nil {
t.Fatal(err)
}
t.Cleanup(cleanUp)
peersUpdateManager := mgmt.NewPeersUpdateManager(nil)
eventStore := &activity.InMemoryEventStore{}
@@ -378,6 +379,7 @@ func Test_SystemMetaDataFromClient(t *testing.T) {
SysProductName: info.SystemProductName,
SysManufacturer: info.SystemManufacturer,
Environment: &mgmtProto.Environment{Cloud: info.Environment.Cloud, Platform: info.Environment.Platform},
Ipv6Supported: info.Ipv6Supported,
}
assert.Equal(t, ValidKey, actualValidKey)

View File

@@ -483,5 +483,6 @@ func infoToMetaData(info *system.Info) *proto.PeerSystemMeta {
Cloud: info.Environment.Cloud,
Platform: info.Environment.Platform,
},
Ipv6Supported: info.Ipv6Supported,
}
}

View File

@@ -7,10 +7,11 @@ import (
"os"
"path"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/util"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/util"
)
var shortDown = "Rollback SQLite store to JSON file store. Please make a backup of the SQLite file before running this command."
@@ -39,16 +40,16 @@ var downCmd = &cobra.Command{
return fmt.Errorf("%s already exists, couldn't continue the operation", fileStorePath)
}
sqlstore, err := server.NewSqliteStore(mgmtDataDir, nil)
sqlStore, err := server.NewSqliteStore(mgmtDataDir, nil)
if err != nil {
return fmt.Errorf("failed creating file store: %s: %v", mgmtDataDir, err)
}
sqliteStoreAccounts := len(sqlstore.GetAllAccounts())
sqliteStoreAccounts := len(sqlStore.GetAllAccounts())
log.Infof("%d account will be migrated from sqlite store %s to file store %s",
sqliteStoreAccounts, sqliteStorePath, fileStorePath)
store, err := server.NewFilestoreFromSqliteStore(sqlstore, mgmtDataDir, nil)
store, err := server.NewFilestoreFromSqliteStore(sqlStore, mgmtDataDir, nil)
if err != nil {
return fmt.Errorf("failed creating file store: %s: %v", mgmtDataDir, err)
}

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v4.24.3
// protoc v4.25.2
// source: management.proto
package proto
@@ -668,6 +668,7 @@ type PeerSystemMeta struct {
SysProductName string `protobuf:"bytes,13,opt,name=sysProductName,proto3" json:"sysProductName,omitempty"`
SysManufacturer string `protobuf:"bytes,14,opt,name=sysManufacturer,proto3" json:"sysManufacturer,omitempty"`
Environment *Environment `protobuf:"bytes,15,opt,name=environment,proto3" json:"environment,omitempty"`
Ipv6Supported bool `protobuf:"varint,16,opt,name=ipv6Supported,proto3" json:"ipv6Supported,omitempty"`
}
func (x *PeerSystemMeta) Reset() {
@@ -807,6 +808,13 @@ func (x *PeerSystemMeta) GetEnvironment() *Environment {
return nil
}
func (x *PeerSystemMeta) GetIpv6Supported() bool {
if x != nil {
return x.Ipv6Supported
}
return false
}
type LoginResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1172,6 +1180,8 @@ type PeerConfig struct {
SshConfig *SSHConfig `protobuf:"bytes,3,opt,name=sshConfig,proto3" json:"sshConfig,omitempty"`
// Peer fully qualified domain name
Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"`
// Peer's virtual IPv6 address within the Wiretrustee VPN (a Wireguard address config)
Address6 string `protobuf:"bytes,5,opt,name=address6,proto3" json:"address6,omitempty"`
}
func (x *PeerConfig) Reset() {
@@ -1234,6 +1244,13 @@ func (x *PeerConfig) GetFqdn() string {
return ""
}
func (x *PeerConfig) GetAddress6() string {
if x != nil {
return x.Address6
}
return ""
}
// NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections
type NetworkMap struct {
state protoimpl.MessageState
@@ -2254,6 +2271,7 @@ type FirewallRule struct {
Action FirewallRuleAction `protobuf:"varint,3,opt,name=Action,proto3,enum=management.FirewallRuleAction" json:"Action,omitempty"`
Protocol FirewallRuleProtocol `protobuf:"varint,4,opt,name=Protocol,proto3,enum=management.FirewallRuleProtocol" json:"Protocol,omitempty"`
Port string `protobuf:"bytes,5,opt,name=Port,proto3" json:"Port,omitempty"`
PeerIP6 string `protobuf:"bytes,6,opt,name=PeerIP6,proto3" json:"PeerIP6,omitempty"`
}
func (x *FirewallRule) Reset() {
@@ -2323,6 +2341,13 @@ func (x *FirewallRule) GetPort() string {
return ""
}
func (x *FirewallRule) GetPeerIP6() string {
if x != nil {
return x.PeerIP6
}
return ""
}
type NetworkAddress struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -2430,7 +2455,7 @@ var file_management_proto_rawDesc = []byte{
0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c,
0x6f, 0x75, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x75, 0x64,
0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0xa9, 0x04, 0x0a,
0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0xcf, 0x04, 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,
@@ -2465,260 +2490,266 @@ var file_management_proto_rawDesc = []byte{
0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0f, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x65, 0x6e, 0x76,
0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 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,
0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x69, 0x70, 0x76, 0x36,
0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52,
0x0d, 0x69, 0x70, 0x76, 0x36, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 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, 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,
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, 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,
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, 0x9d,
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, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x36, 0x18, 0x05,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x36, 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, 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,
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, 0x8a, 0x03, 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, 0x12, 0x18, 0x0a, 0x07,
0x50, 0x65, 0x65, 0x72, 0x49, 0x50, 0x36, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x50,
0x65, 0x65, 0x72, 0x49, 0x50, 0x36, 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, 0x12, 0x46, 0x0a,
0x04, 0x53, 0x79, 0x6e, 0x63, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73,
0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76,
0x65, 0x72, 0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48,
0x65, 0x61, 0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61,
0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a,
0x0a, 0x1a, 0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f,
0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d,
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70,
0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65,
0x64, 0x4d, 0x65, 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,
0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30,
0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65,
0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45,
0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61, 0x6c, 0x74,
0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65,
0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a, 0x47, 0x65,
0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 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,
0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x50, 0x4b, 0x43,
0x45, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c,
0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e,
0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00,
0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
}
var (

View File

@@ -117,6 +117,7 @@ message PeerSystemMeta {
string sysProductName = 13;
string sysManufacturer = 14;
Environment environment = 15;
bool ipv6Supported = 16;
}
message LoginResponse {
@@ -182,6 +183,8 @@ message PeerConfig {
SSHConfig sshConfig = 3;
// Peer fully qualified domain name
string fqdn = 4;
// Peer's virtual IPv6 address within the Wiretrustee VPN (a Wireguard address config)
string address6 = 5;
}
// NetworkMap represents a network state of the peer with the corresponding configuration parameters to establish peer-to-peer connections
@@ -349,6 +352,7 @@ message FirewallRule {
action Action = 3;
protocol Protocol = 4;
string Port = 5;
string PeerIP6 = 6;
enum direction {
IN = 0;

View File

@@ -132,6 +132,7 @@ type AccountManager interface {
GetValidatedPeers(account *Account) (map[string]struct{}, error)
SyncAndMarkPeer(peerPubKey string, realIP net.IP) (*nbpeer.Peer, *NetworkMap, error)
CancelPeerRoutines(peer *nbpeer.Peer) error
FindExistingPostureCheck(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error)
}
type DefaultAccountManager struct {
@@ -241,6 +242,11 @@ type Account struct {
Settings *Settings `gorm:"embedded;embeddedPrefix:settings_"`
}
// Subclass used in gorm to only load settings and not whole account
type AccountSettings struct {
Settings *Settings `gorm:"embedded;embeddedPrefix:settings_"`
}
type UserPermissions struct {
DashboardView string `json:"dashboard_view"`
}
@@ -274,7 +280,8 @@ func (a *Account) getRoutesToSync(peerID string, aclPeers []*nbpeer.Peer) []*rou
groupListMap := a.getPeerGroups(peerID)
for _, peer := range aclPeers {
activeRoutes, _ := a.getRoutingPeerRoutes(peer.ID)
groupFilteredRoutes := a.filterRoutesByGroups(activeRoutes, groupListMap)
addressFamilyFilteredRoutes := a.filterRoutesByIPv6Enabled(activeRoutes, a.GetPeer(peerID).IP6 != nil && peer.IP6 != nil)
groupFilteredRoutes := a.filterRoutesByGroups(addressFamilyFilteredRoutes, groupListMap)
filteredRoutes := a.filterRoutesFromPeersOfSameHAGroup(groupFilteredRoutes, peerRoutesMembership)
routes = append(routes, filteredRoutes...)
}
@@ -309,6 +316,20 @@ func (a *Account) filterRoutesByGroups(routes []*route.Route, groupListMap looku
return filteredRoutes
}
// Filters out IPv6 routes if the peer does not support them.
func (a *Account) filterRoutesByIPv6Enabled(routes []*route.Route, v6Supported bool) []*route.Route {
if v6Supported {
return routes
}
var filteredRoutes []*route.Route
for _, rt := range routes {
if rt.Network.Addr().Is4() {
filteredRoutes = append(filteredRoutes, rt)
}
}
return filteredRoutes
}
// getRoutingPeerRoutes returns the enabled and disabled lists of routes that the given routing peer serves
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
// If the given is not a routing peer, then the lists are empty.
@@ -342,6 +363,10 @@ func (a *Account) getRoutingPeerRoutes(peerID string) (enabledRoutes []*route.Ro
}
for _, r := range a.Routes {
// Skip IPv6 routes if IPv6 is currently not enabled.
if peer.IP6 == nil && r.NetworkType == route.IPv6Network {
continue
}
for _, groupID := range r.PeerGroups {
group := a.GetGroup(groupID)
if group == nil {
@@ -423,7 +448,7 @@ func (a *Account) GetPeerNetworkMap(peerID, dnsDomain string, validatedPeersMap
if dnsManagementStatus {
var zones []nbdns.CustomZone
peersCustomZone := getPeersCustomZone(a, dnsDomain)
peersCustomZone := getPeersCustomZone(a, dnsDomain, peer.IP6 != nil)
if peersCustomZone.Domain != "" {
zones = append(zones, peersCustomZone)
}
@@ -659,6 +684,17 @@ func (a *Account) getTakenIPs() []net.IP {
return takenIps
}
func (a *Account) getTakenIP6s() []net.IP {
var takenIps []net.IP
for _, existingPeer := range a.Peers {
if existingPeer.IP6 != nil {
takenIps = append(takenIps, *existingPeer.IP6)
}
}
return takenIps
}
func (a *Account) getPeerDNSLabels() lookupMap {
existingLabels := make(lookupMap)
for _, peer := range a.Peers {
@@ -1000,7 +1036,6 @@ func (am *DefaultAccountManager) UpdateAccountSettings(accountID, userID string,
}
updatedAccount := account.UpdateSettings(newSettings)
err = am.Store.SaveAccount(account)
if err != nil {
return nil, err
@@ -1768,6 +1803,8 @@ func (am *DefaultAccountManager) GetAccountFromToken(claims jwtclaims.Authorizat
//
// Existing user + Existing account + Existing domain reclassified Domain as private -> Nothing changes (index domain)
func (am *DefaultAccountManager) getAccountWithAuthorizationClaims(claims jwtclaims.AuthorizationClaims) (*Account, error) {
log.Tracef("getting account with authorization claims. User ID: \"%s\", Account ID: \"%s\", Domain: \"%s\", Domain Category: \"%s\"",
claims.UserId, claims.AccountId, claims.Domain, claims.DomainCategory)
if claims.UserId == "" {
return nil, fmt.Errorf("user ID is empty")
}
@@ -1788,8 +1825,10 @@ func (am *DefaultAccountManager) getAccountWithAuthorizationClaims(claims jwtcla
}
}
start := time.Now()
unlock := am.Store.AcquireGlobalLock()
defer unlock()
log.Debugf("Acquired global lock in %s for user %s", time.Since(start), claims.UserId)
// We checked if the domain has a primary account already
domainAccount, err := am.Store.GetAccountByPrivateDomain(claims.Domain)
@@ -1840,6 +1879,9 @@ func (am *DefaultAccountManager) getAccountWithAuthorizationClaims(claims jwtcla
func (am *DefaultAccountManager) SyncAndMarkPeer(peerPubKey string, realIP net.IP) (*nbpeer.Peer, *NetworkMap, error) {
accountID, err := am.Store.GetAccountIDByPeerPubKey(peerPubKey)
if err != nil {
if errStatus, ok := status.FromError(err); ok && errStatus.Type() == status.NotFound {
return nil, nil, status.Errorf(status.Unauthenticated, "peer not registered")
}
return nil, nil, err
}
@@ -1853,7 +1895,7 @@ func (am *DefaultAccountManager) SyncAndMarkPeer(peerPubKey string, realIP net.I
peer, netMap, err := am.SyncPeer(PeerSync{WireGuardPubKey: peerPubKey}, account)
if err != nil {
return nil, nil, mapError(err)
return nil, nil, err
}
err = am.MarkPeerConnected(peerPubKey, true, realIP, account)
@@ -1867,6 +1909,9 @@ func (am *DefaultAccountManager) SyncAndMarkPeer(peerPubKey string, realIP net.I
func (am *DefaultAccountManager) CancelPeerRoutines(peer *nbpeer.Peer) error {
accountID, err := am.Store.GetAccountIDByPeerPubKey(peer.Key)
if err != nil {
if errStatus, ok := status.FromError(err); ok && errStatus.Type() == status.NotFound {
return status.Errorf(status.Unauthenticated, "peer not registered")
}
return err
}
@@ -1951,6 +1996,10 @@ func (am *DefaultAccountManager) onPeersInvalidated(accountID string) {
am.updateAccountPeers(updatedAccount)
}
func (am *DefaultAccountManager) FindExistingPostureCheck(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error) {
return am.Store.GetPostureCheckByChecksDefinition(accountID, checks)
}
// addAllGroup to account object if it doesn't exist
func addAllGroup(account *Account) error {
if len(account.Groups) == 0 {
@@ -1995,7 +2044,7 @@ func addAllGroup(account *Account) error {
func newAccountWithId(accountID, userID, domain string) *Account {
log.Debugf("creating new account")
network := NewNetwork()
network := NewNetwork(true)
peers := make(map[string]*nbpeer.Peer)
users := make(map[string]*User)
routes := make(map[route.ID]*route.Route)

View File

@@ -48,8 +48,8 @@ func (MocIntegratedValidator) PreparePeer(accountID string, peer *nbpeer.Peer, p
return peer
}
func (MocIntegratedValidator) IsNotValidPeer(accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) (bool, bool) {
return false, false
func (MocIntegratedValidator) IsNotValidPeer(accountID string, peer *nbpeer.Peer, peersGroup []string, extraSettings *account.ExtraSettings) (bool, bool, error) {
return false, false, nil
}
func (MocIntegratedValidator) PeerDeleted(_, _ string) error {
@@ -69,14 +69,15 @@ func verifyCanAddPeerToAccount(t *testing.T, manager AccountManager, account *Ac
Key: "BhRPtynAAYRDy08+q4HTMsos8fs4plTP4NOSh7C1ry8=",
Name: "test-host@netbird.io",
Meta: nbpeer.PeerSystemMeta{
Hostname: "test-host@netbird.io",
GoOS: "linux",
Kernel: "Linux",
Core: "21.04",
Platform: "x86_64",
OS: "Ubuntu",
WtVersion: "development",
UIVersion: "development",
Hostname: "test-host@netbird.io",
GoOS: "linux",
Kernel: "Linux",
Core: "21.04",
Platform: "x86_64",
OS: "Ubuntu",
WtVersion: "development",
UIVersion: "development",
Ipv6Supported: false,
},
}
@@ -964,78 +965,136 @@ func TestAccountManager_DeleteAccount(t *testing.T) {
}
func TestAccountManager_AddPeer(t *testing.T) {
manager, err := createManager(t)
if err != nil {
t.Fatal(err)
return
testCases := []struct {
name string
peerIPv6Supported bool
allGroupIPv6Enabled bool
}{
{
name: "Peer and Group IPv6 enabled",
peerIPv6Supported: true,
allGroupIPv6Enabled: true,
},
{
name: "Peer IPv6 enabled, Group IPv6 disabled",
peerIPv6Supported: true,
allGroupIPv6Enabled: false,
},
{
name: "Peer IPv6 disabled, Group IPv6 enabled",
peerIPv6Supported: false,
allGroupIPv6Enabled: true,
},
{
name: "Peer and Group IPv6 disabled",
peerIPv6Supported: false,
allGroupIPv6Enabled: false,
},
}
userID := "testingUser"
account, err := createAccount(manager, "test_account", userID, "netbird.cloud")
if err != nil {
t.Fatal(err)
for _, c := range testCases {
t.Run(c.name, func(t *testing.T) {
manager, err := createManager(t)
if err != nil {
t.Fatal(err)
return
}
userID := "testingUser"
account, err := createAccount(manager, "test_account", userID, "netbird.cloud")
if err != nil {
t.Fatal(err)
}
serial := account.Network.CurrentSerial() // should be 0
setupKey, err := manager.CreateSetupKey(account.Id, "test-key", SetupKeyReusable, time.Hour, nil, 999, userID, false)
if err != nil {
t.Fatal("error creating setup key")
return
}
if account.Network.Serial != 0 {
t.Errorf("expecting account network to have an initial Serial=0")
return
}
account, err = manager.Store.GetAccount(account.Id)
if err != nil {
t.Fatal(err)
return
}
if c.allGroupIPv6Enabled {
unlock := manager.Store.AcquireAccountWriteLock(account.Id)
groupAll, err := account.GetGroupAll()
if err != nil {
t.Fatal(err)
}
groupAll.IPv6Enabled = true
err = manager.Store.SaveAccount(account)
if err != nil {
t.Fatal(err)
}
unlock()
}
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
t.Fatal(err)
return
}
expectedPeerKey := key.PublicKey().String()
expectedSetupKey := setupKey.Key
peer, _, err := manager.AddPeer(setupKey.Key, "", &nbpeer.Peer{
Key: expectedPeerKey,
Meta: nbpeer.PeerSystemMeta{Hostname: expectedPeerKey, Ipv6Supported: c.peerIPv6Supported},
})
if err != nil {
t.Errorf("expecting peer to be added, got failure %v", err)
return
}
account, err = manager.Store.GetAccount(account.Id)
if err != nil {
t.Fatal(err)
return
}
if peer.Key != expectedPeerKey {
t.Errorf("expecting just added peer to have key = %s, got %s", expectedPeerKey, peer.Key)
}
if !account.Network.Net.Contains(peer.IP) {
t.Errorf("expecting just added peer's IP %s to be in a network range %s", peer.IP.String(), account.Network.Net.String())
}
if peer.SetupKey != expectedSetupKey {
t.Errorf("expecting just added peer to have SetupKey = %s, got %s", expectedSetupKey, peer.SetupKey)
}
if account.Network.CurrentSerial() != 1 {
t.Errorf("expecting Network Serial=%d to be incremented by 1 and be equal to %d when adding new peer to account", serial, account.Network.CurrentSerial())
}
ev := getEvent(t, account.Id, manager, activity.PeerAddedWithSetupKey)
assert.NotNil(t, ev)
assert.Equal(t, account.Id, ev.AccountID)
assert.Equal(t, peer.Name, ev.Meta["name"])
assert.Equal(t, peer.FQDN(account.Domain), ev.Meta["fqdn"])
assert.Equal(t, setupKey.Id, ev.InitiatorID)
assert.Equal(t, peer.ID, ev.TargetID)
assert.Equal(t, peer.IP.String(), fmt.Sprint(ev.Meta["ip"]))
assert.Equal(t, peer.V6Setting, nbpeer.V6Auto)
if c.peerIPv6Supported && c.allGroupIPv6Enabled {
assert.NotNil(t, peer.IP6)
} else {
assert.Nil(t, peer.IP6)
}
})
}
serial := account.Network.CurrentSerial() // should be 0
setupKey, err := manager.CreateSetupKey(account.Id, "test-key", SetupKeyReusable, time.Hour, nil, 999, userID, false)
if err != nil {
t.Fatal("error creating setup key")
return
}
if account.Network.Serial != 0 {
t.Errorf("expecting account network to have an initial Serial=0")
return
}
key, err := wgtypes.GeneratePrivateKey()
if err != nil {
t.Fatal(err)
return
}
expectedPeerKey := key.PublicKey().String()
expectedSetupKey := setupKey.Key
peer, _, err := manager.AddPeer(setupKey.Key, "", &nbpeer.Peer{
Key: expectedPeerKey,
Meta: nbpeer.PeerSystemMeta{Hostname: expectedPeerKey},
})
if err != nil {
t.Errorf("expecting peer to be added, got failure %v", err)
return
}
account, err = manager.Store.GetAccount(account.Id)
if err != nil {
t.Fatal(err)
return
}
if peer.Key != expectedPeerKey {
t.Errorf("expecting just added peer to have key = %s, got %s", expectedPeerKey, peer.Key)
}
if !account.Network.Net.Contains(peer.IP) {
t.Errorf("expecting just added peer's IP %s to be in a network range %s", peer.IP.String(), account.Network.Net.String())
}
if peer.SetupKey != expectedSetupKey {
t.Errorf("expecting just added peer to have SetupKey = %s, got %s", expectedSetupKey, peer.SetupKey)
}
if account.Network.CurrentSerial() != 1 {
t.Errorf("expecting Network Serial=%d to be incremented by 1 and be equal to %d when adding new peer to account", serial, account.Network.CurrentSerial())
}
ev := getEvent(t, account.Id, manager, activity.PeerAddedWithSetupKey)
assert.NotNil(t, ev)
assert.Equal(t, account.Id, ev.AccountID)
assert.Equal(t, peer.Name, ev.Meta["name"])
assert.Equal(t, peer.FQDN(account.Domain), ev.Meta["fqdn"])
assert.Equal(t, setupKey.Id, ev.InitiatorID)
assert.Equal(t, peer.ID, ev.TargetID)
assert.Equal(t, peer.IP.String(), fmt.Sprint(ev.Meta["ip"]))
}
func TestAccountManager_AddPeerWithUserID(t *testing.T) {
@@ -1294,6 +1353,7 @@ func TestAccountManager_DeletePeer(t *testing.T) {
t.Fatal(err)
return
}
userID := "account_creator"
account, err := createAccount(manager, "test_account", userID, "netbird.cloud")
if err != nil {
@@ -1454,9 +1514,22 @@ func TestAccount_GetRoutesToSync(t *testing.T) {
if err != nil {
t.Fatal(err)
}
_, prefix3, err := route.ParseNetwork("2001:db8:1234:5678::/64")
if err != nil {
t.Fatal(err)
}
_, prefix4, err := route.ParseNetwork("2001:db8:1234:6789::/64")
if err != nil {
t.Fatal(err)
}
peer2IP6 := net.ParseIP("2001:db8:abcd:1234::12")
account := &Account{
Peers: map[string]*nbpeer.Peer{
"peer-1": {Key: "peer-1", Meta: nbpeer.PeerSystemMeta{GoOS: "linux"}}, "peer-2": {Key: "peer-2", Meta: nbpeer.PeerSystemMeta{GoOS: "linux"}}, "peer-3": {Key: "peer-1", Meta: nbpeer.PeerSystemMeta{GoOS: "linux"}},
"peer-1": {
ID: "peer-1", Key: "peer-1", Meta: nbpeer.PeerSystemMeta{GoOS: "linux"},
},
"peer-2": {ID: "peer-2", Key: "peer-2", Meta: nbpeer.PeerSystemMeta{GoOS: "linux", Ipv6Supported: true}, IP6: &peer2IP6},
"peer-3": {ID: "peer-3", Key: "peer-1", Meta: nbpeer.PeerSystemMeta{GoOS: "linux"}},
},
Groups: map[string]*group.Group{"group1": {ID: "group1", Peers: []string{"peer-1", "peer-2"}}},
Routes: map[route.ID]*route.Route{
@@ -1466,7 +1539,7 @@ func TestAccount_GetRoutesToSync(t *testing.T) {
NetID: "network-1",
Description: "network-1",
Peer: "peer-1",
NetworkType: 0,
NetworkType: route.IPv4Network,
Masquerade: false,
Metric: 999,
Enabled: true,
@@ -1478,7 +1551,7 @@ func TestAccount_GetRoutesToSync(t *testing.T) {
NetID: "network-2",
Description: "network-2",
Peer: "peer-2",
NetworkType: 0,
NetworkType: route.IPv4Network,
Masquerade: false,
Metric: 999,
Enabled: true,
@@ -1490,7 +1563,31 @@ func TestAccount_GetRoutesToSync(t *testing.T) {
NetID: "network-1",
Description: "network-1",
Peer: "peer-2",
NetworkType: 0,
NetworkType: route.IPv4Network,
Masquerade: false,
Metric: 999,
Enabled: true,
Groups: []string{"group1"},
},
"route-4": {
ID: "route-4",
Network: prefix3,
NetID: "network-3",
Description: "network-3",
Peer: "peer-1",
NetworkType: route.IPv6Network,
Masquerade: false,
Metric: 999,
Enabled: true,
Groups: []string{"group1"},
},
"route-5": {
ID: "route-5",
Network: prefix4,
NetID: "network-4",
Description: "network-4",
Peer: "peer-2",
NetworkType: route.IPv6Network,
Masquerade: false,
Metric: 999,
Enabled: true,
@@ -1499,17 +1596,29 @@ func TestAccount_GetRoutesToSync(t *testing.T) {
},
}
routes := account.getRoutesToSync("peer-2", []*nbpeer.Peer{{Key: "peer-1"}, {Key: "peer-3"}})
routes := account.getRoutesToSync("peer-1", []*nbpeer.Peer{{ID: "peer-2", Key: "peer-2"}, {ID: "peer-3", Key: "peer-3"}})
assert.Len(t, routes, 2)
routeIDs := make(map[route.ID]struct{}, 2)
for _, r := range routes {
routeIDs[r.ID] = struct{}{}
}
assert.Contains(t, routeIDs, route.ID("route-1"))
assert.Contains(t, routeIDs, route.ID("route-2"))
routes = account.getRoutesToSync("peer-2", []*nbpeer.Peer{{ID: "peer-1", Key: "peer-1"}, {ID: "peer-3", Key: "peer-3"}})
assert.Len(t, routes, 3)
routeIDs = make(map[route.ID]struct{}, 2)
for _, r := range routes {
routeIDs[r.ID] = struct{}{}
}
assert.Contains(t, routeIDs, route.ID("route-2"))
assert.Contains(t, routeIDs, route.ID("route-3"))
assert.Contains(t, routeIDs, route.ID("route-5"))
emptyRoutes := account.getRoutesToSync("peer-3", []*nbpeer.Peer{{Key: "peer-1"}, {Key: "peer-2"}})
emptyRoutes := account.getRoutesToSync("peer-3", []*nbpeer.Peer{{ID: "peer-1", Key: "peer-1"}, {ID: "peer-2", Key: "peer-2"}})
assert.Len(t, emptyRoutes, 0)
}
@@ -1655,6 +1764,7 @@ func TestDefaultAccountManager_DefaultAccountSettings(t *testing.T) {
func TestDefaultAccountManager_UpdatePeer_PeerLoginExpiration(t *testing.T) {
manager, err := createManager(t)
require.NoError(t, err, "unable to create account manager")
_, err = manager.GetAccountByUserOrAccountID(userID, "", "")
require.NoError(t, err, "unable to create an account")
@@ -1707,6 +1817,7 @@ func TestDefaultAccountManager_UpdatePeer_PeerLoginExpiration(t *testing.T) {
func TestDefaultAccountManager_MarkPeerConnected_PeerLoginExpiration(t *testing.T) {
manager, err := createManager(t)
require.NoError(t, err, "unable to create account manager")
account, err := manager.GetAccountByUserOrAccountID(userID, "", "")
require.NoError(t, err, "unable to create an account")
@@ -1750,6 +1861,7 @@ func TestDefaultAccountManager_MarkPeerConnected_PeerLoginExpiration(t *testing.
func TestDefaultAccountManager_UpdateAccountSettings_PeerLoginExpiration(t *testing.T) {
manager, err := createManager(t)
require.NoError(t, err, "unable to create account manager")
_, err = manager.GetAccountByUserOrAccountID(userID, "", "")
require.NoError(t, err, "unable to create an account")
@@ -2267,21 +2379,29 @@ func TestAccount_UserGroupsRemoveFromPeers(t *testing.T) {
func createManager(t *testing.T) (*DefaultAccountManager, error) {
t.Helper()
store, err := createStore(t)
if err != nil {
return nil, err
}
eventStore := &activity.InMemoryEventStore{}
return BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{})
manager, err := BuildManager(store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{})
if err != nil {
return nil, err
}
return manager, nil
}
func createStore(t *testing.T) (Store, error) {
t.Helper()
dataDir := t.TempDir()
store, err := NewStoreFromJson(dataDir, nil)
store, cleanUp, err := NewTestStoreFromJson(dataDir)
if err != nil {
return nil, err
}
t.Cleanup(cleanUp)
return store, nil
}

View File

@@ -139,6 +139,14 @@ const (
PostureCheckUpdated Activity = 61
// PostureCheckDeleted indicates that the user deleted a posture check
PostureCheckDeleted Activity = 62
// PeerIPv6Enabled indicates that a user enabled IPv6 for a peer
PeerIPv6Enabled Activity = 63
// PeerIPv6Disabled indicates that a user disabled IPv6 for a peer
PeerIPv6Disabled Activity = 64
// PeerIPv6InheritEnabled indicates that IPv6 was enabled for a peer due to a change in group memberships.
PeerIPv6InheritEnabled Activity = 65
// PeerIPv6InheritDisabled indicates that IPv6 was disabled for a peer due to a change in group memberships.
PeerIPv6InheritDisabled Activity = 66
)
var activityMap = map[Activity]Code{
@@ -205,6 +213,10 @@ var activityMap = map[Activity]Code{
PostureCheckCreated: {"Posture check created", "posture.check.created"},
PostureCheckUpdated: {"Posture check updated", "posture.check.updated"},
PostureCheckDeleted: {"Posture check deleted", "posture.check.deleted"},
PeerIPv6Enabled: {"Peer IPv6 enabled by user", "peer.ipv6.manual_enable"},
PeerIPv6Disabled: {"Peer IPv6 disabled by user", "peer.ipv6.manual_disable"},
PeerIPv6InheritDisabled: {"Peer IPv6 disabled due to change in group settings or membership", "peer.ipv6.inherit_disable"},
PeerIPv6InheritEnabled: {"Peer IPv6 enabled due to change in group settings or membership", "peer.ipv6.inherit_enable"},
}
// StringCode returns a string code of the activity

View File

@@ -149,7 +149,7 @@ func toProtocolDNSConfig(update nbdns.Config) *proto.DNSConfig {
return protoUpdate
}
func getPeersCustomZone(account *Account, dnsDomain string) nbdns.CustomZone {
func getPeersCustomZone(account *Account, dnsDomain string, enableIPv6 bool) nbdns.CustomZone {
if dnsDomain == "" {
log.Errorf("no dns domain is set, returning empty zone")
return nbdns.CustomZone{}
@@ -172,6 +172,16 @@ func getPeersCustomZone(account *Account, dnsDomain string) nbdns.CustomZone {
TTL: defaultTTL,
RData: peer.IP.String(),
})
if peer.IP6 != nil && enableIPv6 {
customZone.Records = append(customZone.Records, nbdns.SimpleRecord{
Name: dns.Fqdn(peer.DNSLabel + "." + dnsDomain),
Type: int(dns.TypeAAAA),
Class: nbdns.DefaultClass,
TTL: defaultTTL,
RData: peer.IP6.String(),
})
}
}
return customZone
@@ -179,6 +189,7 @@ func getPeersCustomZone(account *Account, dnsDomain string) nbdns.CustomZone {
func getPeerNSGroups(account *Account, peerID string) []*nbdns.NameServerGroup {
groupList := account.getPeerGroups(peerID)
peer := account.GetPeer(peerID)
var peerNSGroups []*nbdns.NameServerGroup
@@ -189,8 +200,18 @@ func getPeerNSGroups(account *Account, peerID string) []*nbdns.NameServerGroup {
for _, gID := range nsGroup.Groups {
_, found := groupList[gID]
if found {
if !peerIsNameserver(account.GetPeer(peerID), nsGroup) {
peerNSGroups = append(peerNSGroups, nsGroup.Copy())
if !peerIsNameserver(peer, nsGroup) {
filteredNsGroup := nsGroup.Copy()
var newNameserverList []nbdns.NameServer
for _, nameserver := range filteredNsGroup.NameServers {
if nameserver.IP.Is4() || peer.IP6 != nil {
newNameserverList = append(newNameserverList, nameserver)
}
}
if len(newNameserverList) > 0 {
filteredNsGroup.NameServers = newNameserverList
peerNSGroups = append(peerNSGroups, filteredNsGroup)
}
break
}
}

View File

@@ -22,6 +22,7 @@ const (
dnsAdminUserID = "testingAdminUser"
dnsRegularUserID = "testingRegularUser"
dnsNSGroup1 = "ns1"
dnsNSGroup2 = "ns2"
)
func TestGetDNSSettings(t *testing.T) {
@@ -32,7 +33,7 @@ func TestGetDNSSettings(t *testing.T) {
account, err := initTestDNSAccount(t, am)
if err != nil {
t.Error("failed to init testing account")
t.Fatal("failed to init testing account")
}
dnsSettings, err := am.GetDNSSettings(account.Id, dnsAdminUserID)
@@ -184,7 +185,7 @@ func TestGetNetworkMap_DNSConfigSync(t *testing.T) {
require.NoError(t, err)
require.Len(t, peer2AccountDNSConfig.DNSConfig.CustomZones, 1, "DNS config should have one custom zone for peers not in the disabled group")
require.True(t, peer2AccountDNSConfig.DNSConfig.ServiceEnable, "DNS config should have DNS service enabled for peers not in the disabled group")
require.Len(t, peer2AccountDNSConfig.DNSConfig.NameServerGroups, 1, "updated DNS config should have 1 nameserver groups since peer 2 is part of the group All")
require.Len(t, peer2AccountDNSConfig.DNSConfig.NameServerGroups, 2, "updated DNS config should have 2 nameserver groups since peer 2 is part of the group All and supports IPv6")
}
func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
@@ -200,10 +201,11 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
func createDNSStore(t *testing.T) (Store, error) {
t.Helper()
dataDir := t.TempDir()
store, err := NewStoreFromJson(dataDir, nil)
store, cleanUp, err := NewTestStoreFromJson(dataDir)
if err != nil {
return nil, err
}
t.Cleanup(cleanUp)
return store, nil
}
@@ -214,14 +216,15 @@ func initTestDNSAccount(t *testing.T, am *DefaultAccountManager) (*Account, erro
Key: dnsPeer1Key,
Name: "test-host1@netbird.io",
Meta: nbpeer.PeerSystemMeta{
Hostname: "test-host1@netbird.io",
GoOS: "linux",
Kernel: "Linux",
Core: "21.04",
Platform: "x86_64",
OS: "Ubuntu",
WtVersion: "development",
UIVersion: "development",
Hostname: "test-host1@netbird.io",
GoOS: "linux",
Kernel: "Linux",
Core: "21.04",
Platform: "x86_64",
OS: "Ubuntu",
WtVersion: "development",
UIVersion: "development",
Ipv6Supported: false,
},
DNSLabel: dnsPeer1Key,
}
@@ -229,16 +232,18 @@ func initTestDNSAccount(t *testing.T, am *DefaultAccountManager) (*Account, erro
Key: dnsPeer2Key,
Name: "test-host2@netbird.io",
Meta: nbpeer.PeerSystemMeta{
Hostname: "test-host2@netbird.io",
GoOS: "linux",
Kernel: "Linux",
Core: "21.04",
Platform: "x86_64",
OS: "Ubuntu",
WtVersion: "development",
UIVersion: "development",
Hostname: "test-host2@netbird.io",
GoOS: "linux",
Kernel: "Linux",
Core: "21.04",
Platform: "x86_64",
OS: "Ubuntu",
WtVersion: "development",
UIVersion: "development",
Ipv6Supported: true,
},
DNSLabel: dnsPeer2Key,
V6Setting: nbpeer.V6Enabled,
DNSLabel: dnsPeer2Key,
}
domain := "example.com"
@@ -311,6 +316,20 @@ func initTestDNSAccount(t *testing.T, am *DefaultAccountManager) (*Account, erro
Groups: []string{allGroup.ID},
}
account.NameServerGroups[dnsNSGroup2] = &dns.NameServerGroup{
ID: dnsNSGroup2,
Name: "ns-group-2",
NameServers: []dns.NameServer{{
IP: netip.MustParseAddr("2001:4860:4860:0:0:0:0:8888"), // Google DNS
NSType: dns.UDPNameServerType,
Port: dns.DefaultDNSPort,
}},
Primary: false,
Domains: []string{"example.com"},
Enabled: true,
Groups: []string{allGroup.ID},
}
err = am.Store.SaveAccount(account)
if err != nil {
return nil, err

View File

@@ -12,6 +12,7 @@ import (
nbgroup "github.com/netbirdio/netbird/management/server/group"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/posture"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -57,18 +58,18 @@ func NewFileStore(dataDir string, metrics telemetry.AppMetrics) (*FileStore, err
}
// NewFilestoreFromSqliteStore restores a store from Sqlite and stores to Filestore json in the file located in datadir
func NewFilestoreFromSqliteStore(sqlitestore *SqliteStore, dataDir string, metrics telemetry.AppMetrics) (*FileStore, error) {
func NewFilestoreFromSqliteStore(sqlStore *SqlStore, dataDir string, metrics telemetry.AppMetrics) (*FileStore, error) {
store, err := NewFileStore(dataDir, metrics)
if err != nil {
return nil, err
}
err = store.SaveInstallationID(sqlitestore.GetInstallationID())
err = store.SaveInstallationID(sqlStore.GetInstallationID())
if err != nil {
return nil, err
}
for _, account := range sqlitestore.GetAllAccounts() {
for _, account := range sqlStore.GetAllAccounts() {
store.Accounts[account.Id] = account
}
@@ -508,7 +509,7 @@ func (s *FileStore) GetAccountByUser(userID string) (*Account, error) {
accountID, ok := s.UserID2AccountID[userID]
if !ok {
return nil, status.Errorf(status.NotFound, "account not found")
return nil, status.NewUserNotFoundError(userID)
}
account, err := s.getAccount(accountID)
@@ -539,7 +540,7 @@ func (s *FileStore) GetAccountByPeerID(peerID string) (*Account, error) {
if _, ok := account.Peers[peerID]; !ok {
delete(s.PeerID2AccountID, peerID)
log.Warnf("removed stale peerID %s to accountID %s index", peerID, accountID)
return nil, status.Errorf(status.NotFound, "provided peer doesn't exists %s", peerID)
return nil, status.NewPeerNotFoundError(peerID)
}
return account.Copy(), nil
@@ -552,7 +553,7 @@ func (s *FileStore) GetAccountByPeerPubKey(peerKey string) (*Account, error) {
accountID, ok := s.PeerKeyID2AccountID[peerKey]
if !ok {
return nil, status.Errorf(status.NotFound, "provided peer key doesn't exists %s", peerKey)
return nil, status.NewPeerNotFoundError(peerKey)
}
account, err := s.getAccount(accountID)
@@ -572,7 +573,7 @@ func (s *FileStore) GetAccountByPeerPubKey(peerKey string) (*Account, error) {
if stale {
delete(s.PeerKeyID2AccountID, peerKey)
log.Warnf("removed stale peerKey %s to accountID %s index", peerKey, accountID)
return nil, status.Errorf(status.NotFound, "provided peer doesn't exists %s", peerKey)
return nil, status.NewPeerNotFoundError(peerKey)
}
return account.Copy(), nil
@@ -584,12 +585,71 @@ func (s *FileStore) GetAccountIDByPeerPubKey(peerKey string) (string, error) {
accountID, ok := s.PeerKeyID2AccountID[peerKey]
if !ok {
return "", status.Errorf(status.NotFound, "provided peer key doesn't exists %s", peerKey)
return "", status.NewPeerNotFoundError(peerKey)
}
return accountID, nil
}
func (s *FileStore) GetAccountIDByUserID(userID string) (string, error) {
s.mux.Lock()
defer s.mux.Unlock()
accountID, ok := s.UserID2AccountID[userID]
if !ok {
return "", status.NewUserNotFoundError(userID)
}
return accountID, nil
}
func (s *FileStore) GetAccountIDBySetupKey(setupKey string) (string, error) {
s.mux.Lock()
defer s.mux.Unlock()
accountID, ok := s.SetupKeyID2AccountID[strings.ToUpper(setupKey)]
if !ok {
return "", status.Errorf(status.NotFound, "account not found: provided setup key doesn't exists")
}
return accountID, nil
}
func (s *FileStore) GetPeerByPeerPubKey(peerKey string) (*nbpeer.Peer, error) {
s.mux.Lock()
defer s.mux.Unlock()
accountID, ok := s.PeerKeyID2AccountID[peerKey]
if !ok {
return nil, status.NewPeerNotFoundError(peerKey)
}
account, err := s.getAccount(accountID)
if err != nil {
return nil, err
}
for _, peer := range account.Peers {
if peer.Key == peerKey {
return peer.Copy(), nil
}
}
return nil, status.NewPeerNotFoundError(peerKey)
}
func (s *FileStore) GetAccountSettings(accountID string) (*Settings, error) {
s.mux.Lock()
defer s.mux.Unlock()
account, err := s.getAccount(accountID)
if err != nil {
return nil, err
}
return account.Settings.Copy(), nil
}
// GetInstallationID returns the installation ID from the store
func (s *FileStore) GetInstallationID() string {
return s.InstallationID
@@ -667,6 +727,10 @@ func (s *FileStore) SaveUserLastLogin(accountID, userID string, lastLogin time.T
return nil
}
func (s *FileStore) GetPostureCheckByChecksDefinition(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error) {
return nil, status.Errorf(status.Internal, "GetPostureCheckByChecksDefinition is not implemented")
}
// Close the FileStore persisting data to disk
func (s *FileStore) Close() error {
s.mux.Lock()

View File

@@ -59,6 +59,7 @@ func TestStalePeerIndices(t *testing.T) {
func TestNewStore(t *testing.T) {
store := newStore(t)
defer store.Close()
if store.Accounts == nil || len(store.Accounts) != 0 {
t.Errorf("expected to create a new empty Accounts map when creating a new FileStore")
@@ -87,6 +88,7 @@ func TestNewStore(t *testing.T) {
func TestSaveAccount(t *testing.T) {
store := newStore(t)
defer store.Close()
account := newAccountWithId("account_id", "testuser", "")
setupKey := GenerateDefaultSetupKey()
@@ -135,6 +137,8 @@ func TestDeleteAccount(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer store.Close()
var account *Account
for _, a := range store.Accounts {
account = a
@@ -179,6 +183,7 @@ func TestDeleteAccount(t *testing.T) {
func TestStore(t *testing.T) {
store := newStore(t)
defer store.Close()
account := newAccountWithId("account_id", "testuser", "")
account.Peers["testpeer"] = &nbpeer.Peer{
@@ -436,6 +441,7 @@ func TestFileStore_GetTokenIDByHashedToken(t *testing.T) {
func TestFileStore_DeleteHashedPAT2TokenIDIndex(t *testing.T) {
store := newStore(t)
defer store.Close()
store.HashedPAT2TokenID["someHashedToken"] = "someTokenId"
err := store.DeleteHashedPAT2TokenIDIndex("someHashedToken")

View File

@@ -149,6 +149,35 @@ func (am *DefaultAccountManager) SaveGroup(accountID, userID string, newGroup *n
oldGroup, exists := account.Groups[newGroup.ID]
account.Groups[newGroup.ID] = newGroup
// Determine peer difference for group.
addedPeers := make([]string, 0)
removedPeers := make([]string, 0)
if exists {
addedPeers = difference(newGroup.Peers, oldGroup.Peers)
removedPeers = difference(oldGroup.Peers, newGroup.Peers)
} else {
addedPeers = append(addedPeers, newGroup.Peers...)
}
// Need to check whether IPv6 status has changed for all potentially affected peers.
peersToUpdate := make([]string, 0)
// If group previously had IPv6 enabled, need to check all old peers for changes in IPv6 status.
if exists && oldGroup.IPv6Enabled {
peersToUpdate = removedPeers
}
// If group IPv6 status changed, need to check all current peers, if it did not, but IPv6 is enabled, only check
// added peers, otherwise check no peers (as group can not affect IPv6 state).
if exists && oldGroup.IPv6Enabled != newGroup.IPv6Enabled {
peersToUpdate = append(peersToUpdate, newGroup.Peers...)
} else if newGroup.IPv6Enabled {
peersToUpdate = append(peersToUpdate, addedPeers...)
}
_, err = am.updatePeerIPv6Status(account, userID, newGroup, peersToUpdate)
if err != nil {
return err
}
account.Network.IncSerial()
if err = am.Store.SaveAccount(account); err != nil {
return err
@@ -158,13 +187,7 @@ func (am *DefaultAccountManager) SaveGroup(accountID, userID string, newGroup *n
// the following snippet tracks the activity and stores the group events in the event store.
// It has to happen after all the operations have been successfully performed.
addedPeers := make([]string, 0)
removedPeers := make([]string, 0)
if exists {
addedPeers = difference(newGroup.Peers, oldGroup.Peers)
removedPeers = difference(oldGroup.Peers, newGroup.Peers)
} else {
addedPeers = append(addedPeers, newGroup.Peers...)
if !exists {
am.StoreEvent(userID, newGroup.ID, accountID, activity.GroupCreated, newGroup.EventMeta())
}
@@ -245,6 +268,11 @@ func (am *DefaultAccountManager) DeleteGroup(accountId, userId, groupID string)
return &GroupLinkError{"route", string(r.NetID)}
}
}
for _, g := range r.PeerGroups {
if g == groupID {
return &GroupLinkError{"route", string(r.NetID)}
}
}
}
// check DNS links
@@ -309,6 +337,14 @@ func (am *DefaultAccountManager) DeleteGroup(accountId, userId, groupID string)
delete(account.Groups, groupID)
// Update IPv6 status of all group members if necessary.
if g.IPv6Enabled {
_, err = am.updatePeerIPv6Status(account, userId, g, g.Peers)
if err != nil {
return err
}
}
account.Network.IncSerial()
if err = am.Store.SaveAccount(account); err != nil {
return err
@@ -340,6 +376,7 @@ func (am *DefaultAccountManager) ListGroups(accountID string) ([]*nbgroup.Group,
}
// GroupAddPeer appends peer to the group
// TODO Question for devs: Is this method dead code? I can't seem to find any usages outside of tests...
func (am *DefaultAccountManager) GroupAddPeer(accountID, groupID, peerID string) error {
unlock := am.Store.AcquireAccountWriteLock(accountID)
defer unlock()
@@ -363,6 +400,14 @@ func (am *DefaultAccountManager) GroupAddPeer(accountID, groupID, peerID string)
}
if add {
group.Peers = append(group.Peers, peerID)
if group.IPv6Enabled {
// Update IPv6 status of added group member.
_, err = am.updatePeerIPv6Status(account, "", group, []string{peerID})
if err != nil {
return err
}
}
}
account.Network.IncSerial()
@@ -376,6 +421,7 @@ func (am *DefaultAccountManager) GroupAddPeer(accountID, groupID, peerID string)
}
// GroupDeletePeer removes peer from the group
// TODO Question for devs: Same as above, this seems like dead code
func (am *DefaultAccountManager) GroupDeletePeer(accountID, groupID, peerID string) error {
unlock := am.Store.AcquireAccountWriteLock(accountID)
defer unlock()
@@ -394,6 +440,15 @@ func (am *DefaultAccountManager) GroupDeletePeer(accountID, groupID, peerID stri
for i, itemID := range group.Peers {
if itemID == peerID {
group.Peers = append(group.Peers[:i], group.Peers[i+1:]...)
if group.IPv6Enabled {
// Update IPv6 status of deleted group member.
_, err = am.updatePeerIPv6Status(account, "", group, []string{peerID})
if err != nil {
return err
}
}
if err := am.Store.SaveAccount(account); err != nil {
return err
}
@@ -404,3 +459,24 @@ func (am *DefaultAccountManager) GroupDeletePeer(accountID, groupID, peerID stri
return nil
}
func (am *DefaultAccountManager) updatePeerIPv6Status(account *Account, userID string, group *nbgroup.Group, peersToUpdate []string) (bool, error) {
updated := false
for _, peer := range peersToUpdate {
peerObj := account.GetPeer(peer)
update, err := am.DeterminePeerV6(account, peerObj)
if err != nil {
return false, err
}
if update {
updated = true
account.UpdatePeer(peerObj)
if peerObj.IP6 != nil {
am.StoreEvent(userID, group.ID, account.Id, activity.PeerIPv6InheritEnabled, group.EventMeta())
} else {
am.StoreEvent(userID, group.ID, account.Id, activity.PeerIPv6InheritDisabled, group.EventMeta())
}
}
}
return updated, nil
}

View File

@@ -25,6 +25,8 @@ type Group struct {
// Peers list of the group
Peers []string `gorm:"serializer:json"`
IPv6Enabled bool
IntegrationReference integration_reference.IntegrationReference `gorm:"embedded;embeddedPrefix:integration_ref_"`
}
@@ -38,6 +40,7 @@ func (g *Group) Copy() *Group {
ID: g.ID,
Name: g.Name,
Issued: g.Issued,
IPv6Enabled: g.IPv6Enabled,
Peers: make([]string, len(g.Peers)),
IntegrationReference: g.IntegrationReference,
}

View File

@@ -2,6 +2,8 @@ package server
import (
"errors"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/stretchr/testify/require"
"testing"
nbdns "github.com/netbirdio/netbird/dns"
@@ -12,6 +14,8 @@ import (
const (
groupAdminUserID = "testingAdminUser"
groupPeer1Key = "BhRPtynAAYRDy08+q4HTMsos8fs4plTP4NOSh7C1ry8="
groupPeer2Key = "/yF0+vCfv+mRR5k0dca0TrGdO/oiNeAI58gToZm5NyI="
)
func TestDefaultAccountManager_CreateGroup(t *testing.T) {
@@ -20,7 +24,7 @@ func TestDefaultAccountManager_CreateGroup(t *testing.T) {
t.Error("failed to create account manager")
}
account, err := initTestGroupAccount(am)
_, account, err := initTestGroupAccount(am)
if err != nil {
t.Error("failed to init testing account")
}
@@ -55,7 +59,7 @@ func TestDefaultAccountManager_DeleteGroup(t *testing.T) {
t.Error("failed to create account manager")
}
account, err := initTestGroupAccount(am)
_, account, err := initTestGroupAccount(am)
if err != nil {
t.Error("failed to init testing account")
}
@@ -70,6 +74,11 @@ func TestDefaultAccountManager_DeleteGroup(t *testing.T) {
"grp-for-route",
"route",
},
{
"route with peer groups",
"grp-for-route2",
"route",
},
{
"name server groups",
"grp-for-name-server-grp",
@@ -126,10 +135,137 @@ func TestDefaultAccountManager_DeleteGroup(t *testing.T) {
}
}
func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
func TestDefaultAccountManager_GroupIPv6Consistency(t *testing.T) {
am, err := createManager(t)
if err != nil {
t.Error("failed to create account manager")
}
peers, account, err := initTestGroupAccount(am)
peer1Id := peers[0]
peer2Id := peers[1]
if err != nil {
t.Error("failed to init testing account")
}
group := account.GetGroup("grp-for-ipv6")
// First, add one member to the IPv6 group before enabling IPv6.
group.Peers = append(group.Peers, peer1Id)
err = am.SaveGroup(account.Id, groupAdminUserID, group)
require.NoError(t, err, "unable to update group")
account, err = am.Store.GetAccount(account.Id)
require.NoError(t, err, "unable to update account")
group = account.GetGroup("grp-for-ipv6")
require.Nil(t, account.Peers[peer1Id].IP6, "peer1 should not have an IPv6 address if the group doesn't have it enabled.")
require.Nil(t, account.Peers[peer2Id].IP6, "peer2 should not have an IPv6 address.")
// Now, enable IPv6.
group.IPv6Enabled = true
err = am.SaveGroup(account.Id, groupAdminUserID, group)
require.NoError(t, err, "unable to update group")
account, err = am.Store.GetAccount(account.Id)
require.NoError(t, err, "unable to update account")
group = account.GetGroup("grp-for-ipv6")
require.NotNil(t, account.Peers[peer1Id].IP6, "peer1 should have an IPv6 address as it is a member of the IPv6-enabled group.")
require.Nil(t, account.Peers[peer2Id].IP6, "peer2 should not have an IPv6 address as it is not a member of the IPv6-enabled group.")
// Add the second peer.
group.Peers = append(group.Peers, peer2Id)
err = am.SaveGroup(account.Id, groupAdminUserID, group)
require.NoError(t, err, "unable to update group")
account, err = am.Store.GetAccount(account.Id)
require.NoError(t, err, "unable to update account")
group = account.GetGroup("grp-for-ipv6")
require.NotNil(t, account.Peers[peer1Id].IP6, "peer1 should have an IPv6 address as it is a member of the IPv6-enabled group.")
require.NotNil(t, account.Peers[peer2Id].IP6, "peer2 should have an IPv6 address as it is a member of the IPv6-enabled group.")
// Disable IPv6 and simultaneously delete the first peer.
group.IPv6Enabled = false
group.Peers = group.Peers[1:]
err = am.SaveGroup(account.Id, groupAdminUserID, group)
require.NoError(t, err, "unable to update group")
account, err = am.Store.GetAccount(account.Id)
require.NoError(t, err, "unable to update account")
group = account.GetGroup("grp-for-ipv6")
require.Nil(t, account.Peers[peer1Id].IP6, "peer1 should not have an IPv6 address as it is not a member of any IPv6-enabled group.")
require.Nil(t, account.Peers[peer2Id].IP6, "peer2 should not have an IPv6 address as the group has IPv6 disabled.")
// Enable IPv6 and simultaneously add the first peer again.
group.IPv6Enabled = true
group.Peers = append(group.Peers, peer1Id)
err = am.SaveGroup(account.Id, groupAdminUserID, group)
require.NoError(t, err, "unable to update group")
account, err = am.Store.GetAccount(account.Id)
require.NoError(t, err, "unable to update account")
require.NotNil(t, account.Peers[peer1Id].IP6, "peer1 should have an IPv6 address as it is a member of the IPv6-enabled group.")
require.NotNil(t, account.Peers[peer2Id].IP6, "peer2 should have an IPv6 address as it is a member of the IPv6-enabled group.")
// Force disable IPv6.
peer1 := account.GetPeer(peer1Id)
peer2 := account.GetPeer(peer2Id)
peer1.V6Setting = nbpeer.V6Disabled
peer2.V6Setting = nbpeer.V6Disabled
_, err = am.UpdatePeer(account.Id, groupAdminUserID, peer1)
require.NoError(t, err, "unable to update peer1")
_, err = am.UpdatePeer(account.Id, groupAdminUserID, peer2)
require.NoError(t, err, "unable to update peer2")
account, err = am.Store.GetAccount(account.Id)
require.NoError(t, err, "unable to fetch updated account")
group = account.GetGroup("grp-for-ipv6")
require.Nil(t, account.GetPeer(peer1Id).IP6, "peer1 should not have an IPv6 address as it is force disabled.")
require.Nil(t, account.GetPeer(peer2Id).IP6, "peer2 should not have an IPv6 address as it is force disabled.")
// Delete Group.
err = am.DeleteGroup(account.Id, groupAdminUserID, group.ID)
require.NoError(t, err, "unable to delete group")
account, err = am.Store.GetAccount(account.Id)
require.NoError(t, err, "unable to update account")
group = account.GetGroup("grp-for-ipv6")
require.Nil(t, group, "Group should no longer exist.")
require.Nil(t, account.Peers[peer1Id].IP6, "peer1 should not have an IPv6 address as the only IPv6-enabled group was deleted.")
require.Nil(t, account.Peers[peer2Id].IP6, "peer2 should not have an IPv6 address as the only IPv6-enabled group was deleted.")
}
func initTestGroupAccount(am *DefaultAccountManager) ([]string, *Account, error) {
accountID := "testingAcc"
domain := "example.com"
peer1 := &nbpeer.Peer{
Key: peer1Key,
Name: "peer1",
Meta: nbpeer.PeerSystemMeta{
Hostname: "test-host1@netbird.io",
GoOS: "linux",
Kernel: "Linux",
Core: "21.04",
Platform: "x86_64",
OS: "Ubuntu",
WtVersion: "development",
UIVersion: "development",
Ipv6Supported: true,
},
V6Setting: nbpeer.V6Auto,
DNSLabel: groupPeer1Key,
}
peer2 := &nbpeer.Peer{
Key: peer2Key,
Name: "peer2",
Meta: nbpeer.PeerSystemMeta{
Hostname: "test-host2@netbird.io",
GoOS: "linux",
Kernel: "Linux",
Core: "21.04",
Platform: "x86_64",
OS: "Ubuntu",
WtVersion: "development",
UIVersion: "development",
Ipv6Supported: true,
},
V6Setting: nbpeer.V6Auto,
DNSLabel: groupPeer2Key,
}
groupForRoute := &nbgroup.Group{
ID: "grp-for-route",
AccountID: "account-id",
@@ -138,6 +274,14 @@ func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
Peers: make([]string, 0),
}
groupForRoute2 := &nbgroup.Group{
ID: "grp-for-route2",
AccountID: "account-id",
Name: "Group for route",
Issued: nbgroup.GroupIssuedAPI,
Peers: make([]string, 0),
}
groupForNameServerGroups := &nbgroup.Group{
ID: "grp-for-name-server-grp",
AccountID: "account-id",
@@ -178,11 +322,24 @@ func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
Peers: make([]string, 0),
}
groupForIPv6 := &nbgroup.Group{
ID: "grp-for-ipv6",
AccountID: "account-id",
Name: "Group for IPv6",
Issued: nbgroup.GroupIssuedAPI,
Peers: make([]string, 0),
}
routeResource := &route.Route{
ID: "example route",
Groups: []string{groupForRoute.ID},
}
routePeerGroupResource := &route.Route{
ID: "example route with peer groups",
PeerGroups: []string{groupForRoute2.ID},
}
nameServerGroup := &nbdns.NameServerGroup{
ID: "example name server group",
Groups: []string{groupForNameServerGroups.ID},
@@ -209,6 +366,7 @@ func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
}
account := newAccountWithId(accountID, groupAdminUserID, domain)
account.Routes[routeResource.ID] = routeResource
account.Routes[routePeerGroupResource.ID] = routePeerGroupResource
account.NameServerGroups[nameServerGroup.ID] = nameServerGroup
account.Policies = append(account.Policies, policy)
account.SetupKeys[setupKey.Id] = setupKey
@@ -216,15 +374,21 @@ func initTestGroupAccount(am *DefaultAccountManager) (*Account, error) {
err := am.Store.SaveAccount(account)
if err != nil {
return nil, err
return nil, nil, err
}
_ = am.SaveGroup(accountID, groupAdminUserID, groupForRoute)
_ = am.SaveGroup(accountID, groupAdminUserID, groupForRoute2)
_ = am.SaveGroup(accountID, groupAdminUserID, groupForNameServerGroups)
_ = am.SaveGroup(accountID, groupAdminUserID, groupForPolicies)
_ = am.SaveGroup(accountID, groupAdminUserID, groupForSetupKeys)
_ = am.SaveGroup(accountID, groupAdminUserID, groupForUsers)
_ = am.SaveGroup(accountID, groupAdminUserID, groupForIntegration)
_ = am.SaveGroup(accountID, groupAdminUserID, groupForIPv6)
peer1, _, _ = am.AddPeer(setupKey.Key, user.Id, peer1)
peer2, _, _ = am.AddPeer(setupKey.Key, user.Id, peer2)
return am.Store.GetAccount(account.Id)
account, err = am.Store.GetAccount(account.Id)
return []string{peer1.ID, peer2.ID}, account, err
}

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