mirror of
https://github.com/netbirdio/netbird.git
synced 2025-06-19 17:31:39 +02:00
Merge branch 'main' into feature/add_PAT_generation
This commit is contained in:
commit
f8db5742b5
@ -25,7 +25,7 @@ builds:
|
|||||||
- goos: windows
|
- goos: windows
|
||||||
goarch: 386
|
goarch: 386
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w -X github.com/netbirdio/netbird/client/system.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
- -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
tags:
|
tags:
|
||||||
- load_wgnt_from_rsrc
|
- load_wgnt_from_rsrc
|
||||||
@ -47,7 +47,7 @@ builds:
|
|||||||
- arm64
|
- arm64
|
||||||
- arm
|
- arm
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w -X github.com/netbirdio/netbird/client/system.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
- -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
|
|
||||||
- id: netbird-signal
|
- id: netbird-signal
|
||||||
@ -61,7 +61,7 @@ builds:
|
|||||||
- arm64
|
- arm64
|
||||||
- arm
|
- arm
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
- -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
|
@ -10,7 +10,7 @@ builds:
|
|||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w -X github.com/netbirdio/netbird/client/system.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
- -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
|
|
||||||
- id: netbird-ui-windows
|
- id: netbird-ui-windows
|
||||||
@ -24,7 +24,7 @@ builds:
|
|||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w -X github.com/netbirdio/netbird/client/system.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
- -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
||||||
- -H windowsgui
|
- -H windowsgui
|
||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ builds:
|
|||||||
- hardfloat
|
- hardfloat
|
||||||
- softfloat
|
- softfloat
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w -X github.com/netbirdio/netbird/client/system.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
- -s -w -X github.com/netbirdio/netbird/version.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} -X main.builtBy=goreleaser
|
||||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
tags:
|
tags:
|
||||||
- load_wgnt_from_rsrc
|
- load_wgnt_from_rsrc
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<strong>:hatching_chick: New Release! DNS support.</strong>
|
<strong>:hatching_chick: New Release! Peer expiration.</strong>
|
||||||
<a href="https://github.com/netbirdio/netbird/releases">
|
<a href="https://github.com/netbirdio/netbird/releases">
|
||||||
Learn more
|
Learn more
|
||||||
</a>
|
</a>
|
||||||
|
@ -3,10 +3,11 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/skratchdot/open-golang/open"
|
"github.com/skratchdot/open-golang/open"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
gstatus "google.golang.org/grpc/status"
|
gstatus "google.golang.org/grpc/status"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ var loginCmd = &cobra.Command{
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
config, err := internal.GetConfig(internal.ConfigInput{
|
config, err := internal.UpdateOrCreateConfig(internal.ConfigInput{
|
||||||
ManagementURL: managementURL,
|
ManagementURL: managementURL,
|
||||||
AdminURL: adminURL,
|
AdminURL: adminURL,
|
||||||
ConfigPath: configPath,
|
ConfigPath: configPath,
|
||||||
@ -152,7 +153,7 @@ func foregroundLogin(ctx context.Context, cmd *cobra.Command, config *internal.C
|
|||||||
}
|
}
|
||||||
|
|
||||||
func foregroundGetTokenInfo(ctx context.Context, cmd *cobra.Command, config *internal.Config) (*internal.TokenInfo, error) {
|
func foregroundGetTokenInfo(ctx context.Context, cmd *cobra.Command, config *internal.Config) (*internal.TokenInfo, error) {
|
||||||
providerConfig, err := internal.GetDeviceAuthorizationFlowInfo(ctx, config)
|
providerConfig, err := internal.GetDeviceAuthorizationFlowInfo(ctx, config.PrivateKey, config.ManagementURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s, ok := gstatus.FromError(err)
|
s, ok := gstatus.FromError(err)
|
||||||
if ok && s.Code() == codes.NotFound {
|
if ok && s.Code() == codes.NotFound {
|
||||||
|
@ -4,15 +4,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/netbirdio/netbird/client/internal"
|
|
||||||
nbssh "github.com/netbirdio/netbird/client/ssh"
|
|
||||||
"github.com/netbirdio/netbird/util"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
|
nbssh "github.com/netbirdio/netbird/client/ssh"
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -57,7 +59,7 @@ var sshCmd = &cobra.Command{
|
|||||||
|
|
||||||
ctx := internal.CtxInitState(cmd.Context())
|
ctx := internal.CtxInitState(cmd.Context())
|
||||||
|
|
||||||
config, err := internal.ReadConfig(internal.ConfigInput{
|
config, err := internal.UpdateConfig(internal.ConfigInput{
|
||||||
ConfigPath: configPath,
|
ConfigPath: configPath,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -17,8 +17,8 @@ import (
|
|||||||
"github.com/netbirdio/netbird/client/internal"
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
"github.com/netbirdio/netbird/client/system"
|
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
type peerStateDetailOutput struct {
|
type peerStateDetailOutput struct {
|
||||||
@ -209,7 +209,7 @@ func convertToStatusOutputOverview(resp *proto.StatusResponse) statusOutputOverv
|
|||||||
|
|
||||||
overview := statusOutputOverview{
|
overview := statusOutputOverview{
|
||||||
Peers: peersOverview,
|
Peers: peersOverview,
|
||||||
CliVersion: system.NetbirdVersion(),
|
CliVersion: version.NetbirdVersion(),
|
||||||
DaemonVersion: resp.GetDaemonVersion(),
|
DaemonVersion: resp.GetDaemonVersion(),
|
||||||
ManagementState: managementOverview,
|
ManagementState: managementOverview,
|
||||||
SignalState: signalOverview,
|
SignalState: signalOverview,
|
||||||
@ -345,7 +345,7 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool) string {
|
|||||||
"Interface type: %s\n"+
|
"Interface type: %s\n"+
|
||||||
"Peers count: %s\n",
|
"Peers count: %s\n",
|
||||||
overview.DaemonVersion,
|
overview.DaemonVersion,
|
||||||
system.NetbirdVersion(),
|
version.NetbirdVersion(),
|
||||||
managementConnString,
|
managementConnString,
|
||||||
signalConnString,
|
signalConnString,
|
||||||
overview.FQDN,
|
overview.FQDN,
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
"github.com/netbirdio/netbird/client/system"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
var resp = &proto.StatusResponse{
|
var resp = &proto.StatusResponse{
|
||||||
@ -89,7 +89,7 @@ var overview = statusOutputOverview{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
CliVersion: system.NetbirdVersion(),
|
CliVersion: version.NetbirdVersion(),
|
||||||
DaemonVersion: "0.14.1",
|
DaemonVersion: "0.14.1",
|
||||||
ManagementState: managementStateOutput{
|
ManagementState: managementStateOutput{
|
||||||
URL: "my-awesome-management.com:443",
|
URL: "my-awesome-management.com:443",
|
||||||
|
@ -3,17 +3,19 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/netbirdio/netbird/client/internal"
|
"net"
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
"net/netip"
|
||||||
nbStatus "github.com/netbirdio/netbird/client/status"
|
"strings"
|
||||||
"github.com/netbirdio/netbird/util"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
gstatus "google.golang.org/grpc/status"
|
gstatus "google.golang.org/grpc/status"
|
||||||
"net"
|
|
||||||
"net/netip"
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
"strings"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -70,7 +72,7 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
config, err := internal.GetConfig(internal.ConfigInput{
|
config, err := internal.UpdateOrCreateConfig(internal.ConfigInput{
|
||||||
ManagementURL: managementURL,
|
ManagementURL: managementURL,
|
||||||
AdminURL: adminURL,
|
AdminURL: adminURL,
|
||||||
ConfigPath: configPath,
|
ConfigPath: configPath,
|
||||||
@ -92,7 +94,7 @@ func runInForegroundMode(ctx context.Context, cmd *cobra.Command) error {
|
|||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
ctx, cancel = context.WithCancel(ctx)
|
ctx, cancel = context.WithCancel(ctx)
|
||||||
SetupCloseHandler(ctx, cancel)
|
SetupCloseHandler(ctx, cancel)
|
||||||
return internal.RunClient(ctx, config, nbStatus.NewRecorder())
|
return internal.RunClient(ctx, config, peer.NewRecorder())
|
||||||
}
|
}
|
||||||
|
|
||||||
func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
func runInDaemonMode(ctx context.Context, cmd *cobra.Command) error {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/netbirdio/netbird/client/system"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -11,7 +12,7 @@ var (
|
|||||||
Short: "prints Netbird version",
|
Short: "prints Netbird version",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
cmd.SetOut(cmd.OutOrStdout())
|
cmd.SetOut(cmd.OutOrStdout())
|
||||||
cmd.Println(system.NetbirdVersion())
|
cmd.Println(version.NetbirdVersion())
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
57
client/firewall/firewall.go
Normal file
57
client/firewall/firewall.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package firewall
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Rule abstraction should be implemented by each firewall manager
|
||||||
|
//
|
||||||
|
// Each firewall type for different OS can use different type
|
||||||
|
// of the properties to hold data of the created rule
|
||||||
|
type Rule interface {
|
||||||
|
// GetRuleID returns the rule id
|
||||||
|
GetRuleID() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direction is the direction of the traffic
|
||||||
|
type Direction int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DirectionSrc is the direction of the traffic from the source
|
||||||
|
DirectionSrc Direction = iota
|
||||||
|
// DirectionDst is the direction of the traffic from the destination
|
||||||
|
DirectionDst
|
||||||
|
)
|
||||||
|
|
||||||
|
// Action is the action to be taken on a rule
|
||||||
|
type Action int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ActionAccept is the action to accept a packet
|
||||||
|
ActionAccept Action = iota
|
||||||
|
// ActionDrop is the action to drop a packet
|
||||||
|
ActionDrop
|
||||||
|
)
|
||||||
|
|
||||||
|
// Manager is the high level abstraction of a firewall manager
|
||||||
|
//
|
||||||
|
// It declares methods which handle actions required by the
|
||||||
|
// Netbird client for ACL and routing functionality
|
||||||
|
type Manager interface {
|
||||||
|
// AddFiltering rule to the firewall
|
||||||
|
AddFiltering(
|
||||||
|
ip net.IP,
|
||||||
|
port *Port,
|
||||||
|
direction Direction,
|
||||||
|
action Action,
|
||||||
|
comment string,
|
||||||
|
) (Rule, error)
|
||||||
|
|
||||||
|
// DeleteRule from the firewall by rule definition
|
||||||
|
DeleteRule(rule Rule) error
|
||||||
|
|
||||||
|
// Reset firewall to the default state
|
||||||
|
Reset() error
|
||||||
|
|
||||||
|
// TODO: migrate routemanager firewal actions to this interface
|
||||||
|
}
|
160
client/firewall/iptables/manager_linux.go
Normal file
160
client/firewall/iptables/manager_linux.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package iptables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
fw "github.com/netbirdio/netbird/client/firewall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ChainFilterName is the name of the chain that is used for filtering by the Netbird client
|
||||||
|
ChainFilterName = "NETBIRD-ACL"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Manager of iptables firewall
|
||||||
|
type Manager struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
|
||||||
|
ipv4Client *iptables.IPTables
|
||||||
|
ipv6Client *iptables.IPTables
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create iptables firewall manager
|
||||||
|
func Create() (*Manager, error) {
|
||||||
|
m := &Manager{}
|
||||||
|
|
||||||
|
// init clients for booth ipv4 and ipv6
|
||||||
|
ipv4Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("iptables is not installed in the system or not supported")
|
||||||
|
}
|
||||||
|
m.ipv4Client = ipv4Client
|
||||||
|
|
||||||
|
ipv6Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv6)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ip6tables is not installed in the system or not supported")
|
||||||
|
}
|
||||||
|
m.ipv6Client = ipv6Client
|
||||||
|
|
||||||
|
if err := m.Reset(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to reset firewall: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFiltering rule to the firewall
|
||||||
|
func (m *Manager) AddFiltering(
|
||||||
|
ip net.IP,
|
||||||
|
port *fw.Port,
|
||||||
|
direction fw.Direction,
|
||||||
|
action fw.Action,
|
||||||
|
comment string,
|
||||||
|
) (fw.Rule, error) {
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
client := m.client(ip)
|
||||||
|
ok, err := client.ChainExists("filter", ChainFilterName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to check if chain exists: %s", err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
if err := client.NewChain("filter", ChainFilterName); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create chain: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if port == nil || port.Values == nil || (port.IsRange && len(port.Values) != 2) {
|
||||||
|
return nil, fmt.Errorf("invalid port definition")
|
||||||
|
}
|
||||||
|
pv := strconv.Itoa(port.Values[0])
|
||||||
|
if port.IsRange {
|
||||||
|
pv += ":" + strconv.Itoa(port.Values[1])
|
||||||
|
}
|
||||||
|
specs := m.filterRuleSpecs("filter", ChainFilterName, ip, pv, direction, action, comment)
|
||||||
|
if err := client.AppendUnique("filter", ChainFilterName, specs...); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rule := &Rule{
|
||||||
|
id: uuid.New().String(),
|
||||||
|
specs: specs,
|
||||||
|
v6: ip.To4() == nil,
|
||||||
|
}
|
||||||
|
return rule, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRule from the firewall by rule definition
|
||||||
|
func (m *Manager) DeleteRule(rule fw.Rule) error {
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
r, ok := rule.(*Rule)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid rule type")
|
||||||
|
}
|
||||||
|
client := m.ipv4Client
|
||||||
|
if r.v6 {
|
||||||
|
client = m.ipv6Client
|
||||||
|
}
|
||||||
|
return client.Delete("filter", ChainFilterName, r.specs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset firewall to the default state
|
||||||
|
func (m *Manager) Reset() error {
|
||||||
|
m.mutex.Lock()
|
||||||
|
defer m.mutex.Unlock()
|
||||||
|
if err := m.reset(m.ipv4Client, "filter", ChainFilterName); err != nil {
|
||||||
|
return fmt.Errorf("clean ipv4 firewall ACL chain: %w", err)
|
||||||
|
}
|
||||||
|
if err := m.reset(m.ipv6Client, "filter", ChainFilterName); err != nil {
|
||||||
|
return fmt.Errorf("clean ipv6 firewall ACL chain: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset firewall chain, clear it and drop it
|
||||||
|
func (m *Manager) reset(client *iptables.IPTables, table, chain string) error {
|
||||||
|
ok, err := client.ChainExists(table, chain)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to check if chain exists: %w", err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := client.ClearChain(table, ChainFilterName); err != nil {
|
||||||
|
return fmt.Errorf("failed to clear chain: %w", err)
|
||||||
|
}
|
||||||
|
return client.DeleteChain(table, ChainFilterName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterRuleSpecs returns the specs of a filtering rule
|
||||||
|
func (m *Manager) filterRuleSpecs(
|
||||||
|
table string, chain string, ip net.IP, port string,
|
||||||
|
direction fw.Direction, action fw.Action, comment string,
|
||||||
|
) (specs []string) {
|
||||||
|
if direction == fw.DirectionSrc {
|
||||||
|
specs = append(specs, "-s", ip.String())
|
||||||
|
}
|
||||||
|
specs = append(specs, "-p", "tcp", "--dport", port)
|
||||||
|
specs = append(specs, "-j", m.actionToStr(action))
|
||||||
|
return append(specs, "-m", "comment", "--comment", comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// client returns corresponding iptables client for the given ip
|
||||||
|
func (m *Manager) client(ip net.IP) *iptables.IPTables {
|
||||||
|
if ip.To4() != nil {
|
||||||
|
return m.ipv4Client
|
||||||
|
}
|
||||||
|
return m.ipv6Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) actionToStr(action fw.Action) string {
|
||||||
|
if action == fw.ActionAccept {
|
||||||
|
return "ACCEPT"
|
||||||
|
}
|
||||||
|
return "DROP"
|
||||||
|
}
|
105
client/firewall/iptables/manager_linux_test.go
Normal file
105
client/firewall/iptables/manager_linux_test.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package iptables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
|
fw "github.com/netbirdio/netbird/client/firewall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewManager(t *testing.T) {
|
||||||
|
ipv4Client, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manager, err := Create()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rule1 fw.Rule
|
||||||
|
t.Run("add first rule", func(t *testing.T) {
|
||||||
|
ip := net.ParseIP("10.20.0.2")
|
||||||
|
port := &fw.Port{Proto: fw.PortProtocolTCP, Values: []int{8080}}
|
||||||
|
rule1, err = manager.AddFiltering(ip, port, fw.DirectionDst, fw.ActionAccept, "accept HTTP traffic")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to add rule: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRuleSpecs(t, ipv4Client, true, rule1.(*Rule).specs...)
|
||||||
|
})
|
||||||
|
|
||||||
|
var rule2 fw.Rule
|
||||||
|
t.Run("add second rule", func(t *testing.T) {
|
||||||
|
ip := net.ParseIP("10.20.0.3")
|
||||||
|
port := &fw.Port{
|
||||||
|
Proto: fw.PortProtocolTCP,
|
||||||
|
Values: []int{8043: 8046},
|
||||||
|
}
|
||||||
|
rule2, err = manager.AddFiltering(
|
||||||
|
ip, port, fw.DirectionDst, fw.ActionAccept, "accept HTTPS traffic from ports range")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to add rule: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRuleSpecs(t, ipv4Client, true, rule2.(*Rule).specs...)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("delete first rule", func(t *testing.T) {
|
||||||
|
if err := manager.DeleteRule(rule1); err != nil {
|
||||||
|
t.Errorf("failed to delete rule: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRuleSpecs(t, ipv4Client, false, rule1.(*Rule).specs...)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("delete second rule", func(t *testing.T) {
|
||||||
|
if err := manager.DeleteRule(rule2); err != nil {
|
||||||
|
t.Errorf("failed to delete rule: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRuleSpecs(t, ipv4Client, false, rule2.(*Rule).specs...)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("reset check", func(t *testing.T) {
|
||||||
|
// add second rule
|
||||||
|
ip := net.ParseIP("10.20.0.3")
|
||||||
|
port := &fw.Port{Proto: fw.PortProtocolUDP, Values: []int{5353}}
|
||||||
|
_, err = manager.AddFiltering(ip, port, fw.DirectionDst, fw.ActionAccept, "accept Fake DNS traffic")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to add rule: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := manager.Reset(); err != nil {
|
||||||
|
t.Errorf("failed to reset: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := ipv4Client.ChainExists("filter", ChainFilterName)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to drop chain: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
t.Errorf("chain '%v' still exists after Reset", ChainFilterName)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRuleSpecs(t *testing.T, ipv4Client *iptables.IPTables, mustExists bool, rulespec ...string) {
|
||||||
|
exists, err := ipv4Client.Exists("filter", ChainFilterName, rulespec...)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to check rule: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exists && mustExists {
|
||||||
|
t.Errorf("rule '%v' does not exist", rulespec)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if exists && !mustExists {
|
||||||
|
t.Errorf("rule '%v' exist", rulespec)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
13
client/firewall/iptables/rule.go
Normal file
13
client/firewall/iptables/rule.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package iptables
|
||||||
|
|
||||||
|
// Rule to handle management of rules
|
||||||
|
type Rule struct {
|
||||||
|
id string
|
||||||
|
specs []string
|
||||||
|
v6 bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRuleID returns the rule id
|
||||||
|
func (r *Rule) GetRuleID() string {
|
||||||
|
return r.id
|
||||||
|
}
|
24
client/firewall/port.go
Normal file
24
client/firewall/port.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package firewall
|
||||||
|
|
||||||
|
// PortProtocol is the protocol of the port
|
||||||
|
type PortProtocol string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PortProtocolTCP is the TCP protocol
|
||||||
|
PortProtocolTCP PortProtocol = "tcp"
|
||||||
|
|
||||||
|
// PortProtocolUDP is the UDP protocol
|
||||||
|
PortProtocolUDP PortProtocol = "udp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Port of the address for firewall rule
|
||||||
|
type Port struct {
|
||||||
|
// IsRange is true Values contains two values, the first is the start port, the second is the end port
|
||||||
|
IsRange bool
|
||||||
|
|
||||||
|
// Values contains one value for single port, multiple values for the list of ports, or two values for the range of ports
|
||||||
|
Values []int
|
||||||
|
|
||||||
|
// Proto is the protocol of the port
|
||||||
|
Proto PortProtocol
|
||||||
|
}
|
@ -1,19 +1,18 @@
|
|||||||
package internal
|
package internal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/ssh"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
mgm "github.com/netbirdio/netbird/management/client"
|
|
||||||
"github.com/netbirdio/netbird/util"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/ssh"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -74,6 +73,28 @@ type Config struct {
|
|||||||
CustomDNSAddress string
|
CustomDNSAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateConfig update existing configuration according to input configuration and return with the configuration
|
||||||
|
func UpdateConfig(input ConfigInput) (*Config, error) {
|
||||||
|
if !configFileIsExists(input.ConfigPath) {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "config file doesn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
return update(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOrCreateConfig reads existing config or generates a new one
|
||||||
|
func UpdateOrCreateConfig(input ConfigInput) (*Config, error) {
|
||||||
|
if !configFileIsExists(input.ConfigPath) {
|
||||||
|
log.Infof("generating new config %s", input.ConfigPath)
|
||||||
|
return createNewConfig(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isPreSharedKeyHidden(input.PreSharedKey) {
|
||||||
|
input.PreSharedKey = nil
|
||||||
|
}
|
||||||
|
return update(input)
|
||||||
|
}
|
||||||
|
|
||||||
// createNewConfig creates a new config generating a new Wireguard key and saving to file
|
// createNewConfig creates a new config generating a new Wireguard key and saving to file
|
||||||
func createNewConfig(input ConfigInput) (*Config, error) {
|
func createNewConfig(input ConfigInput) (*Config, error) {
|
||||||
wgKey := generateKey()
|
wgKey := generateKey()
|
||||||
@ -92,14 +113,14 @@ func createNewConfig(input ConfigInput) (*Config, error) {
|
|||||||
CustomDNSAddress: string(input.CustomDNSAddress),
|
CustomDNSAddress: string(input.CustomDNSAddress),
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultManagementURL, err := ParseURL("Management URL", DefaultManagementURL)
|
defaultManagementURL, err := parseURL("Management URL", DefaultManagementURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
config.ManagementURL = defaultManagementURL
|
config.ManagementURL = defaultManagementURL
|
||||||
if input.ManagementURL != "" {
|
if input.ManagementURL != "" {
|
||||||
URL, err := ParseURL("Management URL", input.ManagementURL)
|
URL, err := parseURL("Management URL", input.ManagementURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -110,14 +131,14 @@ func createNewConfig(input ConfigInput) (*Config, error) {
|
|||||||
config.PreSharedKey = *input.PreSharedKey
|
config.PreSharedKey = *input.PreSharedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultAdminURL, err := ParseURL("Admin URL", DefaultAdminURL)
|
defaultAdminURL, err := parseURL("Admin URL", DefaultAdminURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
config.AdminURL = defaultAdminURL
|
config.AdminURL = defaultAdminURL
|
||||||
if input.AdminURL != "" {
|
if input.AdminURL != "" {
|
||||||
newURL, err := ParseURL("Admin Panel URL", input.AdminURL)
|
newURL, err := parseURL("Admin Panel URL", input.AdminURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -134,40 +155,8 @@ func createNewConfig(input ConfigInput) (*Config, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseURL parses and validates a service URL
|
func update(input ConfigInput) (*Config, error) {
|
||||||
func ParseURL(serviceName, serviceURL string) (*url.URL, error) {
|
|
||||||
parsedMgmtURL, err := url.ParseRequestURI(serviceURL)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed parsing %s URL %s: [%s]", serviceName, serviceURL, err.Error())
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if parsedMgmtURL.Scheme != "https" && parsedMgmtURL.Scheme != "http" {
|
|
||||||
return nil, fmt.Errorf(
|
|
||||||
"invalid %s URL provided %s. Supported format [http|https]://[host]:[port]",
|
|
||||||
serviceName, serviceURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
if parsedMgmtURL.Port() == "" {
|
|
||||||
switch parsedMgmtURL.Scheme {
|
|
||||||
case "https":
|
|
||||||
parsedMgmtURL.Host = parsedMgmtURL.Host + ":443"
|
|
||||||
case "http":
|
|
||||||
parsedMgmtURL.Host = parsedMgmtURL.Host + ":80"
|
|
||||||
default:
|
|
||||||
log.Infof("unable to determine a default port for schema %s in URL %s", parsedMgmtURL.Scheme, serviceURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedMgmtURL, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadConfig reads existing configuration and update settings according to input configuration
|
|
||||||
func ReadConfig(input ConfigInput) (*Config, error) {
|
|
||||||
config := &Config{}
|
config := &Config{}
|
||||||
if _, err := os.Stat(input.ConfigPath); os.IsNotExist(err) {
|
|
||||||
return nil, status.Errorf(codes.NotFound, "config file doesn't exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := util.ReadJson(input.ConfigPath, config); err != nil {
|
if _, err := util.ReadJson(input.ConfigPath, config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -178,7 +167,7 @@ func ReadConfig(input ConfigInput) (*Config, error) {
|
|||||||
if input.ManagementURL != "" && config.ManagementURL.String() != input.ManagementURL {
|
if input.ManagementURL != "" && config.ManagementURL.String() != input.ManagementURL {
|
||||||
log.Infof("new Management URL provided, updated to %s (old value %s)",
|
log.Infof("new Management URL provided, updated to %s (old value %s)",
|
||||||
input.ManagementURL, config.ManagementURL)
|
input.ManagementURL, config.ManagementURL)
|
||||||
newURL, err := ParseURL("Management URL", input.ManagementURL)
|
newURL, err := parseURL("Management URL", input.ManagementURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -189,7 +178,7 @@ func ReadConfig(input ConfigInput) (*Config, error) {
|
|||||||
if input.AdminURL != "" && (config.AdminURL == nil || config.AdminURL.String() != input.AdminURL) {
|
if input.AdminURL != "" && (config.AdminURL == nil || config.AdminURL.String() != input.AdminURL) {
|
||||||
log.Infof("new Admin Panel URL provided, updated to %s (old value %s)",
|
log.Infof("new Admin Panel URL provided, updated to %s (old value %s)",
|
||||||
input.AdminURL, config.AdminURL)
|
input.AdminURL, config.AdminURL)
|
||||||
newURL, err := ParseURL("Admin Panel URL", input.AdminURL)
|
newURL, err := parseURL("Admin Panel URL", input.AdminURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -237,17 +226,32 @@ func ReadConfig(input ConfigInput) (*Config, error) {
|
|||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConfig reads existing config or generates a new one
|
// parseURL parses and validates a service URL
|
||||||
func GetConfig(input ConfigInput) (*Config, error) {
|
func parseURL(serviceName, serviceURL string) (*url.URL, error) {
|
||||||
if _, err := os.Stat(input.ConfigPath); os.IsNotExist(err) {
|
parsedMgmtURL, err := url.ParseRequestURI(serviceURL)
|
||||||
log.Infof("generating new config %s", input.ConfigPath)
|
if err != nil {
|
||||||
return createNewConfig(input)
|
log.Errorf("failed parsing %s URL %s: [%s]", serviceName, serviceURL, err.Error())
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if isPreSharedKeyHidden(input.PreSharedKey) {
|
if parsedMgmtURL.Scheme != "https" && parsedMgmtURL.Scheme != "http" {
|
||||||
input.PreSharedKey = nil
|
return nil, fmt.Errorf(
|
||||||
|
"invalid %s URL provided %s. Supported format [http|https]://[host]:[port]",
|
||||||
|
serviceName, serviceURL)
|
||||||
}
|
}
|
||||||
return ReadConfig(input)
|
|
||||||
|
if parsedMgmtURL.Port() == "" {
|
||||||
|
switch parsedMgmtURL.Scheme {
|
||||||
|
case "https":
|
||||||
|
parsedMgmtURL.Host = parsedMgmtURL.Host + ":443"
|
||||||
|
case "http":
|
||||||
|
parsedMgmtURL.Host = parsedMgmtURL.Host + ":80"
|
||||||
|
default:
|
||||||
|
log.Infof("unable to determine a default port for schema %s in URL %s", parsedMgmtURL.Scheme, serviceURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedMgmtURL, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateKey generates a new Wireguard private key
|
// generateKey generates a new Wireguard private key
|
||||||
@ -259,111 +263,6 @@ func generateKey() string {
|
|||||||
return key.String()
|
return key.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeviceAuthorizationFlow represents Device Authorization Flow information
|
|
||||||
type DeviceAuthorizationFlow struct {
|
|
||||||
Provider string
|
|
||||||
ProviderConfig ProviderConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProviderConfig has all attributes needed to initiate a device authorization flow
|
|
||||||
type ProviderConfig struct {
|
|
||||||
// ClientID An IDP application client id
|
|
||||||
ClientID string
|
|
||||||
// ClientSecret An IDP application client secret
|
|
||||||
ClientSecret string
|
|
||||||
// Domain An IDP API domain
|
|
||||||
// Deprecated. Use OIDCConfigEndpoint instead
|
|
||||||
Domain string
|
|
||||||
// Audience An Audience for to authorization validation
|
|
||||||
Audience string
|
|
||||||
// TokenEndpoint is the endpoint of an IDP manager where clients can obtain access token
|
|
||||||
TokenEndpoint string
|
|
||||||
// DeviceAuthEndpoint is the endpoint of an IDP manager where clients can obtain device authorization code
|
|
||||||
DeviceAuthEndpoint string
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDeviceAuthorizationFlowInfo(ctx context.Context, config *Config) (DeviceAuthorizationFlow, error) {
|
|
||||||
// validate our peer's Wireguard PRIVATE key
|
|
||||||
myPrivateKey, err := wgtypes.ParseKey(config.PrivateKey)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed parsing Wireguard key %s: [%s]", config.PrivateKey, err.Error())
|
|
||||||
return DeviceAuthorizationFlow{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var mgmTlsEnabled bool
|
|
||||||
if config.ManagementURL.Scheme == "https" {
|
|
||||||
mgmTlsEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("connecting to Management Service %s", config.ManagementURL.String())
|
|
||||||
mgmClient, err := mgm.NewClient(ctx, config.ManagementURL.Host, myPrivateKey, mgmTlsEnabled)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed connecting to Management Service %s %v", config.ManagementURL.String(), err)
|
|
||||||
return DeviceAuthorizationFlow{}, err
|
|
||||||
}
|
|
||||||
log.Debugf("connected to the Management service %s", config.ManagementURL.String())
|
|
||||||
defer func() {
|
|
||||||
err = mgmClient.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed to close the Management service client %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
serverKey, err := mgmClient.GetServerPublicKey()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed while getting Management Service public key: %v", err)
|
|
||||||
return DeviceAuthorizationFlow{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
protoDeviceAuthorizationFlow, err := mgmClient.GetDeviceAuthorizationFlow(*serverKey)
|
|
||||||
if err != nil {
|
|
||||||
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
|
|
||||||
log.Warnf("server couldn't find device flow, contact admin: %v", err)
|
|
||||||
return DeviceAuthorizationFlow{}, err
|
|
||||||
} else {
|
|
||||||
log.Errorf("failed to retrieve device flow: %v", err)
|
|
||||||
return DeviceAuthorizationFlow{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceAuthorizationFlow := DeviceAuthorizationFlow{
|
|
||||||
Provider: protoDeviceAuthorizationFlow.Provider.String(),
|
|
||||||
|
|
||||||
ProviderConfig: ProviderConfig{
|
|
||||||
Audience: protoDeviceAuthorizationFlow.GetProviderConfig().GetAudience(),
|
|
||||||
ClientID: protoDeviceAuthorizationFlow.GetProviderConfig().GetClientID(),
|
|
||||||
ClientSecret: protoDeviceAuthorizationFlow.GetProviderConfig().GetClientSecret(),
|
|
||||||
Domain: protoDeviceAuthorizationFlow.GetProviderConfig().Domain,
|
|
||||||
TokenEndpoint: protoDeviceAuthorizationFlow.GetProviderConfig().GetTokenEndpoint(),
|
|
||||||
DeviceAuthEndpoint: protoDeviceAuthorizationFlow.GetProviderConfig().GetDeviceAuthEndpoint(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err = isProviderConfigValid(deviceAuthorizationFlow.ProviderConfig)
|
|
||||||
if err != nil {
|
|
||||||
return DeviceAuthorizationFlow{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return deviceAuthorizationFlow, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isProviderConfigValid(config ProviderConfig) error {
|
|
||||||
errorMSGFormat := "invalid provider configuration received from management: %s value is empty. Contact your NetBird administrator"
|
|
||||||
if config.Audience == "" {
|
|
||||||
return fmt.Errorf(errorMSGFormat, "Audience")
|
|
||||||
}
|
|
||||||
if config.ClientID == "" {
|
|
||||||
return fmt.Errorf(errorMSGFormat, "Client ID")
|
|
||||||
}
|
|
||||||
if config.TokenEndpoint == "" {
|
|
||||||
return fmt.Errorf(errorMSGFormat, "Token Endpoint")
|
|
||||||
}
|
|
||||||
if config.DeviceAuthEndpoint == "" {
|
|
||||||
return fmt.Errorf(errorMSGFormat, "Device Auth Endpoint")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't overwrite pre-shared key if we receive asterisks from UI
|
// don't overwrite pre-shared key if we receive asterisks from UI
|
||||||
func isPreSharedKeyHidden(preSharedKey *string) bool {
|
func isPreSharedKeyHidden(preSharedKey *string) bool {
|
||||||
if preSharedKey != nil && *preSharedKey == "**********" {
|
if preSharedKey != nil && *preSharedKey == "**********" {
|
||||||
@ -371,3 +270,8 @@ func isPreSharedKeyHidden(preSharedKey *string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configFileIsExists(path string) bool {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
func TestGetConfig(t *testing.T) {
|
func TestGetConfig(t *testing.T) {
|
||||||
// case 1: new default config has to be generated
|
// case 1: new default config has to be generated
|
||||||
config, err := GetConfig(ConfigInput{
|
config, err := UpdateOrCreateConfig(ConfigInput{
|
||||||
ConfigPath: filepath.Join(t.TempDir(), "config.json"),
|
ConfigPath: filepath.Join(t.TempDir(), "config.json"),
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ func TestGetConfig(t *testing.T) {
|
|||||||
preSharedKey := "preSharedKey"
|
preSharedKey := "preSharedKey"
|
||||||
|
|
||||||
// case 2: new config has to be generated
|
// case 2: new config has to be generated
|
||||||
config, err = GetConfig(ConfigInput{
|
config, err = UpdateOrCreateConfig(ConfigInput{
|
||||||
ManagementURL: managementURL,
|
ManagementURL: managementURL,
|
||||||
AdminURL: adminURL,
|
AdminURL: adminURL,
|
||||||
ConfigPath: path,
|
ConfigPath: path,
|
||||||
@ -50,7 +50,7 @@ func TestGetConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// case 3: existing config -> fetch it
|
// case 3: existing config -> fetch it
|
||||||
config, err = GetConfig(ConfigInput{
|
config, err = UpdateOrCreateConfig(ConfigInput{
|
||||||
ManagementURL: managementURL,
|
ManagementURL: managementURL,
|
||||||
AdminURL: adminURL,
|
AdminURL: adminURL,
|
||||||
ConfigPath: path,
|
ConfigPath: path,
|
||||||
@ -65,7 +65,7 @@ func TestGetConfig(t *testing.T) {
|
|||||||
|
|
||||||
// case 4: existing config, but new managementURL has been provided -> update config
|
// case 4: existing config, but new managementURL has been provided -> update config
|
||||||
newManagementURL := "https://test.newManagement.url:33071"
|
newManagementURL := "https://test.newManagement.url:33071"
|
||||||
config, err = GetConfig(ConfigInput{
|
config, err = UpdateOrCreateConfig(ConfigInput{
|
||||||
ManagementURL: newManagementURL,
|
ManagementURL: newManagementURL,
|
||||||
AdminURL: adminURL,
|
AdminURL: adminURL,
|
||||||
ConfigPath: path,
|
ConfigPath: path,
|
||||||
@ -101,13 +101,13 @@ func TestHiddenPreSharedKey(t *testing.T) {
|
|||||||
|
|
||||||
// generate default cfg
|
// generate default cfg
|
||||||
cfgFile := filepath.Join(t.TempDir(), "config.json")
|
cfgFile := filepath.Join(t.TempDir(), "config.json")
|
||||||
_, _ = GetConfig(ConfigInput{
|
_, _ = UpdateOrCreateConfig(ConfigInput{
|
||||||
ConfigPath: cfgFile,
|
ConfigPath: cfgFile,
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
cfg, err := GetConfig(ConfigInput{
|
cfg, err := UpdateOrCreateConfig(ConfigInput{
|
||||||
ConfigPath: cfgFile,
|
ConfigPath: cfgFile,
|
||||||
PreSharedKey: tt.preSharedKey,
|
PreSharedKey: tt.preSharedKey,
|
||||||
})
|
})
|
||||||
|
@ -6,25 +6,23 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/cenkalti/backoff/v4"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
gstatus "google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/ssh"
|
"github.com/netbirdio/netbird/client/ssh"
|
||||||
nbStatus "github.com/netbirdio/netbird/client/status"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/system"
|
"github.com/netbirdio/netbird/client/system"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
mgm "github.com/netbirdio/netbird/management/client"
|
mgm "github.com/netbirdio/netbird/management/client"
|
||||||
mgmProto "github.com/netbirdio/netbird/management/proto"
|
mgmProto "github.com/netbirdio/netbird/management/proto"
|
||||||
signal "github.com/netbirdio/netbird/signal/client"
|
signal "github.com/netbirdio/netbird/signal/client"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/cenkalti/backoff/v4"
|
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
gstatus "google.golang.org/grpc/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunClient with main logic.
|
// RunClient with main logic.
|
||||||
func RunClient(ctx context.Context, config *Config, statusRecorder *nbStatus.Status) error {
|
func RunClient(ctx context.Context, config *Config, statusRecorder *peer.Status) error {
|
||||||
backOff := &backoff.ExponentialBackOff{
|
backOff := &backoff.ExponentialBackOff{
|
||||||
InitialInterval: time.Second,
|
InitialInterval: time.Second,
|
||||||
RandomizationFactor: 1,
|
RandomizationFactor: 1,
|
||||||
@ -105,7 +103,7 @@ func RunClient(ctx context.Context, config *Config, statusRecorder *nbStatus.Sta
|
|||||||
}
|
}
|
||||||
statusRecorder.MarkManagementConnected(managementURL)
|
statusRecorder.MarkManagementConnected(managementURL)
|
||||||
|
|
||||||
localPeerState := nbStatus.LocalPeerState{
|
localPeerState := peer.LocalPeerState{
|
||||||
IP: loginResp.GetPeerConfig().GetAddress(),
|
IP: loginResp.GetPeerConfig().GetAddress(),
|
||||||
PubKey: myPrivateKey.PublicKey().String(),
|
PubKey: myPrivateKey.PublicKey().String(),
|
||||||
KernelInterface: iface.WireguardModuleIsLoaded(),
|
KernelInterface: iface.WireguardModuleIsLoaded(),
|
||||||
@ -251,7 +249,7 @@ func loginToManagement(ctx context.Context, client mgm.Client, pubSSHKey []byte)
|
|||||||
// The check is performed only for the NetBird's managed version.
|
// The check is performed only for the NetBird's managed version.
|
||||||
func UpdateOldManagementPort(ctx context.Context, config *Config, configPath string) (*Config, error) {
|
func UpdateOldManagementPort(ctx context.Context, config *Config, configPath string) (*Config, error) {
|
||||||
|
|
||||||
defaultManagementURL, err := ParseURL("Management URL", DefaultManagementURL)
|
defaultManagementURL, err := parseURL("Management URL", DefaultManagementURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -273,7 +271,7 @@ func UpdateOldManagementPort(ctx context.Context, config *Config, configPath str
|
|||||||
|
|
||||||
if mgmTlsEnabled && config.ManagementURL.Port() == fmt.Sprintf("%d", ManagementLegacyPort) {
|
if mgmTlsEnabled && config.ManagementURL.Port() == fmt.Sprintf("%d", ManagementLegacyPort) {
|
||||||
|
|
||||||
newURL, err := ParseURL("Management URL", fmt.Sprintf("%s://%s:%d",
|
newURL, err := parseURL("Management URL", fmt.Sprintf("%s://%s:%d",
|
||||||
config.ManagementURL.Scheme, config.ManagementURL.Hostname(), 443))
|
config.ManagementURL.Scheme, config.ManagementURL.Hostname(), 443))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -307,7 +305,7 @@ func UpdateOldManagementPort(ctx context.Context, config *Config, configPath str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// everything is alright => update the config
|
// everything is alright => update the config
|
||||||
newConfig, err := ReadConfig(ConfigInput{
|
newConfig, err := UpdateConfig(ConfigInput{
|
||||||
ManagementURL: newURL.String(),
|
ManagementURL: newURL.String(),
|
||||||
ConfigPath: configPath,
|
ConfigPath: configPath,
|
||||||
})
|
})
|
||||||
|
119
client/internal/device_auth.go
Normal file
119
client/internal/device_auth.go
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
|
||||||
|
mgm "github.com/netbirdio/netbird/management/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeviceAuthorizationFlow represents Device Authorization Flow information
|
||||||
|
type DeviceAuthorizationFlow struct {
|
||||||
|
Provider string
|
||||||
|
ProviderConfig ProviderConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProviderConfig has all attributes needed to initiate a device authorization flow
|
||||||
|
type ProviderConfig struct {
|
||||||
|
// ClientID An IDP application client id
|
||||||
|
ClientID string
|
||||||
|
// ClientSecret An IDP application client secret
|
||||||
|
ClientSecret string
|
||||||
|
// Domain An IDP API domain
|
||||||
|
// Deprecated. Use OIDCConfigEndpoint instead
|
||||||
|
Domain string
|
||||||
|
// Audience An Audience for to authorization validation
|
||||||
|
Audience string
|
||||||
|
// TokenEndpoint is the endpoint of an IDP manager where clients can obtain access token
|
||||||
|
TokenEndpoint string
|
||||||
|
// DeviceAuthEndpoint is the endpoint of an IDP manager where clients can obtain device authorization code
|
||||||
|
DeviceAuthEndpoint string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDeviceAuthorizationFlowInfo initialize a DeviceAuthorizationFlow instance and return with it
|
||||||
|
func GetDeviceAuthorizationFlowInfo(ctx context.Context, privateKey string, mgmURL *url.URL) (DeviceAuthorizationFlow, error) {
|
||||||
|
// validate our peer's Wireguard PRIVATE key
|
||||||
|
myPrivateKey, err := wgtypes.ParseKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed parsing Wireguard key %s: [%s]", privateKey, err.Error())
|
||||||
|
return DeviceAuthorizationFlow{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var mgmTLSEnabled bool
|
||||||
|
if mgmURL.Scheme == "https" {
|
||||||
|
mgmTLSEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("connecting to Management Service %s", mgmURL.String())
|
||||||
|
mgmClient, err := mgm.NewClient(ctx, mgmURL.Host, myPrivateKey, mgmTLSEnabled)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed connecting to Management Service %s %v", mgmURL.String(), err)
|
||||||
|
return DeviceAuthorizationFlow{}, err
|
||||||
|
}
|
||||||
|
log.Debugf("connected to the Management service %s", mgmURL.String())
|
||||||
|
defer func() {
|
||||||
|
err = mgmClient.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("failed to close the Management service client %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
serverKey, err := mgmClient.GetServerPublicKey()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed while getting Management Service public key: %v", err)
|
||||||
|
return DeviceAuthorizationFlow{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
protoDeviceAuthorizationFlow, err := mgmClient.GetDeviceAuthorizationFlow(*serverKey)
|
||||||
|
if err != nil {
|
||||||
|
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
|
||||||
|
log.Warnf("server couldn't find device flow, contact admin: %v", err)
|
||||||
|
return DeviceAuthorizationFlow{}, err
|
||||||
|
}
|
||||||
|
log.Errorf("failed to retrieve device flow: %v", err)
|
||||||
|
return DeviceAuthorizationFlow{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceAuthorizationFlow := DeviceAuthorizationFlow{
|
||||||
|
Provider: protoDeviceAuthorizationFlow.Provider.String(),
|
||||||
|
|
||||||
|
ProviderConfig: ProviderConfig{
|
||||||
|
Audience: protoDeviceAuthorizationFlow.GetProviderConfig().GetAudience(),
|
||||||
|
ClientID: protoDeviceAuthorizationFlow.GetProviderConfig().GetClientID(),
|
||||||
|
ClientSecret: protoDeviceAuthorizationFlow.GetProviderConfig().GetClientSecret(),
|
||||||
|
Domain: protoDeviceAuthorizationFlow.GetProviderConfig().Domain,
|
||||||
|
TokenEndpoint: protoDeviceAuthorizationFlow.GetProviderConfig().GetTokenEndpoint(),
|
||||||
|
DeviceAuthEndpoint: protoDeviceAuthorizationFlow.GetProviderConfig().GetDeviceAuthEndpoint(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = isProviderConfigValid(deviceAuthorizationFlow.ProviderConfig)
|
||||||
|
if err != nil {
|
||||||
|
return DeviceAuthorizationFlow{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return deviceAuthorizationFlow, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isProviderConfigValid(config ProviderConfig) error {
|
||||||
|
errorMSGFormat := "invalid provider configuration received from management: %s value is empty. Contact your NetBird administrator"
|
||||||
|
if config.Audience == "" {
|
||||||
|
return fmt.Errorf(errorMSGFormat, "Audience")
|
||||||
|
}
|
||||||
|
if config.ClientID == "" {
|
||||||
|
return fmt.Errorf(errorMSGFormat, "Client ID")
|
||||||
|
}
|
||||||
|
if config.TokenEndpoint == "" {
|
||||||
|
return fmt.Errorf(errorMSGFormat, "Token Endpoint")
|
||||||
|
}
|
||||||
|
if config.DeviceAuthEndpoint == "" {
|
||||||
|
return fmt.Errorf(errorMSGFormat, "Device Auth Endpoint")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -12,24 +12,23 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/dns"
|
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
|
||||||
nbssh "github.com/netbirdio/netbird/client/ssh"
|
|
||||||
nbstatus "github.com/netbirdio/netbird/client/status"
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
|
||||||
"github.com/netbirdio/netbird/route"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
|
||||||
"github.com/netbirdio/netbird/client/internal/proxy"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
mgm "github.com/netbirdio/netbird/management/client"
|
|
||||||
mgmProto "github.com/netbirdio/netbird/management/proto"
|
|
||||||
signal "github.com/netbirdio/netbird/signal/client"
|
|
||||||
sProto "github.com/netbirdio/netbird/signal/proto"
|
|
||||||
"github.com/netbirdio/netbird/util"
|
|
||||||
"github.com/pion/ice/v2"
|
"github.com/pion/ice/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/dns"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/proxy"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||||
|
nbssh "github.com/netbirdio/netbird/client/ssh"
|
||||||
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
mgm "github.com/netbirdio/netbird/management/client"
|
||||||
|
mgmProto "github.com/netbirdio/netbird/management/proto"
|
||||||
|
"github.com/netbirdio/netbird/route"
|
||||||
|
signal "github.com/netbirdio/netbird/signal/client"
|
||||||
|
sProto "github.com/netbirdio/netbird/signal/proto"
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PeerConnectionTimeoutMax is a timeout of an initial connection attempt to a remote peer.
|
// PeerConnectionTimeoutMax is a timeout of an initial connection attempt to a remote peer.
|
||||||
@ -109,7 +108,7 @@ type Engine struct {
|
|||||||
sshServerFunc func(hostKeyPEM []byte, addr string) (nbssh.Server, error)
|
sshServerFunc func(hostKeyPEM []byte, addr string) (nbssh.Server, error)
|
||||||
sshServer nbssh.Server
|
sshServer nbssh.Server
|
||||||
|
|
||||||
statusRecorder *nbstatus.Status
|
statusRecorder *peer.Status
|
||||||
|
|
||||||
routeManager routemanager.Manager
|
routeManager routemanager.Manager
|
||||||
|
|
||||||
@ -126,14 +125,14 @@ type Peer struct {
|
|||||||
func NewEngine(
|
func NewEngine(
|
||||||
ctx context.Context, cancel context.CancelFunc,
|
ctx context.Context, cancel context.CancelFunc,
|
||||||
signalClient signal.Client, mgmClient mgm.Client,
|
signalClient signal.Client, mgmClient mgm.Client,
|
||||||
config *EngineConfig, statusRecorder *nbstatus.Status,
|
config *EngineConfig, statusRecorder *peer.Status,
|
||||||
) *Engine {
|
) *Engine {
|
||||||
return &Engine{
|
return &Engine{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
signal: signalClient,
|
signal: signalClient,
|
||||||
mgmClient: mgmClient,
|
mgmClient: mgmClient,
|
||||||
peerConns: map[string]*peer.Conn{},
|
peerConns: make(map[string]*peer.Conn),
|
||||||
syncMsgMux: &sync.Mutex{},
|
syncMsgMux: &sync.Mutex{},
|
||||||
config: config,
|
config: config,
|
||||||
STUNs: []*ice.URL{},
|
STUNs: []*ice.URL{},
|
||||||
@ -338,42 +337,6 @@ func (e *Engine) removePeer(peerKey string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPeerConnectionStatus returns a connection Status or nil if peer connection wasn't found
|
|
||||||
func (e *Engine) GetPeerConnectionStatus(peerKey string) peer.ConnStatus {
|
|
||||||
conn, exists := e.peerConns[peerKey]
|
|
||||||
if exists && conn != nil {
|
|
||||||
return conn.Status()
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Engine) GetPeers() []string {
|
|
||||||
e.syncMsgMux.Lock()
|
|
||||||
defer e.syncMsgMux.Unlock()
|
|
||||||
|
|
||||||
peers := []string{}
|
|
||||||
for s := range e.peerConns {
|
|
||||||
peers = append(peers, s)
|
|
||||||
}
|
|
||||||
return peers
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetConnectedPeers returns a connection Status or nil if peer connection wasn't found
|
|
||||||
func (e *Engine) GetConnectedPeers() []string {
|
|
||||||
e.syncMsgMux.Lock()
|
|
||||||
defer e.syncMsgMux.Unlock()
|
|
||||||
|
|
||||||
peers := []string{}
|
|
||||||
for s, conn := range e.peerConns {
|
|
||||||
if conn.Status() == peer.StatusConnected {
|
|
||||||
peers = append(peers, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return peers
|
|
||||||
}
|
|
||||||
|
|
||||||
func signalCandidate(candidate ice.Candidate, myKey wgtypes.Key, remoteKey wgtypes.Key, s signal.Client) error {
|
func signalCandidate(candidate ice.Candidate, myKey wgtypes.Key, remoteKey wgtypes.Key, s signal.Client) error {
|
||||||
err := s.Send(&sProto.Message{
|
err := s.Send(&sProto.Message{
|
||||||
Key: myKey.PublicKey().String(),
|
Key: myKey.PublicKey().String(),
|
||||||
@ -509,7 +472,7 @@ func (e *Engine) updateConfig(conf *mgmProto.PeerConfig) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
e.statusRecorder.UpdateLocalPeerState(nbstatus.LocalPeerState{
|
e.statusRecorder.UpdateLocalPeerState(peer.LocalPeerState{
|
||||||
IP: e.config.WgAddr,
|
IP: e.config.WgAddr,
|
||||||
PubKey: e.config.WgPrivateKey.PublicKey().String(),
|
PubKey: e.config.WgPrivateKey.PublicKey().String(),
|
||||||
KernelInterface: iface.WireguardModuleIsLoaded(),
|
KernelInterface: iface.WireguardModuleIsLoaded(),
|
||||||
@ -594,6 +557,8 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error {
|
|||||||
|
|
||||||
log.Debugf("got peers update from Management Service, total peers to connect to = %d", len(networkMap.GetRemotePeers()))
|
log.Debugf("got peers update from Management Service, total peers to connect to = %d", len(networkMap.GetRemotePeers()))
|
||||||
|
|
||||||
|
e.updateOfflinePeers(networkMap.GetOfflinePeers())
|
||||||
|
|
||||||
// cleanup request, most likely our peer has been deleted
|
// cleanup request, most likely our peer has been deleted
|
||||||
if networkMap.GetRemotePeersIsEmpty() {
|
if networkMap.GetRemotePeersIsEmpty() {
|
||||||
err := e.removeAllPeers()
|
err := e.removeAllPeers()
|
||||||
@ -710,6 +675,21 @@ func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig) nbdns.Config {
|
|||||||
return dnsUpdate
|
return dnsUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Engine) updateOfflinePeers(offlinePeers []*mgmProto.RemotePeerConfig) {
|
||||||
|
replacement := make([]peer.State, len(offlinePeers))
|
||||||
|
for i, offlinePeer := range offlinePeers {
|
||||||
|
log.Debugf("added offline peer %s", offlinePeer.Fqdn)
|
||||||
|
replacement[i] = peer.State{
|
||||||
|
IP: strings.Join(offlinePeer.GetAllowedIps(), ","),
|
||||||
|
PubKey: offlinePeer.GetWgPubKey(),
|
||||||
|
FQDN: offlinePeer.GetFqdn(),
|
||||||
|
ConnStatus: peer.StatusDisconnected,
|
||||||
|
ConnStatusUpdate: time.Now(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.statusRecorder.ReplaceOfflinePeers(replacement)
|
||||||
|
}
|
||||||
|
|
||||||
// addNewPeers adds peers that were not know before but arrived from the Management service with the update
|
// addNewPeers adds peers that were not know before but arrived from the Management service with the update
|
||||||
func (e *Engine) addNewPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
func (e *Engine) addNewPeers(peersUpdate []*mgmProto.RemotePeerConfig) error {
|
||||||
for _, p := range peersUpdate {
|
for _, p := range peersUpdate {
|
||||||
|
@ -3,16 +3,6 @@ package internal
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/netbirdio/netbird/client/internal/dns"
|
|
||||||
"github.com/netbirdio/netbird/client/internal/routemanager"
|
|
||||||
"github.com/netbirdio/netbird/client/ssh"
|
|
||||||
nbstatus "github.com/netbirdio/netbird/client/status"
|
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
|
||||||
"github.com/netbirdio/netbird/route"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
@ -23,18 +13,29 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/keepalive"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/dns"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/routemanager"
|
||||||
|
"github.com/netbirdio/netbird/client/ssh"
|
||||||
"github.com/netbirdio/netbird/client/system"
|
"github.com/netbirdio/netbird/client/system"
|
||||||
|
nbdns "github.com/netbirdio/netbird/dns"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
mgmt "github.com/netbirdio/netbird/management/client"
|
mgmt "github.com/netbirdio/netbird/management/client"
|
||||||
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
mgmtProto "github.com/netbirdio/netbird/management/proto"
|
||||||
"github.com/netbirdio/netbird/management/server"
|
"github.com/netbirdio/netbird/management/server"
|
||||||
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/route"
|
||||||
signal "github.com/netbirdio/netbird/signal/client"
|
signal "github.com/netbirdio/netbird/signal/client"
|
||||||
"github.com/netbirdio/netbird/signal/proto"
|
"github.com/netbirdio/netbird/signal/proto"
|
||||||
signalServer "github.com/netbirdio/netbird/signal/server"
|
signalServer "github.com/netbirdio/netbird/signal/server"
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"google.golang.org/grpc/keepalive"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -71,7 +72,7 @@ func TestEngine_SSH(t *testing.T) {
|
|||||||
WgAddr: "100.64.0.1/24",
|
WgAddr: "100.64.0.1/24",
|
||||||
WgPrivateKey: key,
|
WgPrivateKey: key,
|
||||||
WgPort: 33100,
|
WgPort: 33100,
|
||||||
}, nbstatus.NewRecorder())
|
}, peer.NewRecorder())
|
||||||
|
|
||||||
engine.dnsServer = &dns.MockServer{
|
engine.dnsServer = &dns.MockServer{
|
||||||
UpdateDNSServerFunc: func(serial uint64, update nbdns.Config) error { return nil },
|
UpdateDNSServerFunc: func(serial uint64, update nbdns.Config) error { return nil },
|
||||||
@ -205,7 +206,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) {
|
|||||||
WgAddr: "100.64.0.1/24",
|
WgAddr: "100.64.0.1/24",
|
||||||
WgPrivateKey: key,
|
WgPrivateKey: key,
|
||||||
WgPort: 33100,
|
WgPort: 33100,
|
||||||
}, nbstatus.NewRecorder())
|
}, peer.NewRecorder())
|
||||||
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", iface.DefaultMTU)
|
engine.wgInterface, err = iface.NewWGIFace("utun102", "100.64.0.1/24", iface.DefaultMTU)
|
||||||
engine.routeManager = routemanager.NewManager(ctx, key.PublicKey().String(), engine.wgInterface, engine.statusRecorder)
|
engine.routeManager = routemanager.NewManager(ctx, key.PublicKey().String(), engine.wgInterface, engine.statusRecorder)
|
||||||
engine.dnsServer = &dns.MockServer{
|
engine.dnsServer = &dns.MockServer{
|
||||||
@ -389,7 +390,7 @@ func TestEngine_Sync(t *testing.T) {
|
|||||||
WgAddr: "100.64.0.1/24",
|
WgAddr: "100.64.0.1/24",
|
||||||
WgPrivateKey: key,
|
WgPrivateKey: key,
|
||||||
WgPort: 33100,
|
WgPort: 33100,
|
||||||
}, nbstatus.NewRecorder())
|
}, peer.NewRecorder())
|
||||||
|
|
||||||
engine.dnsServer = &dns.MockServer{
|
engine.dnsServer = &dns.MockServer{
|
||||||
UpdateDNSServerFunc: func(serial uint64, update nbdns.Config) error { return nil },
|
UpdateDNSServerFunc: func(serial uint64, update nbdns.Config) error { return nil },
|
||||||
@ -439,7 +440,7 @@ func TestEngine_Sync(t *testing.T) {
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(engine.GetPeers()) == 3 && engine.networkSerial == 10 {
|
if getPeers(engine) == 3 && engine.networkSerial == 10 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -547,7 +548,7 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) {
|
|||||||
WgAddr: wgAddr,
|
WgAddr: wgAddr,
|
||||||
WgPrivateKey: key,
|
WgPrivateKey: key,
|
||||||
WgPort: 33100,
|
WgPort: 33100,
|
||||||
}, nbstatus.NewRecorder())
|
}, peer.NewRecorder())
|
||||||
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU)
|
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
input := struct {
|
input := struct {
|
||||||
@ -712,7 +713,7 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) {
|
|||||||
WgAddr: wgAddr,
|
WgAddr: wgAddr,
|
||||||
WgPrivateKey: key,
|
WgPrivateKey: key,
|
||||||
WgPort: 33100,
|
WgPort: 33100,
|
||||||
}, nbstatus.NewRecorder())
|
}, peer.NewRecorder())
|
||||||
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU)
|
engine.wgInterface, err = iface.NewWGIFace(wgIfaceName, wgAddr, iface.DefaultMTU)
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
@ -846,7 +847,7 @@ loop:
|
|||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
totalConnected := 0
|
totalConnected := 0
|
||||||
for _, engine := range engines {
|
for _, engine := range engines {
|
||||||
totalConnected = totalConnected + len(engine.GetConnectedPeers())
|
totalConnected = totalConnected + getConnectedPeers(engine)
|
||||||
}
|
}
|
||||||
if totalConnected == expectedConnected {
|
if totalConnected == expectedConnected {
|
||||||
log.Infof("total connected=%d", totalConnected)
|
log.Infof("total connected=%d", totalConnected)
|
||||||
@ -977,7 +978,7 @@ func createEngine(ctx context.Context, cancel context.CancelFunc, setupKey strin
|
|||||||
WgPort: wgPort,
|
WgPort: wgPort,
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewEngine(ctx, cancel, signalClient, mgmtClient, conf, nbstatus.NewRecorder()), nil
|
return NewEngine(ctx, cancel, signalClient, mgmtClient, conf, peer.NewRecorder()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func startSignal() (*grpc.Server, string, error) {
|
func startSignal() (*grpc.Server, string, error) {
|
||||||
@ -1044,3 +1045,23 @@ func startManagement(dataDir string) (*grpc.Server, string, error) {
|
|||||||
|
|
||||||
return s, lis.Addr().String(), nil
|
return s, lis.Addr().String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getConnectedPeers returns a connection Status or nil if peer connection wasn't found
|
||||||
|
func getConnectedPeers(e *Engine) int {
|
||||||
|
e.syncMsgMux.Lock()
|
||||||
|
defer e.syncMsgMux.Unlock()
|
||||||
|
i := 0
|
||||||
|
for _, conn := range e.peerConns {
|
||||||
|
if conn.Status() == peer.StatusConnected {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPeers(e *Engine) int {
|
||||||
|
e.syncMsgMux.Lock()
|
||||||
|
defer e.syncMsgMux.Unlock()
|
||||||
|
|
||||||
|
return len(e.peerConns)
|
||||||
|
}
|
||||||
|
@ -59,13 +59,6 @@ func Login(ctx context.Context, config *Config, setupKey string, jwtToken string
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Infof("peer has successfully logged-in to the Management service %s", config.ManagementURL.String())
|
log.Infof("peer has successfully logged-in to the Management service %s", config.ManagementURL.String())
|
||||||
|
|
||||||
err = mgmClient.Close()
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("failed to close the Management service client: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,13 +7,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/proxy"
|
|
||||||
nbStatus "github.com/netbirdio/netbird/client/status"
|
|
||||||
"github.com/netbirdio/netbird/client/system"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
"github.com/pion/ice/v2"
|
"github.com/pion/ice/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl"
|
"golang.zx2c4.com/wireguard/wgctrl"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/proxy"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConnConfig is a peer Connection configuration
|
// ConnConfig is a peer Connection configuration
|
||||||
@ -83,7 +83,7 @@ type Conn struct {
|
|||||||
agent *ice.Agent
|
agent *ice.Agent
|
||||||
status ConnStatus
|
status ConnStatus
|
||||||
|
|
||||||
statusRecorder *nbStatus.Status
|
statusRecorder *Status
|
||||||
|
|
||||||
proxy proxy.Proxy
|
proxy proxy.Proxy
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ func (conn *Conn) UpdateConf(conf ConnConfig) {
|
|||||||
|
|
||||||
// NewConn creates a new not opened Conn to the remote peer.
|
// NewConn creates a new not opened Conn to the remote peer.
|
||||||
// To establish a connection run Conn.Open
|
// To establish a connection run Conn.Open
|
||||||
func NewConn(config ConnConfig, statusRecorder *nbStatus.Status) (*Conn, error) {
|
func NewConn(config ConnConfig, statusRecorder *Status) (*Conn, error) {
|
||||||
return &Conn{
|
return &Conn{
|
||||||
config: config,
|
config: config,
|
||||||
mu: sync.Mutex{},
|
mu: sync.Mutex{},
|
||||||
@ -190,11 +190,11 @@ func (conn *Conn) reCreateAgent() error {
|
|||||||
func (conn *Conn) Open() error {
|
func (conn *Conn) Open() error {
|
||||||
log.Debugf("trying to connect to peer %s", conn.config.Key)
|
log.Debugf("trying to connect to peer %s", conn.config.Key)
|
||||||
|
|
||||||
peerState := nbStatus.PeerState{PubKey: conn.config.Key}
|
peerState := State{PubKey: conn.config.Key}
|
||||||
|
|
||||||
peerState.IP = strings.Split(conn.config.ProxyConfig.AllowedIps, "/")[0]
|
peerState.IP = strings.Split(conn.config.ProxyConfig.AllowedIps, "/")[0]
|
||||||
peerState.ConnStatusUpdate = time.Now()
|
peerState.ConnStatusUpdate = time.Now()
|
||||||
peerState.ConnStatus = conn.status.String()
|
peerState.ConnStatus = conn.status
|
||||||
|
|
||||||
err := conn.statusRecorder.UpdatePeerState(peerState)
|
err := conn.statusRecorder.UpdatePeerState(peerState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -250,9 +250,9 @@ func (conn *Conn) Open() error {
|
|||||||
defer conn.notifyDisconnected()
|
defer conn.notifyDisconnected()
|
||||||
conn.mu.Unlock()
|
conn.mu.Unlock()
|
||||||
|
|
||||||
peerState = nbStatus.PeerState{PubKey: conn.config.Key}
|
peerState = State{PubKey: conn.config.Key}
|
||||||
|
|
||||||
peerState.ConnStatus = conn.status.String()
|
peerState.ConnStatus = conn.status
|
||||||
peerState.ConnStatusUpdate = time.Now()
|
peerState.ConnStatusUpdate = time.Now()
|
||||||
err = conn.statusRecorder.UpdatePeerState(peerState)
|
err = conn.statusRecorder.UpdatePeerState(peerState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -310,38 +310,44 @@ func (conn *Conn) Open() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// useProxy determines whether a direct connection (without a go proxy) is possible
|
// useProxy determines whether a direct connection (without a go proxy) is possible
|
||||||
// There are 3 cases: one of the peers has a public IP or both peers are in the same private network
|
//
|
||||||
|
// There are 2 cases:
|
||||||
|
//
|
||||||
|
// * When neither candidate is from hard nat and one of the peers has a public IP
|
||||||
|
//
|
||||||
|
// * both peers are in the same private network
|
||||||
|
//
|
||||||
// Please note, that this check happens when peers were already able to ping each other using ICE layer.
|
// Please note, that this check happens when peers were already able to ping each other using ICE layer.
|
||||||
func shouldUseProxy(pair *ice.CandidatePair) bool {
|
func shouldUseProxy(pair *ice.CandidatePair) bool {
|
||||||
remoteIP := net.ParseIP(pair.Remote.Address())
|
if !isHardNATCandidate(pair.Local) && isHostCandidateWithPublicIP(pair.Remote) {
|
||||||
myIp := net.ParseIP(pair.Local.Address())
|
|
||||||
remoteIsPublic := IsPublicIP(remoteIP)
|
|
||||||
myIsPublic := IsPublicIP(myIp)
|
|
||||||
|
|
||||||
if pair.Local.Type() == ice.CandidateTypeRelay || pair.Remote.Type() == ice.CandidateTypeRelay {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
//one of the hosts has a public IP
|
|
||||||
if remoteIsPublic && pair.Remote.Type() == ice.CandidateTypeHost {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if myIsPublic && pair.Local.Type() == ice.CandidateTypeHost {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if pair.Local.Type() == ice.CandidateTypeHost && pair.Remote.Type() == ice.CandidateTypeHost {
|
if !isHardNATCandidate(pair.Remote) && isHostCandidateWithPublicIP(pair.Local) {
|
||||||
if !remoteIsPublic && !myIsPublic {
|
return false
|
||||||
//both hosts are in the same private network
|
}
|
||||||
return false
|
|
||||||
}
|
if isHostCandidateWithPrivateIP(pair.Local) && isHostCandidateWithPrivateIP(pair.Remote) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPublicIP indicates whether IP is public or not.
|
func isHardNATCandidate(candidate ice.Candidate) bool {
|
||||||
func IsPublicIP(ip net.IP) bool {
|
return candidate.Type() == ice.CandidateTypeRelay || candidate.Type() == ice.CandidateTypePeerReflexive
|
||||||
|
}
|
||||||
|
|
||||||
|
func isHostCandidateWithPublicIP(candidate ice.Candidate) bool {
|
||||||
|
return candidate.Type() == ice.CandidateTypeHost && isPublicIP(candidate.Address())
|
||||||
|
}
|
||||||
|
|
||||||
|
func isHostCandidateWithPrivateIP(candidate ice.Candidate) bool {
|
||||||
|
return candidate.Type() == ice.CandidateTypeHost && !isPublicIP(candidate.Address())
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPublicIP(address string) bool {
|
||||||
|
ip := net.ParseIP(address)
|
||||||
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsPrivate() {
|
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsPrivate() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -359,7 +365,7 @@ func (conn *Conn) startProxy(remoteConn net.Conn, remoteWgPort int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
peerState := nbStatus.PeerState{PubKey: conn.config.Key}
|
peerState := State{PubKey: conn.config.Key}
|
||||||
useProxy := shouldUseProxy(pair)
|
useProxy := shouldUseProxy(pair)
|
||||||
var p proxy.Proxy
|
var p proxy.Proxy
|
||||||
if useProxy {
|
if useProxy {
|
||||||
@ -377,7 +383,7 @@ func (conn *Conn) startProxy(remoteConn net.Conn, remoteWgPort int) error {
|
|||||||
|
|
||||||
conn.status = StatusConnected
|
conn.status = StatusConnected
|
||||||
|
|
||||||
peerState.ConnStatus = conn.status.String()
|
peerState.ConnStatus = conn.status
|
||||||
peerState.ConnStatusUpdate = time.Now()
|
peerState.ConnStatusUpdate = time.Now()
|
||||||
peerState.LocalIceCandidateType = pair.Local.Type().String()
|
peerState.LocalIceCandidateType = pair.Local.Type().String()
|
||||||
peerState.RemoteIceCandidateType = pair.Remote.Type().String()
|
peerState.RemoteIceCandidateType = pair.Remote.Type().String()
|
||||||
@ -422,8 +428,8 @@ func (conn *Conn) cleanup() error {
|
|||||||
|
|
||||||
conn.status = StatusDisconnected
|
conn.status = StatusDisconnected
|
||||||
|
|
||||||
peerState := nbStatus.PeerState{PubKey: conn.config.Key}
|
peerState := State{PubKey: conn.config.Key}
|
||||||
peerState.ConnStatus = conn.status.String()
|
peerState.ConnStatus = conn.status
|
||||||
peerState.ConnStatusUpdate = time.Now()
|
peerState.ConnStatusUpdate = time.Now()
|
||||||
|
|
||||||
err := conn.statusRecorder.UpdatePeerState(peerState)
|
err := conn.statusRecorder.UpdatePeerState(peerState)
|
||||||
@ -494,7 +500,7 @@ func (conn *Conn) sendAnswer() error {
|
|||||||
err = conn.signalAnswer(OfferAnswer{
|
err = conn.signalAnswer(OfferAnswer{
|
||||||
IceCredentials: IceCredentials{localUFrag, localPwd},
|
IceCredentials: IceCredentials{localUFrag, localPwd},
|
||||||
WgListenPort: conn.config.LocalWgPort,
|
WgListenPort: conn.config.LocalWgPort,
|
||||||
Version: system.NetbirdVersion(),
|
Version: version.NetbirdVersion(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -515,7 +521,7 @@ func (conn *Conn) sendOffer() error {
|
|||||||
err = conn.signalOffer(OfferAnswer{
|
err = conn.signalOffer(OfferAnswer{
|
||||||
IceCredentials: IceCredentials{localUFrag, localPwd},
|
IceCredentials: IceCredentials{localUFrag, localPwd},
|
||||||
WgListenPort: conn.config.LocalWgPort,
|
WgListenPort: conn.config.LocalWgPort,
|
||||||
Version: system.NetbirdVersion(),
|
Version: version.NetbirdVersion(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
29
client/internal/peer/conn_status.go
Normal file
29
client/internal/peer/conn_status.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package peer
|
||||||
|
|
||||||
|
import log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// StatusConnected indicate the peer is in connected state
|
||||||
|
StatusConnected ConnStatus = iota
|
||||||
|
// StatusConnecting indicate the peer is in connecting state
|
||||||
|
StatusConnecting
|
||||||
|
// StatusDisconnected indicate the peer is in disconnected state
|
||||||
|
StatusDisconnected
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnStatus describe the status of a peer's connection
|
||||||
|
type ConnStatus int
|
||||||
|
|
||||||
|
func (s ConnStatus) String() string {
|
||||||
|
switch s {
|
||||||
|
case StatusConnecting:
|
||||||
|
return "Connecting"
|
||||||
|
case StatusConnected:
|
||||||
|
return "Connected"
|
||||||
|
case StatusDisconnected:
|
||||||
|
return "Disconnected"
|
||||||
|
default:
|
||||||
|
log.Errorf("unknown status: %d", s)
|
||||||
|
return "INVALID_PEER_CONNECTION_STATUS"
|
||||||
|
}
|
||||||
|
}
|
27
client/internal/peer/conn_status_test.go
Normal file
27
client/internal/peer/conn_status_test.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package peer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/magiconair/properties/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConnStatus_String(t *testing.T) {
|
||||||
|
|
||||||
|
tables := []struct {
|
||||||
|
name string
|
||||||
|
status ConnStatus
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"StatusConnected", StatusConnected, "Connected"},
|
||||||
|
{"StatusDisconnected", StatusDisconnected, "Disconnected"},
|
||||||
|
{"StatusConnecting", StatusConnecting, "Connecting"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, table := range tables {
|
||||||
|
t.Run(table.name, func(t *testing.T) {
|
||||||
|
got := table.status.String()
|
||||||
|
assert.Equal(t, got, table.want, "they should be equal")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,14 +1,15 @@
|
|||||||
package peer
|
package peer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/magiconair/properties/assert"
|
|
||||||
"github.com/netbirdio/netbird/client/internal/proxy"
|
|
||||||
nbstatus "github.com/netbirdio/netbird/client/status"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
|
||||||
"github.com/pion/ice/v2"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/magiconair/properties/assert"
|
||||||
|
"github.com/pion/ice/v2"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/proxy"
|
||||||
|
"github.com/netbirdio/netbird/iface"
|
||||||
)
|
)
|
||||||
|
|
||||||
var connConf = ConnConfig{
|
var connConf = ConnConfig{
|
||||||
@ -46,7 +47,7 @@ func TestConn_GetKey(t *testing.T) {
|
|||||||
|
|
||||||
func TestConn_OnRemoteOffer(t *testing.T) {
|
func TestConn_OnRemoteOffer(t *testing.T) {
|
||||||
|
|
||||||
conn, err := NewConn(connConf, nbstatus.NewRecorder())
|
conn, err := NewConn(connConf, NewRecorder())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -80,7 +81,7 @@ func TestConn_OnRemoteOffer(t *testing.T) {
|
|||||||
|
|
||||||
func TestConn_OnRemoteAnswer(t *testing.T) {
|
func TestConn_OnRemoteAnswer(t *testing.T) {
|
||||||
|
|
||||||
conn, err := NewConn(connConf, nbstatus.NewRecorder())
|
conn, err := NewConn(connConf, NewRecorder())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -113,7 +114,7 @@ func TestConn_OnRemoteAnswer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
func TestConn_Status(t *testing.T) {
|
func TestConn_Status(t *testing.T) {
|
||||||
|
|
||||||
conn, err := NewConn(connConf, nbstatus.NewRecorder())
|
conn, err := NewConn(connConf, NewRecorder())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -140,7 +141,7 @@ func TestConn_Status(t *testing.T) {
|
|||||||
|
|
||||||
func TestConn_Close(t *testing.T) {
|
func TestConn_Close(t *testing.T) {
|
||||||
|
|
||||||
conn, err := NewConn(connConf, nbstatus.NewRecorder())
|
conn, err := NewConn(connConf, NewRecorder())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -165,3 +166,166 @@ func TestConn_Close(t *testing.T) {
|
|||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockICECandidate struct {
|
||||||
|
ice.Candidate
|
||||||
|
AddressFunc func() string
|
||||||
|
TypeFunc func() ice.CandidateType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Address mocks and overwrite ice.Candidate Address method
|
||||||
|
func (m *mockICECandidate) Address() string {
|
||||||
|
if m.AddressFunc != nil {
|
||||||
|
return m.AddressFunc()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type mocks and overwrite ice.Candidate Type method
|
||||||
|
func (m *mockICECandidate) Type() ice.CandidateType {
|
||||||
|
if m.TypeFunc != nil {
|
||||||
|
return m.TypeFunc()
|
||||||
|
}
|
||||||
|
return ice.CandidateTypeUnspecified
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConn_ShouldUseProxy(t *testing.T) {
|
||||||
|
publicHostCandidate := &mockICECandidate{
|
||||||
|
AddressFunc: func() string {
|
||||||
|
return "8.8.8.8"
|
||||||
|
},
|
||||||
|
TypeFunc: func() ice.CandidateType {
|
||||||
|
return ice.CandidateTypeHost
|
||||||
|
},
|
||||||
|
}
|
||||||
|
privateHostCandidate := &mockICECandidate{
|
||||||
|
AddressFunc: func() string {
|
||||||
|
return "10.0.0.1"
|
||||||
|
},
|
||||||
|
TypeFunc: func() ice.CandidateType {
|
||||||
|
return ice.CandidateTypeHost
|
||||||
|
},
|
||||||
|
}
|
||||||
|
srflxCandidate := &mockICECandidate{
|
||||||
|
AddressFunc: func() string {
|
||||||
|
return "1.1.1.1"
|
||||||
|
},
|
||||||
|
TypeFunc: func() ice.CandidateType {
|
||||||
|
return ice.CandidateTypeServerReflexive
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
prflxCandidate := &mockICECandidate{
|
||||||
|
AddressFunc: func() string {
|
||||||
|
return "1.1.1.1"
|
||||||
|
},
|
||||||
|
TypeFunc: func() ice.CandidateType {
|
||||||
|
return ice.CandidateTypePeerReflexive
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
relayCandidate := &mockICECandidate{
|
||||||
|
AddressFunc: func() string {
|
||||||
|
return "1.1.1.1"
|
||||||
|
},
|
||||||
|
TypeFunc: func() ice.CandidateType {
|
||||||
|
return ice.CandidateTypeRelay
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
candatePair *ice.CandidatePair
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Use Proxy When Local Candidate Is Relay",
|
||||||
|
candatePair: &ice.CandidatePair{
|
||||||
|
Local: relayCandidate,
|
||||||
|
Remote: privateHostCandidate,
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Use Proxy When Remote Candidate Is Relay",
|
||||||
|
candatePair: &ice.CandidatePair{
|
||||||
|
Local: privateHostCandidate,
|
||||||
|
Remote: relayCandidate,
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Use Proxy When Local Candidate Is Peer Reflexive",
|
||||||
|
candatePair: &ice.CandidatePair{
|
||||||
|
Local: prflxCandidate,
|
||||||
|
Remote: privateHostCandidate,
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Use Proxy When Remote Candidate Is Peer Reflexive",
|
||||||
|
candatePair: &ice.CandidatePair{
|
||||||
|
Local: privateHostCandidate,
|
||||||
|
Remote: prflxCandidate,
|
||||||
|
},
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Don't Use Proxy When Local Candidate Is Public And Remote Is Private",
|
||||||
|
candatePair: &ice.CandidatePair{
|
||||||
|
Local: publicHostCandidate,
|
||||||
|
Remote: privateHostCandidate,
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Don't Use Proxy When Remote Candidate Is Public And Local Is Private",
|
||||||
|
candatePair: &ice.CandidatePair{
|
||||||
|
Local: privateHostCandidate,
|
||||||
|
Remote: publicHostCandidate,
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Don't Use Proxy When Local Candidate is Public And Remote Is Server Reflexive",
|
||||||
|
candatePair: &ice.CandidatePair{
|
||||||
|
Local: publicHostCandidate,
|
||||||
|
Remote: srflxCandidate,
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Don't Use Proxy When Remote Candidate is Public And Local Is Server Reflexive",
|
||||||
|
candatePair: &ice.CandidatePair{
|
||||||
|
Local: srflxCandidate,
|
||||||
|
Remote: publicHostCandidate,
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Don't Use Proxy When Both Candidates Are Public",
|
||||||
|
candatePair: &ice.CandidatePair{
|
||||||
|
Local: publicHostCandidate,
|
||||||
|
Remote: publicHostCandidate,
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Don't Use Proxy When Both Candidates Are Private",
|
||||||
|
candatePair: &ice.CandidatePair{
|
||||||
|
Local: privateHostCandidate,
|
||||||
|
Remote: privateHostCandidate,
|
||||||
|
},
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
result := shouldUseProxy(testCase.candatePair)
|
||||||
|
if result != testCase.expected {
|
||||||
|
t.Errorf("got a different result. Expected %t Got %t", testCase.expected, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,25 +1,253 @@
|
|||||||
package peer
|
package peer
|
||||||
|
|
||||||
import log "github.com/sirupsen/logrus"
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type ConnStatus int
|
// State contains the latest state of a peer
|
||||||
|
type State struct {
|
||||||
|
IP string
|
||||||
|
PubKey string
|
||||||
|
FQDN string
|
||||||
|
ConnStatus ConnStatus
|
||||||
|
ConnStatusUpdate time.Time
|
||||||
|
Relayed bool
|
||||||
|
Direct bool
|
||||||
|
LocalIceCandidateType string
|
||||||
|
RemoteIceCandidateType string
|
||||||
|
}
|
||||||
|
|
||||||
func (s ConnStatus) String() string {
|
// LocalPeerState contains the latest state of the local peer
|
||||||
switch s {
|
type LocalPeerState struct {
|
||||||
case StatusConnecting:
|
IP string
|
||||||
return "Connecting"
|
PubKey string
|
||||||
case StatusConnected:
|
KernelInterface bool
|
||||||
return "Connected"
|
FQDN string
|
||||||
case StatusDisconnected:
|
}
|
||||||
return "Disconnected"
|
|
||||||
default:
|
// SignalState contains the latest state of a signal connection
|
||||||
log.Errorf("unknown status: %d", s)
|
type SignalState struct {
|
||||||
return "INVALID_PEER_CONNECTION_STATUS"
|
URL string
|
||||||
|
Connected bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ManagementState contains the latest state of a management connection
|
||||||
|
type ManagementState struct {
|
||||||
|
URL string
|
||||||
|
Connected bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullStatus contains the full state held by the Status instance
|
||||||
|
type FullStatus struct {
|
||||||
|
Peers []State
|
||||||
|
ManagementState ManagementState
|
||||||
|
SignalState SignalState
|
||||||
|
LocalPeerState LocalPeerState
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status holds a state of peers, signal and management connections
|
||||||
|
type Status struct {
|
||||||
|
mux sync.Mutex
|
||||||
|
peers map[string]State
|
||||||
|
changeNotify map[string]chan struct{}
|
||||||
|
signal SignalState
|
||||||
|
management ManagementState
|
||||||
|
localPeer LocalPeerState
|
||||||
|
offlinePeers []State
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRecorder returns a new Status instance
|
||||||
|
func NewRecorder() *Status {
|
||||||
|
return &Status{
|
||||||
|
peers: make(map[string]State),
|
||||||
|
changeNotify: make(map[string]chan struct{}),
|
||||||
|
offlinePeers: make([]State, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
// ReplaceOfflinePeers replaces
|
||||||
StatusConnected ConnStatus = iota
|
func (d *Status) ReplaceOfflinePeers(replacement []State) {
|
||||||
StatusConnecting
|
d.mux.Lock()
|
||||||
StatusDisconnected
|
defer d.mux.Unlock()
|
||||||
)
|
d.offlinePeers = make([]State, len(replacement))
|
||||||
|
copy(d.offlinePeers, replacement)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPeer adds peer to Daemon status map
|
||||||
|
func (d *Status) AddPeer(peerPubKey string) error {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
_, ok := d.peers[peerPubKey]
|
||||||
|
if ok {
|
||||||
|
return errors.New("peer already exist")
|
||||||
|
}
|
||||||
|
d.peers[peerPubKey] = State{PubKey: peerPubKey, ConnStatus: StatusDisconnected}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeer adds peer to Daemon status map
|
||||||
|
func (d *Status) GetPeer(peerPubKey string) (State, error) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
state, ok := d.peers[peerPubKey]
|
||||||
|
if !ok {
|
||||||
|
return State{}, errors.New("peer not found")
|
||||||
|
}
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemovePeer removes peer from Daemon status map
|
||||||
|
func (d *Status) RemovePeer(peerPubKey string) error {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
_, ok := d.peers[peerPubKey]
|
||||||
|
if ok {
|
||||||
|
delete(d.peers, peerPubKey)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New("no peer with to remove")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePeerState updates peer status
|
||||||
|
func (d *Status) UpdatePeerState(receivedState State) error {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
peerState, ok := d.peers[receivedState.PubKey]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("peer doesn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
if receivedState.IP != "" {
|
||||||
|
peerState.IP = receivedState.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
if receivedState.ConnStatus != peerState.ConnStatus {
|
||||||
|
peerState.ConnStatus = receivedState.ConnStatus
|
||||||
|
peerState.ConnStatusUpdate = receivedState.ConnStatusUpdate
|
||||||
|
peerState.Direct = receivedState.Direct
|
||||||
|
peerState.Relayed = receivedState.Relayed
|
||||||
|
peerState.LocalIceCandidateType = receivedState.LocalIceCandidateType
|
||||||
|
peerState.RemoteIceCandidateType = receivedState.RemoteIceCandidateType
|
||||||
|
}
|
||||||
|
|
||||||
|
d.peers[receivedState.PubKey] = peerState
|
||||||
|
|
||||||
|
ch, found := d.changeNotify[receivedState.PubKey]
|
||||||
|
if found && ch != nil {
|
||||||
|
close(ch)
|
||||||
|
d.changeNotify[receivedState.PubKey] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePeerFQDN update peer's state fqdn only
|
||||||
|
func (d *Status) UpdatePeerFQDN(peerPubKey, fqdn string) error {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
peerState, ok := d.peers[peerPubKey]
|
||||||
|
if !ok {
|
||||||
|
return errors.New("peer doesn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
peerState.FQDN = fqdn
|
||||||
|
d.peers[peerPubKey] = peerState
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeerStateChangeNotifier returns a change notifier channel for a peer
|
||||||
|
func (d *Status) GetPeerStateChangeNotifier(peer string) <-chan struct{} {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
ch, found := d.changeNotify[peer]
|
||||||
|
if !found || ch == nil {
|
||||||
|
ch = make(chan struct{})
|
||||||
|
d.changeNotify[peer] = ch
|
||||||
|
}
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLocalPeerState updates local peer status
|
||||||
|
func (d *Status) UpdateLocalPeerState(localPeerState LocalPeerState) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
d.localPeer = localPeerState
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanLocalPeerState cleans local peer status
|
||||||
|
func (d *Status) CleanLocalPeerState() {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
d.localPeer = LocalPeerState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkManagementDisconnected sets ManagementState to disconnected
|
||||||
|
func (d *Status) MarkManagementDisconnected(managementURL string) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
d.management = ManagementState{
|
||||||
|
URL: managementURL,
|
||||||
|
Connected: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkManagementConnected sets ManagementState to connected
|
||||||
|
func (d *Status) MarkManagementConnected(managementURL string) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
d.management = ManagementState{
|
||||||
|
URL: managementURL,
|
||||||
|
Connected: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkSignalDisconnected sets SignalState to disconnected
|
||||||
|
func (d *Status) MarkSignalDisconnected(signalURL string) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
d.signal = SignalState{
|
||||||
|
signalURL,
|
||||||
|
false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkSignalConnected sets SignalState to connected
|
||||||
|
func (d *Status) MarkSignalConnected(signalURL string) {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
d.signal = SignalState{
|
||||||
|
signalURL,
|
||||||
|
true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFullStatus gets full status
|
||||||
|
func (d *Status) GetFullStatus() FullStatus {
|
||||||
|
d.mux.Lock()
|
||||||
|
defer d.mux.Unlock()
|
||||||
|
|
||||||
|
fullStatus := FullStatus{
|
||||||
|
ManagementState: d.management,
|
||||||
|
SignalState: d.signal,
|
||||||
|
LocalPeerState: d.localPeer,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, status := range d.peers {
|
||||||
|
fullStatus.Peers = append(fullStatus.Peers, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
fullStatus.Peers = append(fullStatus.Peers, d.offlinePeers...)
|
||||||
|
|
||||||
|
return fullStatus
|
||||||
|
}
|
||||||
|
@ -1,27 +1,244 @@
|
|||||||
package peer
|
package peer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/magiconair/properties/assert"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConnStatus_String(t *testing.T) {
|
func TestAddPeer(t *testing.T) {
|
||||||
|
key := "abc"
|
||||||
|
status := NewRecorder()
|
||||||
|
err := status.AddPeer(key)
|
||||||
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
tables := []struct {
|
_, exists := status.peers[key]
|
||||||
name string
|
assert.True(t, exists, "value was found")
|
||||||
status ConnStatus
|
|
||||||
want string
|
err = status.AddPeer(key)
|
||||||
}{
|
|
||||||
{"StatusConnected", StatusConnected, "Connected"},
|
assert.Error(t, err, "should return error on duplicate")
|
||||||
{"StatusDisconnected", StatusDisconnected, "Disconnected"},
|
}
|
||||||
{"StatusConnecting", StatusConnecting, "Connecting"},
|
|
||||||
|
func TestGetPeer(t *testing.T) {
|
||||||
|
key := "abc"
|
||||||
|
status := NewRecorder()
|
||||||
|
err := status.AddPeer(key)
|
||||||
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
|
peerStatus, err := status.GetPeer(key)
|
||||||
|
assert.NoError(t, err, "shouldn't return error on getting peer")
|
||||||
|
|
||||||
|
assert.Equal(t, key, peerStatus.PubKey, "retrieved public key should match")
|
||||||
|
|
||||||
|
_, err = status.GetPeer("non_existing_key")
|
||||||
|
assert.Error(t, err, "should return error when peer doesn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdatePeerState(t *testing.T) {
|
||||||
|
key := "abc"
|
||||||
|
ip := "10.10.10.10"
|
||||||
|
status := NewRecorder()
|
||||||
|
peerState := State{
|
||||||
|
PubKey: key,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, table := range tables {
|
status.peers[key] = peerState
|
||||||
t.Run(table.name, func(t *testing.T) {
|
|
||||||
got := table.status.String()
|
peerState.IP = ip
|
||||||
assert.Equal(t, got, table.want, "they should be equal")
|
|
||||||
|
err := status.UpdatePeerState(peerState)
|
||||||
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
|
state, exists := status.peers[key]
|
||||||
|
assert.True(t, exists, "state should be found")
|
||||||
|
assert.Equal(t, ip, state.IP, "ip should be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatus_UpdatePeerFQDN(t *testing.T) {
|
||||||
|
key := "abc"
|
||||||
|
fqdn := "peer-a.netbird.local"
|
||||||
|
status := NewRecorder()
|
||||||
|
peerState := State{
|
||||||
|
PubKey: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
status.peers[key] = peerState
|
||||||
|
|
||||||
|
err := status.UpdatePeerFQDN(key, fqdn)
|
||||||
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
|
state, exists := status.peers[key]
|
||||||
|
assert.True(t, exists, "state should be found")
|
||||||
|
assert.Equal(t, fqdn, state.FQDN, "fqdn should be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetPeerStateChangeNotifierLogic(t *testing.T) {
|
||||||
|
key := "abc"
|
||||||
|
ip := "10.10.10.10"
|
||||||
|
status := NewRecorder()
|
||||||
|
peerState := State{
|
||||||
|
PubKey: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
status.peers[key] = peerState
|
||||||
|
|
||||||
|
ch := status.GetPeerStateChangeNotifier(key)
|
||||||
|
assert.NotNil(t, ch, "channel shouldn't be nil")
|
||||||
|
|
||||||
|
peerState.IP = ip
|
||||||
|
|
||||||
|
err := status.UpdatePeerState(peerState)
|
||||||
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
default:
|
||||||
|
t.Errorf("channel wasn't closed after update")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemovePeer(t *testing.T) {
|
||||||
|
key := "abc"
|
||||||
|
status := NewRecorder()
|
||||||
|
peerState := State{
|
||||||
|
PubKey: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
status.peers[key] = peerState
|
||||||
|
|
||||||
|
err := status.RemovePeer(key)
|
||||||
|
assert.NoError(t, err, "shouldn't return error")
|
||||||
|
|
||||||
|
_, exists := status.peers[key]
|
||||||
|
assert.False(t, exists, "state value shouldn't be found")
|
||||||
|
|
||||||
|
err = status.RemovePeer("not existing")
|
||||||
|
assert.Error(t, err, "should return error when peer doesn't exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateLocalPeerState(t *testing.T) {
|
||||||
|
localPeerState := LocalPeerState{
|
||||||
|
IP: "10.10.10.10",
|
||||||
|
PubKey: "abc",
|
||||||
|
KernelInterface: false,
|
||||||
|
}
|
||||||
|
status := NewRecorder()
|
||||||
|
|
||||||
|
status.UpdateLocalPeerState(localPeerState)
|
||||||
|
|
||||||
|
assert.Equal(t, localPeerState, status.localPeer, "local peer status should be equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCleanLocalPeerState(t *testing.T) {
|
||||||
|
emptyLocalPeerState := LocalPeerState{}
|
||||||
|
localPeerState := LocalPeerState{
|
||||||
|
IP: "10.10.10.10",
|
||||||
|
PubKey: "abc",
|
||||||
|
KernelInterface: false,
|
||||||
|
}
|
||||||
|
status := NewRecorder()
|
||||||
|
|
||||||
|
status.localPeer = localPeerState
|
||||||
|
|
||||||
|
status.CleanLocalPeerState()
|
||||||
|
|
||||||
|
assert.Equal(t, emptyLocalPeerState, status.localPeer, "local peer status should be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateSignalState(t *testing.T) {
|
||||||
|
url := "https://signal"
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
connected bool
|
||||||
|
want SignalState
|
||||||
|
}{
|
||||||
|
{"should mark as connected", true, SignalState{
|
||||||
|
|
||||||
|
URL: url,
|
||||||
|
Connected: true,
|
||||||
|
}},
|
||||||
|
{"should mark as disconnected", false, SignalState{
|
||||||
|
URL: url,
|
||||||
|
Connected: false,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
status := NewRecorder()
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if test.connected {
|
||||||
|
status.MarkSignalConnected(url)
|
||||||
|
} else {
|
||||||
|
status.MarkSignalDisconnected(url)
|
||||||
|
}
|
||||||
|
assert.Equal(t, test.want, status.signal, "signal status should be equal")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateManagementState(t *testing.T) {
|
||||||
|
url := "https://management"
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
connected bool
|
||||||
|
want ManagementState
|
||||||
|
}{
|
||||||
|
{"should mark as connected", true, ManagementState{
|
||||||
|
|
||||||
|
URL: url,
|
||||||
|
Connected: true,
|
||||||
|
}},
|
||||||
|
{"should mark as disconnected", false, ManagementState{
|
||||||
|
URL: url,
|
||||||
|
Connected: false,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
status := NewRecorder()
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if test.connected {
|
||||||
|
status.MarkManagementConnected(url)
|
||||||
|
} else {
|
||||||
|
status.MarkManagementDisconnected(url)
|
||||||
|
}
|
||||||
|
assert.Equal(t, test.want, status.management, "signal status should be equal")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetFullStatus(t *testing.T) {
|
||||||
|
key1 := "abc"
|
||||||
|
key2 := "def"
|
||||||
|
managementState := ManagementState{
|
||||||
|
URL: "https://signal",
|
||||||
|
Connected: true,
|
||||||
|
}
|
||||||
|
signalState := SignalState{
|
||||||
|
URL: "https://signal",
|
||||||
|
Connected: true,
|
||||||
|
}
|
||||||
|
peerState1 := State{
|
||||||
|
PubKey: key1,
|
||||||
|
}
|
||||||
|
|
||||||
|
peerState2 := State{
|
||||||
|
PubKey: key2,
|
||||||
|
}
|
||||||
|
|
||||||
|
status := NewRecorder()
|
||||||
|
|
||||||
|
status.management = managementState
|
||||||
|
status.signal = signalState
|
||||||
|
status.peers[key1] = peerState1
|
||||||
|
status.peers[key2] = peerState2
|
||||||
|
|
||||||
|
fullStatus := status.GetFullStatus()
|
||||||
|
|
||||||
|
assert.Equal(t, managementState, fullStatus.ManagementState, "management status should be equal")
|
||||||
|
assert.Equal(t, signalState, fullStatus.SignalState, "signal status should be equal")
|
||||||
|
assert.ElementsMatch(t, []State{peerState1, peerState2}, fullStatus.Peers, "peers states should match")
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal/peer"
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/status"
|
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type routerPeerStatus struct {
|
type routerPeerStatus struct {
|
||||||
@ -26,7 +26,7 @@ type routesUpdate struct {
|
|||||||
type clientNetwork struct {
|
type clientNetwork struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
stop context.CancelFunc
|
stop context.CancelFunc
|
||||||
statusRecorder *status.Status
|
statusRecorder *peer.Status
|
||||||
wgInterface *iface.WGIface
|
wgInterface *iface.WGIface
|
||||||
routes map[string]*route.Route
|
routes map[string]*route.Route
|
||||||
routeUpdate chan routesUpdate
|
routeUpdate chan routesUpdate
|
||||||
@ -37,7 +37,7 @@ type clientNetwork struct {
|
|||||||
updateSerial uint64
|
updateSerial uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClientNetworkWatcher(ctx context.Context, wgInterface *iface.WGIface, statusRecorder *status.Status, network netip.Prefix) *clientNetwork {
|
func newClientNetworkWatcher(ctx context.Context, wgInterface *iface.WGIface, statusRecorder *peer.Status, network netip.Prefix) *clientNetwork {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
client := &clientNetwork{
|
client := &clientNetwork{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
@ -62,7 +62,7 @@ func (c *clientNetwork) getRouterPeerStatuses() map[string]routerPeerStatus {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
routePeerStatuses[r.ID] = routerPeerStatus{
|
routePeerStatuses[r.ID] = routerPeerStatus{
|
||||||
connected: peerStatus.ConnStatus == peer.StatusConnected.String(),
|
connected: peerStatus.ConnStatus == peer.StatusConnected,
|
||||||
relayed: peerStatus.Relayed,
|
relayed: peerStatus.Relayed,
|
||||||
direct: peerStatus.Direct,
|
direct: peerStatus.Direct,
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ func (c *clientNetwork) watchPeerStatusChanges(ctx context.Context, peerKey stri
|
|||||||
return
|
return
|
||||||
case <-c.statusRecorder.GetPeerStateChangeNotifier(peerKey):
|
case <-c.statusRecorder.GetPeerStateChangeNotifier(peerKey):
|
||||||
state, err := c.statusRecorder.GetPeer(peerKey)
|
state, err := c.statusRecorder.GetPeer(peerKey)
|
||||||
if err != nil || state.ConnStatus == peer.StatusConnecting.String() {
|
if err != nil || state.ConnStatus == peer.StatusConnecting {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
peerStateUpdate <- struct{}{}
|
peerStateUpdate <- struct{}{}
|
||||||
@ -144,7 +144,7 @@ func (c *clientNetwork) startPeersStatusChangeWatcher() {
|
|||||||
|
|
||||||
func (c *clientNetwork) removeRouteFromWireguardPeer(peerKey string) error {
|
func (c *clientNetwork) removeRouteFromWireguardPeer(peerKey string) error {
|
||||||
state, err := c.statusRecorder.GetPeer(peerKey)
|
state, err := c.statusRecorder.GetPeer(peerKey)
|
||||||
if err != nil || state.ConnStatus != peer.StatusConnected.String() {
|
if err != nil || state.ConnStatus != peer.StatusConnected {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,12 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/status"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/netbirdio/netbird/client/system"
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
log "github.com/sirupsen/logrus"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Manager is a route manager interface
|
// Manager is a route manager interface
|
||||||
@ -27,13 +28,13 @@ type DefaultManager struct {
|
|||||||
clientNetworks map[string]*clientNetwork
|
clientNetworks map[string]*clientNetwork
|
||||||
serverRoutes map[string]*route.Route
|
serverRoutes map[string]*route.Route
|
||||||
serverRouter *serverRouter
|
serverRouter *serverRouter
|
||||||
statusRecorder *status.Status
|
statusRecorder *peer.Status
|
||||||
wgInterface *iface.WGIface
|
wgInterface *iface.WGIface
|
||||||
pubKey string
|
pubKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManager returns a new route manager
|
// NewManager returns a new route manager
|
||||||
func NewManager(ctx context.Context, pubKey string, wgInterface *iface.WGIface, statusRecorder *status.Status) *DefaultManager {
|
func NewManager(ctx context.Context, pubKey string, wgInterface *iface.WGIface, statusRecorder *peer.Status) *DefaultManager {
|
||||||
mCTX, cancel := context.WithCancel(ctx)
|
mCTX, cancel := context.WithCancel(ctx)
|
||||||
return &DefaultManager{
|
return &DefaultManager{
|
||||||
ctx: mCTX,
|
ctx: mCTX,
|
||||||
@ -170,7 +171,7 @@ func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Ro
|
|||||||
// we skip this route management
|
// we skip this route management
|
||||||
if newRoute.Network.Bits() < 7 {
|
if newRoute.Network.Bits() < 7 {
|
||||||
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skiping this route",
|
log.Errorf("this agent version: %s, doesn't support default routes, received %s, skiping this route",
|
||||||
system.NetbirdVersion(), newRoute.Network)
|
version.NetbirdVersion(), newRoute.Network)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
newClientRoutesIDMap[networkID] = append(newClientRoutesIDMap[networkID], newRoute)
|
newClientRoutesIDMap[networkID] = append(newClientRoutesIDMap[networkID], newRoute)
|
||||||
|
@ -7,10 +7,11 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/status"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/iface"
|
"github.com/netbirdio/netbird/iface"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// send 5 routes, one for server and 4 for clients, one normal and 2 HA and one small
|
// send 5 routes, one for server and 4 for clients, one normal and 2 HA and one small
|
||||||
@ -397,7 +398,7 @@ func TestManagerUpdateRoutes(t *testing.T) {
|
|||||||
err = wgInterface.Create()
|
err = wgInterface.Create()
|
||||||
require.NoError(t, err, "should create testing wireguard interface")
|
require.NoError(t, err, "should create testing wireguard interface")
|
||||||
|
|
||||||
statusRecorder := status.NewRecorder()
|
statusRecorder := peer.NewRecorder()
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
routeManager := NewManager(ctx, localPeerKey, wgInterface, statusRecorder)
|
routeManager := NewManager(ctx, localPeerKey, wgInterface, statusRecorder)
|
||||||
defer routeManager.Stop()
|
defer routeManager.Stop()
|
||||||
|
@ -3,20 +3,19 @@ package server
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
nbStatus "github.com/netbirdio/netbird/client/status"
|
|
||||||
"github.com/netbirdio/netbird/client/system"
|
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
gstatus "google.golang.org/grpc/status"
|
gstatus "google.golang.org/grpc/status"
|
||||||
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/internal"
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
|
"github.com/netbirdio/netbird/client/internal/peer"
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server for service control.
|
// Server for service control.
|
||||||
@ -34,7 +33,7 @@ type Server struct {
|
|||||||
config *internal.Config
|
config *internal.Config
|
||||||
proto.UnimplementedDaemonServiceServer
|
proto.UnimplementedDaemonServiceServer
|
||||||
|
|
||||||
statusRecorder *nbStatus.Status
|
statusRecorder *peer.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
type oauthAuthFlow struct {
|
type oauthAuthFlow struct {
|
||||||
@ -77,9 +76,9 @@ func (s *Server) Start() error {
|
|||||||
|
|
||||||
// if configuration exists, we just start connections. if is new config we skip and set status NeedsLogin
|
// if configuration exists, we just start connections. if is new config we skip and set status NeedsLogin
|
||||||
// on failure we return error to retry
|
// on failure we return error to retry
|
||||||
config, err := internal.ReadConfig(s.latestConfigInput)
|
config, err := internal.UpdateConfig(s.latestConfigInput)
|
||||||
if errorStatus, ok := gstatus.FromError(err); ok && errorStatus.Code() == codes.NotFound {
|
if errorStatus, ok := gstatus.FromError(err); ok && errorStatus.Code() == codes.NotFound {
|
||||||
config, err = internal.GetConfig(s.latestConfigInput)
|
config, err = internal.UpdateOrCreateConfig(s.latestConfigInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("unable to create configuration file: %v", err)
|
log.Warnf("unable to create configuration file: %v", err)
|
||||||
return err
|
return err
|
||||||
@ -97,7 +96,7 @@ func (s *Server) Start() error {
|
|||||||
s.config = config
|
s.config = config
|
||||||
|
|
||||||
if s.statusRecorder == nil {
|
if s.statusRecorder == nil {
|
||||||
s.statusRecorder = nbStatus.NewRecorder()
|
s.statusRecorder = peer.NewRecorder()
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -182,7 +181,7 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro
|
|||||||
|
|
||||||
inputConfig.PreSharedKey = &msg.PreSharedKey
|
inputConfig.PreSharedKey = &msg.PreSharedKey
|
||||||
|
|
||||||
config, err := internal.GetConfig(inputConfig)
|
config, err := internal.UpdateOrCreateConfig(inputConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -205,7 +204,7 @@ func (s *Server) Login(callerCtx context.Context, msg *proto.LoginRequest) (*pro
|
|||||||
state.Set(internal.StatusConnecting)
|
state.Set(internal.StatusConnecting)
|
||||||
|
|
||||||
if msg.SetupKey == "" {
|
if msg.SetupKey == "" {
|
||||||
providerConfig, err := internal.GetDeviceAuthorizationFlowInfo(ctx, config)
|
providerConfig, err := internal.GetDeviceAuthorizationFlowInfo(ctx, config.PrivateKey, config.ManagementURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Set(internal.StatusLoginFailed)
|
state.Set(internal.StatusLoginFailed)
|
||||||
s, ok := gstatus.FromError(err)
|
s, ok := gstatus.FromError(err)
|
||||||
@ -387,7 +386,7 @@ func (s *Server) Up(callerCtx context.Context, _ *proto.UpRequest) (*proto.UpRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.statusRecorder == nil {
|
if s.statusRecorder == nil {
|
||||||
s.statusRecorder = nbStatus.NewRecorder()
|
s.statusRecorder = peer.NewRecorder()
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -428,10 +427,10 @@ func (s *Server) Status(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
statusResponse := proto.StatusResponse{Status: string(status), DaemonVersion: system.NetbirdVersion()}
|
statusResponse := proto.StatusResponse{Status: string(status), DaemonVersion: version.NetbirdVersion()}
|
||||||
|
|
||||||
if s.statusRecorder == nil {
|
if s.statusRecorder == nil {
|
||||||
s.statusRecorder = nbStatus.NewRecorder()
|
s.statusRecorder = peer.NewRecorder()
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.GetFullPeerStatus {
|
if msg.GetFullPeerStatus {
|
||||||
@ -477,7 +476,7 @@ func (s *Server) GetConfig(_ context.Context, _ *proto.GetConfigRequest) (*proto
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toProtoFullStatus(fullStatus nbStatus.FullStatus) *proto.FullStatus {
|
func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus {
|
||||||
pbFullStatus := proto.FullStatus{
|
pbFullStatus := proto.FullStatus{
|
||||||
ManagementState: &proto.ManagementState{},
|
ManagementState: &proto.ManagementState{},
|
||||||
SignalState: &proto.SignalState{},
|
SignalState: &proto.SignalState{},
|
||||||
@ -500,7 +499,7 @@ func toProtoFullStatus(fullStatus nbStatus.FullStatus) *proto.FullStatus {
|
|||||||
pbPeerState := &proto.PeerState{
|
pbPeerState := &proto.PeerState{
|
||||||
IP: peerState.IP,
|
IP: peerState.IP,
|
||||||
PubKey: peerState.PubKey,
|
PubKey: peerState.PubKey,
|
||||||
ConnStatus: peerState.ConnStatus,
|
ConnStatus: peerState.ConnStatus.String(),
|
||||||
ConnStatusUpdate: timestamppb.New(peerState.ConnStatusUpdate),
|
ConnStatusUpdate: timestamppb.New(peerState.ConnStatusUpdate),
|
||||||
Relayed: peerState.Relayed,
|
Relayed: peerState.Relayed,
|
||||||
Direct: peerState.Direct,
|
Direct: peerState.Direct,
|
||||||
|
@ -1,241 +0,0 @@
|
|||||||
package status
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PeerState contains the latest state of a peer
|
|
||||||
type PeerState struct {
|
|
||||||
IP string
|
|
||||||
PubKey string
|
|
||||||
FQDN string
|
|
||||||
ConnStatus string
|
|
||||||
ConnStatusUpdate time.Time
|
|
||||||
Relayed bool
|
|
||||||
Direct bool
|
|
||||||
LocalIceCandidateType string
|
|
||||||
RemoteIceCandidateType string
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalPeerState contains the latest state of the local peer
|
|
||||||
type LocalPeerState struct {
|
|
||||||
IP string
|
|
||||||
PubKey string
|
|
||||||
KernelInterface bool
|
|
||||||
FQDN string
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignalState contains the latest state of a signal connection
|
|
||||||
type SignalState struct {
|
|
||||||
URL string
|
|
||||||
Connected bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ManagementState contains the latest state of a management connection
|
|
||||||
type ManagementState struct {
|
|
||||||
URL string
|
|
||||||
Connected bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// FullStatus contains the full state held by the Status instance
|
|
||||||
type FullStatus struct {
|
|
||||||
Peers []PeerState
|
|
||||||
ManagementState ManagementState
|
|
||||||
SignalState SignalState
|
|
||||||
LocalPeerState LocalPeerState
|
|
||||||
}
|
|
||||||
|
|
||||||
// Status holds a state of peers, signal and management connections
|
|
||||||
type Status struct {
|
|
||||||
mux sync.Mutex
|
|
||||||
peers map[string]PeerState
|
|
||||||
changeNotify map[string]chan struct{}
|
|
||||||
signal SignalState
|
|
||||||
management ManagementState
|
|
||||||
localPeer LocalPeerState
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRecorder returns a new Status instance
|
|
||||||
func NewRecorder() *Status {
|
|
||||||
return &Status{
|
|
||||||
peers: make(map[string]PeerState),
|
|
||||||
changeNotify: make(map[string]chan struct{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddPeer adds peer to Daemon status map
|
|
||||||
func (d *Status) AddPeer(peerPubKey string) error {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
|
|
||||||
_, ok := d.peers[peerPubKey]
|
|
||||||
if ok {
|
|
||||||
return errors.New("peer already exist")
|
|
||||||
}
|
|
||||||
d.peers[peerPubKey] = PeerState{PubKey: peerPubKey}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPeer adds peer to Daemon status map
|
|
||||||
func (d *Status) GetPeer(peerPubKey string) (PeerState, error) {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
|
|
||||||
state, ok := d.peers[peerPubKey]
|
|
||||||
if !ok {
|
|
||||||
return PeerState{}, errors.New("peer not found")
|
|
||||||
}
|
|
||||||
return state, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemovePeer removes peer from Daemon status map
|
|
||||||
func (d *Status) RemovePeer(peerPubKey string) error {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
|
|
||||||
_, ok := d.peers[peerPubKey]
|
|
||||||
if ok {
|
|
||||||
delete(d.peers, peerPubKey)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New("no peer with to remove")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePeerState updates peer status
|
|
||||||
func (d *Status) UpdatePeerState(receivedState PeerState) error {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
|
|
||||||
peerState, ok := d.peers[receivedState.PubKey]
|
|
||||||
if !ok {
|
|
||||||
return errors.New("peer doesn't exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
if receivedState.IP != "" {
|
|
||||||
peerState.IP = receivedState.IP
|
|
||||||
}
|
|
||||||
|
|
||||||
if receivedState.ConnStatus != peerState.ConnStatus {
|
|
||||||
peerState.ConnStatus = receivedState.ConnStatus
|
|
||||||
peerState.ConnStatusUpdate = receivedState.ConnStatusUpdate
|
|
||||||
peerState.Direct = receivedState.Direct
|
|
||||||
peerState.Relayed = receivedState.Relayed
|
|
||||||
peerState.LocalIceCandidateType = receivedState.LocalIceCandidateType
|
|
||||||
peerState.RemoteIceCandidateType = receivedState.RemoteIceCandidateType
|
|
||||||
}
|
|
||||||
|
|
||||||
d.peers[receivedState.PubKey] = peerState
|
|
||||||
|
|
||||||
ch, found := d.changeNotify[receivedState.PubKey]
|
|
||||||
if found && ch != nil {
|
|
||||||
close(ch)
|
|
||||||
d.changeNotify[receivedState.PubKey] = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePeerFQDN update peer's state fqdn only
|
|
||||||
func (d *Status) UpdatePeerFQDN(peerPubKey, fqdn string) error {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
|
|
||||||
peerState, ok := d.peers[peerPubKey]
|
|
||||||
if !ok {
|
|
||||||
return errors.New("peer doesn't exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
peerState.FQDN = fqdn
|
|
||||||
d.peers[peerPubKey] = peerState
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPeerStateChangeNotifier returns a change notifier channel for a peer
|
|
||||||
func (d *Status) GetPeerStateChangeNotifier(peer string) <-chan struct{} {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
ch, found := d.changeNotify[peer]
|
|
||||||
if !found || ch == nil {
|
|
||||||
ch = make(chan struct{})
|
|
||||||
d.changeNotify[peer] = ch
|
|
||||||
}
|
|
||||||
return ch
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateLocalPeerState updates local peer status
|
|
||||||
func (d *Status) UpdateLocalPeerState(localPeerState LocalPeerState) {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
|
|
||||||
d.localPeer = localPeerState
|
|
||||||
}
|
|
||||||
|
|
||||||
// CleanLocalPeerState cleans local peer status
|
|
||||||
func (d *Status) CleanLocalPeerState() {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
|
|
||||||
d.localPeer = LocalPeerState{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkManagementDisconnected sets ManagementState to disconnected
|
|
||||||
func (d *Status) MarkManagementDisconnected(managementURL string) {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
d.management = ManagementState{
|
|
||||||
URL: managementURL,
|
|
||||||
Connected: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkManagementConnected sets ManagementState to connected
|
|
||||||
func (d *Status) MarkManagementConnected(managementURL string) {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
d.management = ManagementState{
|
|
||||||
URL: managementURL,
|
|
||||||
Connected: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkSignalDisconnected sets SignalState to disconnected
|
|
||||||
func (d *Status) MarkSignalDisconnected(signalURL string) {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
d.signal = SignalState{
|
|
||||||
signalURL,
|
|
||||||
false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkSignalConnected sets SignalState to connected
|
|
||||||
func (d *Status) MarkSignalConnected(signalURL string) {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
d.signal = SignalState{
|
|
||||||
signalURL,
|
|
||||||
true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFullStatus gets full status
|
|
||||||
func (d *Status) GetFullStatus() FullStatus {
|
|
||||||
d.mux.Lock()
|
|
||||||
defer d.mux.Unlock()
|
|
||||||
|
|
||||||
fullStatus := FullStatus{
|
|
||||||
ManagementState: d.management,
|
|
||||||
SignalState: d.signal,
|
|
||||||
LocalPeerState: d.localPeer,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, status := range d.peers {
|
|
||||||
fullStatus.Peers = append(fullStatus.Peers, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fullStatus
|
|
||||||
}
|
|
@ -1,243 +0,0 @@
|
|||||||
package status
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAddPeer(t *testing.T) {
|
|
||||||
key := "abc"
|
|
||||||
status := NewRecorder()
|
|
||||||
err := status.AddPeer(key)
|
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
|
||||||
|
|
||||||
_, exists := status.peers[key]
|
|
||||||
assert.True(t, exists, "value was found")
|
|
||||||
|
|
||||||
err = status.AddPeer(key)
|
|
||||||
|
|
||||||
assert.Error(t, err, "should return error on duplicate")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetPeer(t *testing.T) {
|
|
||||||
key := "abc"
|
|
||||||
status := NewRecorder()
|
|
||||||
err := status.AddPeer(key)
|
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
|
||||||
|
|
||||||
peerStatus, err := status.GetPeer(key)
|
|
||||||
assert.NoError(t, err, "shouldn't return error on getting peer")
|
|
||||||
|
|
||||||
assert.Equal(t, key, peerStatus.PubKey, "retrieved public key should match")
|
|
||||||
|
|
||||||
_, err = status.GetPeer("non_existing_key")
|
|
||||||
assert.Error(t, err, "should return error when peer doesn't exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdatePeerState(t *testing.T) {
|
|
||||||
key := "abc"
|
|
||||||
ip := "10.10.10.10"
|
|
||||||
status := NewRecorder()
|
|
||||||
peerState := PeerState{
|
|
||||||
PubKey: key,
|
|
||||||
}
|
|
||||||
|
|
||||||
status.peers[key] = peerState
|
|
||||||
|
|
||||||
peerState.IP = ip
|
|
||||||
|
|
||||||
err := status.UpdatePeerState(peerState)
|
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
|
||||||
|
|
||||||
state, exists := status.peers[key]
|
|
||||||
assert.True(t, exists, "state should be found")
|
|
||||||
assert.Equal(t, ip, state.IP, "ip should be equal")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStatus_UpdatePeerFQDN(t *testing.T) {
|
|
||||||
key := "abc"
|
|
||||||
fqdn := "peer-a.netbird.local"
|
|
||||||
status := NewRecorder()
|
|
||||||
peerState := PeerState{
|
|
||||||
PubKey: key,
|
|
||||||
}
|
|
||||||
|
|
||||||
status.peers[key] = peerState
|
|
||||||
|
|
||||||
err := status.UpdatePeerFQDN(key, fqdn)
|
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
|
||||||
|
|
||||||
state, exists := status.peers[key]
|
|
||||||
assert.True(t, exists, "state should be found")
|
|
||||||
assert.Equal(t, fqdn, state.FQDN, "fqdn should be equal")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetPeerStateChangeNotifierLogic(t *testing.T) {
|
|
||||||
key := "abc"
|
|
||||||
ip := "10.10.10.10"
|
|
||||||
status := NewRecorder()
|
|
||||||
peerState := PeerState{
|
|
||||||
PubKey: key,
|
|
||||||
}
|
|
||||||
|
|
||||||
status.peers[key] = peerState
|
|
||||||
|
|
||||||
ch := status.GetPeerStateChangeNotifier(key)
|
|
||||||
assert.NotNil(t, ch, "channel shouldn't be nil")
|
|
||||||
|
|
||||||
peerState.IP = ip
|
|
||||||
|
|
||||||
err := status.UpdatePeerState(peerState)
|
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ch:
|
|
||||||
default:
|
|
||||||
t.Errorf("channel wasn't closed after update")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemovePeer(t *testing.T) {
|
|
||||||
key := "abc"
|
|
||||||
status := NewRecorder()
|
|
||||||
peerState := PeerState{
|
|
||||||
PubKey: key,
|
|
||||||
}
|
|
||||||
|
|
||||||
status.peers[key] = peerState
|
|
||||||
|
|
||||||
err := status.RemovePeer(key)
|
|
||||||
assert.NoError(t, err, "shouldn't return error")
|
|
||||||
|
|
||||||
_, exists := status.peers[key]
|
|
||||||
assert.False(t, exists, "state value shouldn't be found")
|
|
||||||
|
|
||||||
err = status.RemovePeer("not existing")
|
|
||||||
assert.Error(t, err, "should return error when peer doesn't exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateLocalPeerState(t *testing.T) {
|
|
||||||
localPeerState := LocalPeerState{
|
|
||||||
IP: "10.10.10.10",
|
|
||||||
PubKey: "abc",
|
|
||||||
KernelInterface: false,
|
|
||||||
}
|
|
||||||
status := NewRecorder()
|
|
||||||
|
|
||||||
status.UpdateLocalPeerState(localPeerState)
|
|
||||||
|
|
||||||
assert.Equal(t, localPeerState, status.localPeer, "local peer status should be equal")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCleanLocalPeerState(t *testing.T) {
|
|
||||||
emptyLocalPeerState := LocalPeerState{}
|
|
||||||
localPeerState := LocalPeerState{
|
|
||||||
IP: "10.10.10.10",
|
|
||||||
PubKey: "abc",
|
|
||||||
KernelInterface: false,
|
|
||||||
}
|
|
||||||
status := NewRecorder()
|
|
||||||
|
|
||||||
status.localPeer = localPeerState
|
|
||||||
|
|
||||||
status.CleanLocalPeerState()
|
|
||||||
|
|
||||||
assert.Equal(t, emptyLocalPeerState, status.localPeer, "local peer status should be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateSignalState(t *testing.T) {
|
|
||||||
url := "https://signal"
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
connected bool
|
|
||||||
want SignalState
|
|
||||||
}{
|
|
||||||
{"should mark as connected", true, SignalState{
|
|
||||||
|
|
||||||
URL: url,
|
|
||||||
Connected: true,
|
|
||||||
}},
|
|
||||||
{"should mark as disconnected", false, SignalState{
|
|
||||||
URL: url,
|
|
||||||
Connected: false,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
status := NewRecorder()
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
if test.connected {
|
|
||||||
status.MarkSignalConnected(url)
|
|
||||||
} else {
|
|
||||||
status.MarkSignalDisconnected(url)
|
|
||||||
}
|
|
||||||
assert.Equal(t, test.want, status.signal, "signal status should be equal")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdateManagementState(t *testing.T) {
|
|
||||||
url := "https://management"
|
|
||||||
var tests = []struct {
|
|
||||||
name string
|
|
||||||
connected bool
|
|
||||||
want ManagementState
|
|
||||||
}{
|
|
||||||
{"should mark as connected", true, ManagementState{
|
|
||||||
|
|
||||||
URL: url,
|
|
||||||
Connected: true,
|
|
||||||
}},
|
|
||||||
{"should mark as disconnected", false, ManagementState{
|
|
||||||
URL: url,
|
|
||||||
Connected: false,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
status := NewRecorder()
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
if test.connected {
|
|
||||||
status.MarkManagementConnected(url)
|
|
||||||
} else {
|
|
||||||
status.MarkManagementDisconnected(url)
|
|
||||||
}
|
|
||||||
assert.Equal(t, test.want, status.management, "signal status should be equal")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetFullStatus(t *testing.T) {
|
|
||||||
key1 := "abc"
|
|
||||||
key2 := "def"
|
|
||||||
managementState := ManagementState{
|
|
||||||
URL: "https://signal",
|
|
||||||
Connected: true,
|
|
||||||
}
|
|
||||||
signalState := SignalState{
|
|
||||||
URL: "https://signal",
|
|
||||||
Connected: true,
|
|
||||||
}
|
|
||||||
peerState1 := PeerState{
|
|
||||||
PubKey: key1,
|
|
||||||
}
|
|
||||||
|
|
||||||
peerState2 := PeerState{
|
|
||||||
PubKey: key2,
|
|
||||||
}
|
|
||||||
|
|
||||||
status := NewRecorder()
|
|
||||||
|
|
||||||
status.management = managementState
|
|
||||||
status.signal = signalState
|
|
||||||
status.peers[key1] = peerState1
|
|
||||||
status.peers[key2] = peerState2
|
|
||||||
|
|
||||||
fullStatus := status.GetFullStatus()
|
|
||||||
|
|
||||||
assert.Equal(t, managementState, fullStatus.ManagementState, "management status should be equal")
|
|
||||||
assert.Equal(t, signalState, fullStatus.SignalState, "signal status should be equal")
|
|
||||||
assert.ElementsMatch(t, []PeerState{peerState1, peerState2}, fullStatus.Peers, "peers states should match")
|
|
||||||
}
|
|
@ -2,15 +2,14 @@ package system
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// this is the wiretrustee version
|
// Info is an object that contains machine information
|
||||||
// will be replaced with the release version when using goreleaser
|
|
||||||
var version = "development"
|
|
||||||
|
|
||||||
//Info is an object that contains machine information
|
|
||||||
// Most of the code is taken from https://github.com/matishsiao/goInfo
|
// Most of the code is taken from https://github.com/matishsiao/goInfo
|
||||||
type Info struct {
|
type Info struct {
|
||||||
GoOS string
|
GoOS string
|
||||||
@ -25,11 +24,6 @@ type Info struct {
|
|||||||
UIVersion string
|
UIVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetbirdVersion returns the Netbird version
|
|
||||||
func NetbirdVersion() string {
|
|
||||||
return version
|
|
||||||
}
|
|
||||||
|
|
||||||
// extractUserAgent extracts Netbird's agent (client) name and version from the outgoing context
|
// extractUserAgent extracts Netbird's agent (client) name and version from the outgoing context
|
||||||
func extractUserAgent(ctx context.Context) string {
|
func extractUserAgent(ctx context.Context) string {
|
||||||
md, hasMeta := metadata.FromOutgoingContext(ctx)
|
md, hasMeta := metadata.FromOutgoingContext(ctx)
|
||||||
@ -48,5 +42,5 @@ func extractUserAgent(ctx context.Context) string {
|
|||||||
|
|
||||||
// GetDesktopUIUserAgent returns the Desktop ui user agent
|
// GetDesktopUIUserAgent returns the Desktop ui user agent
|
||||||
func GetDesktopUIUserAgent() string {
|
func GetDesktopUIUserAgent() string {
|
||||||
return "netbird-desktop-ui/" + NetbirdVersion()
|
return "netbird-desktop-ui/" + version.NetbirdVersion()
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,16 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
@ -22,14 +26,14 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
sysName := string(bytes.Split(utsname.Sysname[:], []byte{0})[0])
|
sysName := string(bytes.Split(utsname.Sysname[:], []byte{0})[0])
|
||||||
machine := string(bytes.Split(utsname.Machine[:], []byte{0})[0])
|
machine := string(bytes.Split(utsname.Machine[:], []byte{0})[0])
|
||||||
release := string(bytes.Split(utsname.Release[:], []byte{0})[0])
|
release := string(bytes.Split(utsname.Release[:], []byte{0})[0])
|
||||||
version, err := exec.Command("sw_vers", "-productVersion").Output()
|
swVersion, err := exec.Command("sw_vers", "-productVersion").Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("got an error while retrieving macOS version with sw_vers, error: %s. Using darwin version instead.\n", err)
|
log.Warnf("got an error while retrieving macOS version with sw_vers, error: %s. Using darwin version instead.\n", err)
|
||||||
version = []byte(release)
|
swVersion = []byte(release)
|
||||||
}
|
}
|
||||||
gio := &Info{Kernel: sysName, OSVersion: strings.TrimSpace(string(version)), Core: release, Platform: machine, OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
gio := &Info{Kernel: sysName, OSVersion: strings.TrimSpace(string(swVersion)), Core: release, Platform: machine, OS: sysName, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
||||||
gio.Hostname, _ = os.Hostname()
|
gio.Hostname, _ = os.Hostname()
|
||||||
gio.WiretrusteeVersion = NetbirdVersion()
|
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||||
gio.UIVersion = extractUserAgent(ctx)
|
gio.UIVersion = extractUserAgent(ctx)
|
||||||
|
|
||||||
return gio
|
return gio
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
@ -23,7 +25,7 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
osInfo := strings.Split(osStr, " ")
|
osInfo := strings.Split(osStr, " ")
|
||||||
gio := &Info{Kernel: osInfo[0], Core: osInfo[1], Platform: runtime.GOARCH, OS: osInfo[2], GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
gio := &Info{Kernel: osInfo[0], Core: osInfo[1], Platform: runtime.GOARCH, OS: osInfo[2], GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
||||||
gio.Hostname, _ = os.Hostname()
|
gio.Hostname, _ = os.Hostname()
|
||||||
gio.WiretrusteeVersion = NetbirdVersion()
|
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||||
gio.UIVersion = extractUserAgent(ctx)
|
gio.UIVersion = extractUserAgent(ctx)
|
||||||
|
|
||||||
return gio
|
return gio
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
@ -46,7 +48,7 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
}
|
}
|
||||||
gio := &Info{Kernel: osInfo[0], Core: osInfo[1], Platform: osInfo[2], OS: osName, OSVersion: osVer, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
gio := &Info{Kernel: osInfo[0], Core: osInfo[1], Platform: osInfo[2], OS: osName, OSVersion: osVer, GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
||||||
gio.Hostname, _ = os.Hostname()
|
gio.Hostname, _ = os.Hostname()
|
||||||
gio.WiretrusteeVersion = NetbirdVersion()
|
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||||
gio.UIVersion = extractUserAgent(ctx)
|
gio.UIVersion = extractUserAgent(ctx)
|
||||||
|
|
||||||
return gio
|
return gio
|
||||||
|
@ -3,10 +3,13 @@ package system
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/sys/windows/registry"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetInfo retrieves and parses the system information
|
// GetInfo retrieves and parses the system information
|
||||||
@ -14,7 +17,7 @@ func GetInfo(ctx context.Context) *Info {
|
|||||||
ver := getOSVersion()
|
ver := getOSVersion()
|
||||||
gio := &Info{Kernel: "windows", OSVersion: ver, Core: ver, Platform: "unknown", OS: "windows", GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
gio := &Info{Kernel: "windows", OSVersion: ver, Core: ver, Platform: "unknown", OS: "windows", GoOS: runtime.GOOS, CPUs: runtime.NumCPU()}
|
||||||
gio.Hostname, _ = os.Hostname()
|
gio.Hostname, _ = os.Hostname()
|
||||||
gio.WiretrusteeVersion = NetbirdVersion()
|
gio.WiretrusteeVersion = version.NetbirdVersion()
|
||||||
gio.UIVersion = extractUserAgent(ctx)
|
gio.UIVersion = extractUserAgent(ctx)
|
||||||
|
|
||||||
return gio
|
return gio
|
||||||
@ -32,7 +35,7 @@ func getOSVersion() string {
|
|||||||
log.Error(deferErr)
|
log.Error(deferErr)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
major, _, err := k.GetIntegerValue("CurrentMajorVersionNumber")
|
major, _, err := k.GetIntegerValue("CurrentMajorVersionNumber")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
|
@ -6,6 +6,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
_ "embed"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -17,25 +18,22 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/client/system"
|
"fyne.io/fyne/v2"
|
||||||
|
"fyne.io/fyne/v2/app"
|
||||||
|
"fyne.io/fyne/v2/dialog"
|
||||||
|
"fyne.io/fyne/v2/widget"
|
||||||
"github.com/cenkalti/backoff/v4"
|
"github.com/cenkalti/backoff/v4"
|
||||||
|
|
||||||
_ "embed"
|
|
||||||
|
|
||||||
"github.com/getlantern/systray"
|
"github.com/getlantern/systray"
|
||||||
"github.com/netbirdio/netbird/client/internal"
|
|
||||||
"github.com/netbirdio/netbird/client/proto"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/skratchdot/open-golang/open"
|
"github.com/skratchdot/open-golang/open"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"github.com/netbirdio/netbird/client/internal"
|
||||||
"fyne.io/fyne/v2/app"
|
"github.com/netbirdio/netbird/client/proto"
|
||||||
"fyne.io/fyne/v2/dialog"
|
"github.com/netbirdio/netbird/client/system"
|
||||||
"fyne.io/fyne/v2/widget"
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -373,7 +371,7 @@ func (s *serviceClient) onTrayReady() {
|
|||||||
systray.AddSeparator()
|
systray.AddSeparator()
|
||||||
s.mSettings = systray.AddMenuItem("Settings", "Settings of the application")
|
s.mSettings = systray.AddMenuItem("Settings", "Settings of the application")
|
||||||
systray.AddSeparator()
|
systray.AddSeparator()
|
||||||
v := systray.AddMenuItem("v"+system.NetbirdVersion(), "Client Version: "+system.NetbirdVersion())
|
v := systray.AddMenuItem("v"+version.NetbirdVersion(), "Client Version: "+version.NetbirdVersion())
|
||||||
v.Disable()
|
v.Disable()
|
||||||
systray.AddSeparator()
|
systray.AddSeparator()
|
||||||
s.mQuit = systray.AddMenuItem("Quit", "Quit the client app")
|
s.mQuit = systray.AddMenuItem("Quit", "Quit the client app")
|
||||||
|
45
go.mod
45
go.mod
@ -11,18 +11,18 @@ require (
|
|||||||
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
|
github.com/kardianos/service v1.2.1-0.20210728001519-a323c3813bc7
|
||||||
github.com/onsi/ginkgo v1.16.5
|
github.com/onsi/ginkgo v1.16.5
|
||||||
github.com/onsi/gomega v1.18.1
|
github.com/onsi/gomega v1.18.1
|
||||||
github.com/pion/ice/v2 v2.3.0
|
github.com/pion/ice/v2 v2.3.1
|
||||||
github.com/rs/cors v1.8.0
|
github.com/rs/cors v1.8.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
github.com/vishvananda/netlink v1.1.0
|
github.com/vishvananda/netlink v1.1.0
|
||||||
golang.org/x/crypto v0.6.0
|
golang.org/x/crypto v0.7.0
|
||||||
golang.org/x/sys v0.5.0
|
golang.org/x/sys v0.6.0
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20211209221555-9c9e7e272434
|
golang.zx2c4.com/wireguard v0.0.0-20211209221555-9c9e7e272434
|
||||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de
|
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211215182854-7a385b3431de
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.1
|
golang.zx2c4.com/wireguard/windows v0.5.1
|
||||||
google.golang.org/grpc v1.43.0
|
google.golang.org/grpc v1.52.3
|
||||||
google.golang.org/protobuf v1.28.1
|
google.golang.org/protobuf v1.28.1
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
)
|
)
|
||||||
@ -43,24 +43,27 @@ require (
|
|||||||
github.com/libp2p/go-netroute v0.2.0
|
github.com/libp2p/go-netroute v0.2.0
|
||||||
github.com/magiconair/properties v1.8.5
|
github.com/magiconair/properties v1.8.5
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
github.com/miekg/dns v1.1.41
|
github.com/miekg/dns v1.1.43
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
|
github.com/open-policy-agent/opa v0.49.0
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/prometheus/client_golang v1.13.0
|
github.com/prometheus/client_golang v1.14.0
|
||||||
github.com/rs/xid v1.3.0
|
github.com/rs/xid v1.3.0
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.33.0
|
go.opentelemetry.io/otel/exporters/prometheus v0.33.0
|
||||||
go.opentelemetry.io/otel/metric v0.33.0
|
go.opentelemetry.io/otel/metric v0.33.0
|
||||||
go.opentelemetry.io/otel/sdk/metric v0.33.0
|
go.opentelemetry.io/otel/sdk/metric v0.33.0
|
||||||
golang.org/x/net v0.7.0
|
golang.org/x/net v0.8.0
|
||||||
golang.org/x/term v0.5.0
|
golang.org/x/term v0.6.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v0.4.1 // indirect
|
github.com/BurntSushi/toml v0.4.1 // indirect
|
||||||
|
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
|
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect
|
||||||
|
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
|
||||||
@ -68,26 +71,28 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect
|
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect
|
||||||
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect
|
github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect
|
||||||
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 // indirect
|
github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 // indirect
|
||||||
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect
|
github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect
|
||||||
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect
|
github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect
|
||||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect
|
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect
|
||||||
|
github.com/ghodss/yaml v1.0.0 // indirect
|
||||||
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f // indirect
|
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f // indirect
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be // indirect
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20211024062804-40e447a793be // indirect
|
||||||
github.com/go-logr/logr v1.2.3 // indirect
|
github.com/go-logr/logr v1.2.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||||
github.com/go-stack/stack v1.8.0 // indirect
|
github.com/go-stack/stack v1.8.0 // indirect
|
||||||
|
github.com/gobwas/glob v0.2.3 // indirect
|
||||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff // indirect
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/google/gopacket v1.1.19 // indirect
|
github.com/google/gopacket v1.1.19 // indirect
|
||||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
|
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mdlayher/genetlink v1.1.0 // indirect
|
github.com/mdlayher/genetlink v1.1.0 // indirect
|
||||||
github.com/mdlayher/netlink v1.4.2 // indirect
|
github.com/mdlayher/netlink v1.4.2 // indirect
|
||||||
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect
|
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb // indirect
|
||||||
@ -103,28 +108,32 @@ require (
|
|||||||
github.com/pion/turn/v2 v2.1.0 // indirect
|
github.com/pion/turn/v2 v2.1.0 // indirect
|
||||||
github.com/pion/udp/v2 v2.0.1 // indirect
|
github.com/pion/udp/v2 v2.0.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/prometheus/client_model v0.2.0 // indirect
|
github.com/prometheus/client_model v0.3.0 // indirect
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
github.com/prometheus/common v0.37.0 // indirect
|
||||||
github.com/prometheus/procfs v0.8.0 // indirect
|
github.com/prometheus/procfs v0.8.0 // indirect
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
github.com/rogpeppe/go-internal v1.8.0 // indirect
|
||||||
github.com/spf13/cast v1.5.0 // indirect
|
github.com/spf13/cast v1.5.0 // indirect
|
||||||
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
|
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
|
||||||
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
|
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
|
||||||
|
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||||
|
github.com/yashtewari/glob-intersection v0.1.0 // indirect
|
||||||
github.com/yuin/goldmark v1.4.13 // indirect
|
github.com/yuin/goldmark v1.4.13 // indirect
|
||||||
go.opentelemetry.io/otel v1.11.1 // indirect
|
go.opentelemetry.io/otel v1.11.1 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.11.1 // indirect
|
go.opentelemetry.io/otel/sdk v1.11.1 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.11.1 // indirect
|
go.opentelemetry.io/otel/trace v1.11.1 // indirect
|
||||||
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect
|
golang.org/x/exp v0.0.0-20220518171630-0b5c67f07fdf // indirect
|
||||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 // indirect
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8 // indirect
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.org/x/text v0.7.0 // indirect
|
golang.org/x/text v0.8.0 // indirect
|
||||||
golang.org/x/tools v0.1.12 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d // indirect
|
golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
|
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
120
go.sum
120
go.sum
@ -41,13 +41,16 @@ github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
|
|||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
|
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
|
||||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
|
||||||
|
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 h1:pami0oPhVosjOu/qRHepRmdjD6hGILF7DBr+qQZeP10=
|
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 h1:pami0oPhVosjOu/qRHepRmdjD6hGILF7DBr+qQZeP10=
|
||||||
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2/go.mod h1:jNIx5ykW1MroBuaTja9+VpglmaJOUzezumfhLlER3oY=
|
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2/go.mod h1:jNIx5ykW1MroBuaTja9+VpglmaJOUzezumfhLlER3oY=
|
||||||
|
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
||||||
|
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
@ -57,7 +60,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
|
|||||||
github.com/allegro/bigcache/v3 v3.0.2 h1:AKZCw+5eAaVyNTBmI2fgyPVJhHkdWder3O9IrprcQfI=
|
github.com/allegro/bigcache/v3 v3.0.2 h1:AKZCw+5eAaVyNTBmI2fgyPVJhHkdWder3O9IrprcQfI=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
|
||||||
|
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
@ -65,13 +69,14 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
|
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||||
|
github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA=
|
||||||
github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdPU=
|
github.com/c-robinson/iplib v1.0.3 h1:NG0UF0GoEsrC1/vyfX1Lx2Ss7CySWl3KqqXh3q4DdPU=
|
||||||
github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
|
github.com/c-robinson/iplib v1.0.3/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
|
||||||
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||||
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
||||||
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
@ -82,12 +87,6 @@ github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ
|
|||||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
|
||||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
|
||||||
github.com/coocood/freecache v1.2.1 h1:/v1CqMq45NFH9mp/Pt142reundeBM0dVUD3osQBeu/U=
|
github.com/coocood/freecache v1.2.1 h1:/v1CqMq45NFH9mp/Pt142reundeBM0dVUD3osQBeu/U=
|
||||||
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
|
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
|
||||||
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
|
||||||
@ -100,9 +99,12 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
|
github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg=
|
||||||
|
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
|
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
|
||||||
|
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
|
||||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
@ -114,22 +116,20 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||||
|
github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8=
|
||||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||||
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
|
||||||
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8=
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
||||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
|
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4=
|
||||||
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY=
|
||||||
@ -144,6 +144,7 @@ github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2
|
|||||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
|
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA=
|
||||||
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA=
|
||||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||||
@ -188,11 +189,14 @@ github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq
|
|||||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||||
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
|
||||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
||||||
@ -204,6 +208,7 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er
|
|||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||||
@ -230,8 +235,10 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
|||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
@ -321,6 +328,7 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
|
|||||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
@ -344,8 +352,9 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
|
|||||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
||||||
github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60 h1:tHdB+hQRHU10CfcK0furo6rSNgZ38JT8uPh70c/pFD8=
|
github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60 h1:tHdB+hQRHU10CfcK0furo6rSNgZ38JT8uPh70c/pFD8=
|
||||||
github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60/go.mod h1:aYbhishWc4Ai3I2U4Gaa2n3kHWSwzme6EsG/46HRQbE=
|
github.com/mdlayher/ethtool v0.0.0-20211028163843-288d040e9d60/go.mod h1:aYbhishWc4Ai3I2U4Gaa2n3kHWSwzme6EsG/46HRQbE=
|
||||||
@ -368,8 +377,8 @@ github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/
|
|||||||
github.com/mdlayher/socket v0.0.0-20211007213009-516dcbdf0267/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
|
github.com/mdlayher/socket v0.0.0-20211007213009-516dcbdf0267/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
|
||||||
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb h1:2dC7L10LmTqlyMVzFJ00qM25lqESg9Z4u3GuEXN5iHY=
|
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb h1:2dC7L10LmTqlyMVzFJ00qM25lqESg9Z4u3GuEXN5iHY=
|
||||||
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
|
github.com/mdlayher/socket v0.0.0-20211102153432-57e3fa563ecb/go.mod h1:nFZ1EtZYK8Gi/k6QNu7z7CgO20i/4ExeQswwWuPmG/g=
|
||||||
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
|
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||||
@ -412,17 +421,18 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
|||||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||||
|
github.com/open-policy-agent/opa v0.49.0 h1:TIlpCT1B5FSm8Dqo/a4t23gKmHkQysC3+7W77F99P4k=
|
||||||
|
github.com/open-policy-agent/opa v0.49.0/go.mod h1:WTLWtu498/QNTDkiHx76Xj7jaJUPvLJAPtdMkCcst0w=
|
||||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pegasus-kv/thrift v0.13.0 h1:4ESwaNoHImfbHa9RUGJiJZ4hrxorihZHk5aarYwY8d4=
|
github.com/pegasus-kv/thrift v0.13.0 h1:4ESwaNoHImfbHa9RUGJiJZ4hrxorihZHk5aarYwY8d4=
|
||||||
github.com/pegasus-kv/thrift v0.13.0/go.mod h1:Gl9NT/WHG6ABm6NsrbfE8LiJN0sAyneCrvB4qN4NPqQ=
|
github.com/pegasus-kv/thrift v0.13.0/go.mod h1:Gl9NT/WHG6ABm6NsrbfE8LiJN0sAyneCrvB4qN4NPqQ=
|
||||||
github.com/pion/dtls/v2 v2.2.4/go.mod h1:WGKfxqhrddne4Kg3p11FUMJrynkOY4lb25zHNO49wuw=
|
|
||||||
github.com/pion/dtls/v2 v2.2.6 h1:yXMxKr0Skd+Ub6A8UqXTRLSywskx93ooMRHsQUtd+Z4=
|
github.com/pion/dtls/v2 v2.2.6 h1:yXMxKr0Skd+Ub6A8UqXTRLSywskx93ooMRHsQUtd+Z4=
|
||||||
github.com/pion/dtls/v2 v2.2.6/go.mod h1:t8fWJCIquY5rlQZwA2yWxUS1+OCrAdXrhVKXB5oD/wY=
|
github.com/pion/dtls/v2 v2.2.6/go.mod h1:t8fWJCIquY5rlQZwA2yWxUS1+OCrAdXrhVKXB5oD/wY=
|
||||||
github.com/pion/ice/v2 v2.3.0 h1:G+ysriabk1p9wbySDpdsnlD+6ZspLlDLagRduRfzJPk=
|
github.com/pion/ice/v2 v2.3.1 h1:FQCmUfZe2Jpe7LYStVBOP6z1DiSzbIateih3TztgTjc=
|
||||||
github.com/pion/ice/v2 v2.3.0/go.mod h1:+xO/cXVnnVUr6D2ZJcCT5g9LngucUkkTvfnTMqUxKRM=
|
github.com/pion/ice/v2 v2.3.1/go.mod h1:aq2kc6MtYNcn4XmMhobAv6hTNJiHzvD0yXRz80+bnP8=
|
||||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||||
github.com/pion/mdns v0.0.7 h1:P0UB4Sr6xDWEox0kTVxF0LmQihtCbSAdW0H2nEgkA3U=
|
github.com/pion/mdns v0.0.7 h1:P0UB4Sr6xDWEox0kTVxF0LmQihtCbSAdW0H2nEgkA3U=
|
||||||
@ -432,12 +442,10 @@ github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TB
|
|||||||
github.com/pion/stun v0.4.0 h1:vgRrbBE2htWHy7l3Zsxckk7rkjnjOsSM7PHZnBwo8rk=
|
github.com/pion/stun v0.4.0 h1:vgRrbBE2htWHy7l3Zsxckk7rkjnjOsSM7PHZnBwo8rk=
|
||||||
github.com/pion/stun v0.4.0/go.mod h1:QPsh1/SbXASntw3zkkrIk3ZJVKz4saBY2G7S10P3wCw=
|
github.com/pion/stun v0.4.0/go.mod h1:QPsh1/SbXASntw3zkkrIk3ZJVKz4saBY2G7S10P3wCw=
|
||||||
github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc=
|
github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc=
|
||||||
github.com/pion/transport/v2 v2.0.1/go.mod h1:93OYg91+mrGxKW+Jrgzmqr80kgXqD7J0yybOrdr7w0Y=
|
|
||||||
github.com/pion/transport/v2 v2.0.2 h1:St+8o+1PEzPT51O9bv+tH/KYYLMNR5Vwm5Z3Qkjsywg=
|
github.com/pion/transport/v2 v2.0.2 h1:St+8o+1PEzPT51O9bv+tH/KYYLMNR5Vwm5Z3Qkjsywg=
|
||||||
github.com/pion/transport/v2 v2.0.2/go.mod h1:vrz6bUbFr/cjdwbnxq8OdDDzHf7JJfGsIRkxfpZoTA0=
|
github.com/pion/transport/v2 v2.0.2/go.mod h1:vrz6bUbFr/cjdwbnxq8OdDDzHf7JJfGsIRkxfpZoTA0=
|
||||||
github.com/pion/turn/v2 v2.1.0 h1:5wGHSgGhJhP/RpabkUb/T9PdsAjkGLS6toYz5HNzoSI=
|
github.com/pion/turn/v2 v2.1.0 h1:5wGHSgGhJhP/RpabkUb/T9PdsAjkGLS6toYz5HNzoSI=
|
||||||
github.com/pion/turn/v2 v2.1.0/go.mod h1:yrT5XbXSGX1VFSF31A3c1kCNB5bBZgk/uu5LET162qs=
|
github.com/pion/turn/v2 v2.1.0/go.mod h1:yrT5XbXSGX1VFSF31A3c1kCNB5bBZgk/uu5LET162qs=
|
||||||
github.com/pion/udp v0.1.4/go.mod h1:G8LDo56HsFwC24LIcnT4YIDU5qcB6NepqqjP0keL2us=
|
|
||||||
github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54=
|
github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54=
|
||||||
github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8=
|
github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
@ -453,13 +461,14 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
|
|||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||||
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
|
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||||
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||||
|
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||||
@ -473,7 +482,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
|||||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
|
||||||
|
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
@ -487,13 +497,12 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
|||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||||
github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs=
|
github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs=
|
||||||
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
|
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||||
@ -523,6 +532,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
|
||||||
|
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
@ -530,6 +541,12 @@ github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJ
|
|||||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
|
||||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||||
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
|
github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg=
|
||||||
|
github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
@ -545,6 +562,7 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
|||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
|
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||||
go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4=
|
go.opentelemetry.io/otel v1.11.1 h1:4WLLAmcfkmDk2ukNXJyq3/kiz/3UzCaYq6PskJsaou4=
|
||||||
go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE=
|
go.opentelemetry.io/otel v1.11.1/go.mod h1:1nNhXBbWSD0nsL38H6btgnFN2k4i0sNLHNNMZMSbUGE=
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.33.0 h1:xXhPj7SLKWU5/Zd4Hxmd+X1C4jdmvc0Xy+kvjFx2z60=
|
go.opentelemetry.io/otel/exporters/prometheus v0.33.0 h1:xXhPj7SLKWU5/Zd4Hxmd+X1C4jdmvc0Xy+kvjFx2z60=
|
||||||
@ -557,7 +575,6 @@ go.opentelemetry.io/otel/sdk/metric v0.33.0 h1:oTqyWfksgKoJmbrs2q7O7ahkJzt+Ipeki
|
|||||||
go.opentelemetry.io/otel/sdk/metric v0.33.0/go.mod h1:xdypMeA21JBOvjjzDUtD0kzIcHO/SPez+a8HOzJPGp0=
|
go.opentelemetry.io/otel/sdk/metric v0.33.0/go.mod h1:xdypMeA21JBOvjjzDUtD0kzIcHO/SPez+a8HOzJPGp0=
|
||||||
go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ=
|
go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ=
|
||||||
go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk=
|
go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
@ -569,8 +586,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
|||||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@ -607,8 +624,9 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -669,8 +687,9 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
|
|||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||||
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -689,8 +708,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -715,7 +735,6 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -773,18 +792,22 @@ golang.org/x/sys v0.0.0-20211214234402-4825e8c3871d/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||||
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -796,8 +819,9 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||||
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
@ -851,8 +875,9 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
|||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -913,7 +938,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
|
|||||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||||
@ -921,8 +945,8 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D
|
|||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0=
|
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c=
|
||||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
@ -935,11 +959,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
|
|||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ=
|
||||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
|
||||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
|
||||||
google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
|
|
||||||
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
@ -961,7 +982,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
|
@ -2,9 +2,12 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -25,6 +28,7 @@ var (
|
|||||||
Use: "netbird-mgmt",
|
Use: "netbird-mgmt",
|
||||||
Short: "",
|
Short: "",
|
||||||
Long: "",
|
Long: "",
|
||||||
|
Version: version.NetbirdVersion(),
|
||||||
SilenceUsage: true,
|
SilenceUsage: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.26.0
|
// protoc-gen-go v1.26.0
|
||||||
// protoc v3.21.9
|
// protoc v3.12.4
|
||||||
// source: management.proto
|
// source: management.proto
|
||||||
|
|
||||||
package proto
|
package proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
timestamp "github.com/golang/protobuf/ptypes/timestamp"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
sync "sync"
|
sync "sync"
|
||||||
)
|
)
|
||||||
@ -611,7 +611,7 @@ type ServerKeyResponse struct {
|
|||||||
// Server's Wireguard public key
|
// Server's Wireguard public key
|
||||||
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||||
// Key expiration timestamp after which the key should be fetched again by the client
|
// Key expiration timestamp after which the key should be fetched again by the client
|
||||||
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expiresAt,proto3" json:"expiresAt,omitempty"`
|
ExpiresAt *timestamp.Timestamp `protobuf:"bytes,2,opt,name=expiresAt,proto3" json:"expiresAt,omitempty"`
|
||||||
// Version of the Wiretrustee Management Service protocol
|
// Version of the Wiretrustee Management Service protocol
|
||||||
Version int32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"`
|
Version int32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"`
|
||||||
}
|
}
|
||||||
@ -655,7 +655,7 @@ func (x *ServerKeyResponse) GetKey() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ServerKeyResponse) GetExpiresAt() *timestamppb.Timestamp {
|
func (x *ServerKeyResponse) GetExpiresAt() *timestamp.Timestamp {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.ExpiresAt
|
return x.ExpiresAt
|
||||||
}
|
}
|
||||||
@ -993,6 +993,8 @@ type NetworkMap struct {
|
|||||||
Routes []*Route `protobuf:"bytes,5,rep,name=Routes,proto3" json:"Routes,omitempty"`
|
Routes []*Route `protobuf:"bytes,5,rep,name=Routes,proto3" json:"Routes,omitempty"`
|
||||||
// DNS config to be applied
|
// DNS config to be applied
|
||||||
DNSConfig *DNSConfig `protobuf:"bytes,6,opt,name=DNSConfig,proto3" json:"DNSConfig,omitempty"`
|
DNSConfig *DNSConfig `protobuf:"bytes,6,opt,name=DNSConfig,proto3" json:"DNSConfig,omitempty"`
|
||||||
|
// RemotePeerConfig represents a list of remote peers that the receiver can connect to
|
||||||
|
OfflinePeers []*RemotePeerConfig `protobuf:"bytes,7,rep,name=offlinePeers,proto3" json:"offlinePeers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *NetworkMap) Reset() {
|
func (x *NetworkMap) Reset() {
|
||||||
@ -1069,16 +1071,23 @@ func (x *NetworkMap) GetDNSConfig() *DNSConfig {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *NetworkMap) GetOfflinePeers() []*RemotePeerConfig {
|
||||||
|
if x != nil {
|
||||||
|
return x.OfflinePeers
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RemotePeerConfig represents a configuration of a remote peer.
|
// RemotePeerConfig represents a configuration of a remote peer.
|
||||||
// The properties are used to configure Wireguard Peers sections
|
// The properties are used to configure WireGuard Peers sections
|
||||||
type RemotePeerConfig struct {
|
type RemotePeerConfig struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
// A Wireguard public key of a remote peer
|
// A WireGuard public key of a remote peer
|
||||||
WgPubKey string `protobuf:"bytes,1,opt,name=wgPubKey,proto3" json:"wgPubKey,omitempty"`
|
WgPubKey string `protobuf:"bytes,1,opt,name=wgPubKey,proto3" json:"wgPubKey,omitempty"`
|
||||||
// Wireguard allowed IPs of a remote peer e.g. [10.30.30.1/32]
|
// WireGuard allowed IPs of a remote peer e.g. [10.30.30.1/32]
|
||||||
AllowedIps []string `protobuf:"bytes,2,rep,name=allowedIps,proto3" json:"allowedIps,omitempty"`
|
AllowedIps []string `protobuf:"bytes,2,rep,name=allowedIps,proto3" json:"allowedIps,omitempty"`
|
||||||
// SSHConfig is a SSH config of the remote peer. SSHConfig.sshPubKey should be ignored because peer knows it's SSH key.
|
// SSHConfig is a SSH config of the remote peer. SSHConfig.sshPubKey should be ignored because peer knows it's SSH key.
|
||||||
SshConfig *SSHConfig `protobuf:"bytes,3,opt,name=sshConfig,proto3" json:"sshConfig,omitempty"`
|
SshConfig *SSHConfig `protobuf:"bytes,3,opt,name=sshConfig,proto3" json:"sshConfig,omitempty"`
|
||||||
@ -1939,7 +1948,7 @@ var file_management_proto_rawDesc = []byte{
|
|||||||
0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
|
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,
|
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,
|
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, 0xac, 0x02, 0x0a, 0x0a, 0x4e, 0x65, 0x74,
|
0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x22, 0xee, 0x02, 0x0a, 0x0a, 0x4e, 0x65, 0x74,
|
||||||
0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61,
|
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,
|
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,
|
0x36, 0x0a, 0x0a, 0x70, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20,
|
||||||
@ -1958,123 +1967,127 @@ var file_management_proto_rawDesc = []byte{
|
|||||||
0x65, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18,
|
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,
|
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,
|
0x6e, 0x74, 0x2e, 0x44, 0x4e, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x44, 0x4e,
|
||||||
0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f,
|
0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x40, 0x0a, 0x0c, 0x6f, 0x66, 0x66, 0x6c, 0x69,
|
||||||
0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08,
|
0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
|
||||||
0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
|
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74,
|
||||||
0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f,
|
0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6f, 0x66, 0x66,
|
||||||
0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c,
|
0x6c, 0x69, 0x6e, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x22, 0x97, 0x01, 0x0a, 0x10, 0x52, 0x65,
|
||||||
0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43,
|
0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a,
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61,
|
0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66,
|
0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c,
|
||||||
0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a,
|
0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a,
|
||||||
0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64,
|
0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73,
|
||||||
0x6e, 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e,
|
0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e,
|
||||||
0x0a, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01,
|
0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f,
|
||||||
0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c,
|
0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
|
||||||
0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66,
|
||||||
0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e,
|
0x71, 0x64, 0x6e, 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
|
0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
|
||||||
0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf,
|
0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||||
0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
|
0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20,
|
||||||
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72,
|
0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20,
|
||||||
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d,
|
0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
|
||||||
0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,
|
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||||
0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f,
|
0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f,
|
||||||
0x77, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76,
|
0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08,
|
||||||
0x69, 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
|
0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c,
|
||||||
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d,
|
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69,
|
||||||
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, 0xda, 0x01, 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, 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, 0x7f, 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, 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, 0x32, 0xf7, 0x02, 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, 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,
|
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,
|
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, 0xda, 0x01, 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, 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, 0x7f,
|
||||||
|
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, 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, 0x32, 0xf7, 0x02, 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, 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,
|
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,
|
0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
|
||||||
0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
|
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
|
||||||
0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -2119,7 +2132,7 @@ var file_management_proto_goTypes = []interface{}{
|
|||||||
(*SimpleRecord)(nil), // 24: management.SimpleRecord
|
(*SimpleRecord)(nil), // 24: management.SimpleRecord
|
||||||
(*NameServerGroup)(nil), // 25: management.NameServerGroup
|
(*NameServerGroup)(nil), // 25: management.NameServerGroup
|
||||||
(*NameServer)(nil), // 26: management.NameServer
|
(*NameServer)(nil), // 26: management.NameServer
|
||||||
(*timestamppb.Timestamp)(nil), // 27: google.protobuf.Timestamp
|
(*timestamp.Timestamp)(nil), // 27: google.protobuf.Timestamp
|
||||||
}
|
}
|
||||||
var file_management_proto_depIdxs = []int32{
|
var file_management_proto_depIdxs = []int32{
|
||||||
11, // 0: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig
|
11, // 0: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig
|
||||||
@ -2141,28 +2154,29 @@ var file_management_proto_depIdxs = []int32{
|
|||||||
16, // 16: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig
|
16, // 16: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig
|
||||||
21, // 17: management.NetworkMap.Routes:type_name -> management.Route
|
21, // 17: management.NetworkMap.Routes:type_name -> management.Route
|
||||||
22, // 18: management.NetworkMap.DNSConfig:type_name -> management.DNSConfig
|
22, // 18: management.NetworkMap.DNSConfig:type_name -> management.DNSConfig
|
||||||
17, // 19: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig
|
16, // 19: management.NetworkMap.offlinePeers:type_name -> management.RemotePeerConfig
|
||||||
1, // 20: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider
|
17, // 20: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig
|
||||||
20, // 21: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig
|
1, // 21: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider
|
||||||
25, // 22: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup
|
20, // 22: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig
|
||||||
23, // 23: management.DNSConfig.CustomZones:type_name -> management.CustomZone
|
25, // 23: management.DNSConfig.NameServerGroups:type_name -> management.NameServerGroup
|
||||||
24, // 24: management.CustomZone.Records:type_name -> management.SimpleRecord
|
23, // 24: management.DNSConfig.CustomZones:type_name -> management.CustomZone
|
||||||
26, // 25: management.NameServerGroup.NameServers:type_name -> management.NameServer
|
24, // 25: management.CustomZone.Records:type_name -> management.SimpleRecord
|
||||||
2, // 26: management.ManagementService.Login:input_type -> management.EncryptedMessage
|
26, // 26: management.NameServerGroup.NameServers:type_name -> management.NameServer
|
||||||
2, // 27: management.ManagementService.Sync:input_type -> management.EncryptedMessage
|
2, // 27: management.ManagementService.Login:input_type -> management.EncryptedMessage
|
||||||
10, // 28: management.ManagementService.GetServerKey:input_type -> management.Empty
|
2, // 28: management.ManagementService.Sync:input_type -> management.EncryptedMessage
|
||||||
10, // 29: management.ManagementService.isHealthy:input_type -> management.Empty
|
10, // 29: management.ManagementService.GetServerKey:input_type -> management.Empty
|
||||||
2, // 30: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage
|
10, // 30: management.ManagementService.isHealthy:input_type -> management.Empty
|
||||||
2, // 31: management.ManagementService.Login:output_type -> management.EncryptedMessage
|
2, // 31: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage
|
||||||
2, // 32: management.ManagementService.Sync:output_type -> management.EncryptedMessage
|
2, // 32: management.ManagementService.Login:output_type -> management.EncryptedMessage
|
||||||
9, // 33: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse
|
2, // 33: management.ManagementService.Sync:output_type -> management.EncryptedMessage
|
||||||
10, // 34: management.ManagementService.isHealthy:output_type -> management.Empty
|
9, // 34: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse
|
||||||
2, // 35: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage
|
10, // 35: management.ManagementService.isHealthy:output_type -> management.Empty
|
||||||
31, // [31:36] is the sub-list for method output_type
|
2, // 36: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage
|
||||||
26, // [26:31] is the sub-list for method input_type
|
32, // [32:37] is the sub-list for method output_type
|
||||||
26, // [26:26] is the sub-list for extension type_name
|
27, // [27:32] is the sub-list for method input_type
|
||||||
26, // [26:26] is the sub-list for extension extendee
|
27, // [27:27] is the sub-list for extension type_name
|
||||||
0, // [0:26] is the sub-list for field type_name
|
27, // [27:27] is the sub-list for extension extendee
|
||||||
|
0, // [0:27] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_management_proto_init() }
|
func init() { file_management_proto_init() }
|
||||||
|
@ -183,16 +183,19 @@ message NetworkMap {
|
|||||||
|
|
||||||
// DNS config to be applied
|
// DNS config to be applied
|
||||||
DNSConfig DNSConfig = 6;
|
DNSConfig DNSConfig = 6;
|
||||||
|
|
||||||
|
// RemotePeerConfig represents a list of remote peers that the receiver can connect to
|
||||||
|
repeated RemotePeerConfig offlinePeers = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemotePeerConfig represents a configuration of a remote peer.
|
// RemotePeerConfig represents a configuration of a remote peer.
|
||||||
// The properties are used to configure Wireguard Peers sections
|
// The properties are used to configure WireGuard Peers sections
|
||||||
message RemotePeerConfig {
|
message RemotePeerConfig {
|
||||||
|
|
||||||
// A Wireguard public key of a remote peer
|
// A WireGuard public key of a remote peer
|
||||||
string wgPubKey = 1;
|
string wgPubKey = 1;
|
||||||
|
|
||||||
// Wireguard allowed IPs of a remote peer e.g. [10.30.30.1/32]
|
// WireGuard allowed IPs of a remote peer e.g. [10.30.30.1/32]
|
||||||
repeated string allowedIps = 2;
|
repeated string allowedIps = 2;
|
||||||
|
|
||||||
// SSHConfig is a SSH config of the remote peer. SSHConfig.sshPubKey should be ignored because peer knows it's SSH key.
|
// SSHConfig is a SSH config of the remote peer. SSHConfig.sshPubKey should be ignored because peer knows it's SSH key.
|
||||||
|
@ -49,21 +49,18 @@ type AccountManager interface {
|
|||||||
SaveUser(accountID, userID string, update *User) (*UserInfo, error)
|
SaveUser(accountID, userID string, update *User) (*UserInfo, error)
|
||||||
GetSetupKey(accountID, userID, keyID string) (*SetupKey, error)
|
GetSetupKey(accountID, userID, keyID string) (*SetupKey, error)
|
||||||
GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error)
|
GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error)
|
||||||
GetAccountByPeerID(peerID string) (*Account, error)
|
|
||||||
GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*Account, *User, error)
|
GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*Account, *User, error)
|
||||||
IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error)
|
IsUserAdmin(claims jwtclaims.AuthorizationClaims) (bool, error)
|
||||||
AccountExists(accountId string) (*bool, error)
|
AccountExists(accountId string) (*bool, error)
|
||||||
GetPeerByKey(peerKey string) (*Peer, error)
|
GetPeerByKey(peerKey string) (*Peer, error)
|
||||||
GetPeers(accountID, userID string) ([]*Peer, error)
|
GetPeers(accountID, userID string) ([]*Peer, error)
|
||||||
MarkPeerConnected(peerKey string, connected bool) error
|
MarkPeerConnected(peerKey string, connected bool) error
|
||||||
MarkPeerLoginExpired(peerPubKey string, loginExpired bool) error
|
|
||||||
DeletePeer(accountID, peerID, userID string) (*Peer, error)
|
DeletePeer(accountID, peerID, userID string) (*Peer, error)
|
||||||
GetPeerByIP(accountId string, peerIP string) (*Peer, error)
|
GetPeerByIP(accountId string, peerIP string) (*Peer, error)
|
||||||
UpdatePeer(accountID, userID string, peer *Peer) (*Peer, error)
|
UpdatePeer(accountID, userID string, peer *Peer) (*Peer, error)
|
||||||
GetNetworkMap(peerID string) (*NetworkMap, error)
|
GetNetworkMap(peerID string) (*NetworkMap, error)
|
||||||
GetPeerNetwork(peerID string) (*Network, error)
|
GetPeerNetwork(peerID string) (*Network, error)
|
||||||
AddPeer(setupKey, userID string, peer *Peer) (*Peer, error)
|
AddPeer(setupKey, userID string, peer *Peer) (*Peer, *NetworkMap, error)
|
||||||
UpdatePeerMeta(peerID string, meta PeerSystemMeta) error
|
|
||||||
UpdatePeerSSHKey(peerID string, sshKey string) error
|
UpdatePeerSSHKey(peerID string, sshKey string) error
|
||||||
GetUsersFromAccount(accountID, userID string) ([]*UserInfo, error)
|
GetUsersFromAccount(accountID, userID string) ([]*UserInfo, error)
|
||||||
GetGroup(accountId, groupID string) (*Group, error)
|
GetGroup(accountId, groupID string) (*Group, error)
|
||||||
@ -74,11 +71,10 @@ type AccountManager interface {
|
|||||||
GroupAddPeer(accountId, groupID, peerID string) error
|
GroupAddPeer(accountId, groupID, peerID string) error
|
||||||
GroupDeletePeer(accountId, groupID, peerKey string) error
|
GroupDeletePeer(accountId, groupID, peerKey string) error
|
||||||
GroupListPeers(accountId, groupID string) ([]*Peer, error)
|
GroupListPeers(accountId, groupID string) ([]*Peer, error)
|
||||||
GetRule(accountID, ruleID, userID string) (*Rule, error)
|
GetPolicy(accountID, policyID, userID string) (*Policy, error)
|
||||||
SaveRule(accountID, userID string, rule *Rule) error
|
SavePolicy(accountID, userID string, policy *Policy) error
|
||||||
UpdateRule(accountID string, ruleID string, operations []RuleUpdateOperation) (*Rule, error)
|
DeletePolicy(accountID, policyID, userID string) error
|
||||||
DeleteRule(accountID, ruleID, userID string) error
|
ListPolicies(accountID, userID string) ([]*Policy, error)
|
||||||
ListRules(accountID, userID string) ([]*Rule, error)
|
|
||||||
GetRoute(accountID, routeID, userID string) (*route.Route, error)
|
GetRoute(accountID, routeID, userID string) (*route.Route, error)
|
||||||
CreateRoute(accountID string, prefix, peerID, description, netID string, masquerade bool, metric int, groups []string, enabled bool, userID string) (*route.Route, error)
|
CreateRoute(accountID string, prefix, peerID, description, netID string, masquerade bool, metric int, groups []string, enabled bool, userID string) (*route.Route, error)
|
||||||
SaveRoute(accountID, userID string, route *route.Route) error
|
SaveRoute(accountID, userID string, route *route.Route) error
|
||||||
@ -96,8 +92,9 @@ type AccountManager interface {
|
|||||||
GetDNSSettings(accountID string, userID string) (*DNSSettings, error)
|
GetDNSSettings(accountID string, userID string) (*DNSSettings, error)
|
||||||
SaveDNSSettings(accountID string, userID string, dnsSettingsToSave *DNSSettings) error
|
SaveDNSSettings(accountID string, userID string, dnsSettingsToSave *DNSSettings) error
|
||||||
GetPeer(accountID, peerID, userID string) (*Peer, error)
|
GetPeer(accountID, peerID, userID string) (*Peer, error)
|
||||||
UpdatePeerLastLogin(peerID string) error
|
|
||||||
UpdateAccountSettings(accountID, userID string, newSettings *Settings) (*Account, error)
|
UpdateAccountSettings(accountID, userID string, newSettings *Settings) (*Account, error)
|
||||||
|
LoginPeer(login PeerLogin) (*Peer, *NetworkMap, error) // used by peer gRPC API
|
||||||
|
SyncPeer(sync PeerSync) (*Peer, *NetworkMap, error) // used by peer gRPC API
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultAccountManager struct {
|
type DefaultAccountManager struct {
|
||||||
@ -154,6 +151,7 @@ type Account struct {
|
|||||||
Users map[string]*User
|
Users map[string]*User
|
||||||
Groups map[string]*Group
|
Groups map[string]*Group
|
||||||
Rules map[string]*Rule
|
Rules map[string]*Rule
|
||||||
|
Policies []*Policy
|
||||||
Routes map[string]*route.Route
|
Routes map[string]*route.Route
|
||||||
NameServerGroups map[string]*nbdns.NameServerGroup
|
NameServerGroups map[string]*nbdns.NameServerGroup
|
||||||
DNSSettings *DNSSettings
|
DNSSettings *DNSSettings
|
||||||
@ -267,47 +265,52 @@ func (a *Account) GetPeerByIP(peerIP string) *Peer {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPeerRules returns a list of source or destination rules of a given peer.
|
|
||||||
func (a *Account) GetPeerRules(peerID string) (srcRules []*Rule, dstRules []*Rule) {
|
|
||||||
// Rules are group based so there is no direct access to peers.
|
|
||||||
// First, find all groups that the given peer belongs to
|
|
||||||
peerGroups := make(map[string]struct{})
|
|
||||||
|
|
||||||
for s, group := range a.Groups {
|
|
||||||
for _, peer := range group.Peers {
|
|
||||||
if peerID == peer {
|
|
||||||
peerGroups[s] = struct{}{}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second, find all rules that have discovered source and destination groups
|
|
||||||
srcRulesMap := make(map[string]*Rule)
|
|
||||||
dstRulesMap := make(map[string]*Rule)
|
|
||||||
for _, rule := range a.Rules {
|
|
||||||
for _, g := range rule.Source {
|
|
||||||
if _, ok := peerGroups[g]; ok && srcRulesMap[rule.ID] == nil {
|
|
||||||
srcRules = append(srcRules, rule)
|
|
||||||
srcRulesMap[rule.ID] = rule
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, g := range rule.Destination {
|
|
||||||
if _, ok := peerGroups[g]; ok && dstRulesMap[rule.ID] == nil {
|
|
||||||
dstRules = append(dstRules, rule)
|
|
||||||
dstRulesMap[rule.ID] = rule
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return srcRules, dstRules
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetGroup returns a group by ID if exists, nil otherwise
|
// GetGroup returns a group by ID if exists, nil otherwise
|
||||||
func (a *Account) GetGroup(groupID string) *Group {
|
func (a *Account) GetGroup(groupID string) *Group {
|
||||||
return a.Groups[groupID]
|
return a.Groups[groupID]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPeerNetworkMap returns a group by ID if exists, nil otherwise
|
||||||
|
func (a *Account) GetPeerNetworkMap(peerID, dnsDomain string) *NetworkMap {
|
||||||
|
aclPeers, _ := a.getPeersByPolicy(peerID)
|
||||||
|
// exclude expired peers
|
||||||
|
var peersToConnect []*Peer
|
||||||
|
var expiredPeers []*Peer
|
||||||
|
for _, p := range aclPeers {
|
||||||
|
expired, _ := p.LoginExpired(a.Settings.PeerLoginExpiration)
|
||||||
|
if a.Settings.PeerLoginExpirationEnabled && expired {
|
||||||
|
expiredPeers = append(expiredPeers, p)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
peersToConnect = append(peersToConnect, p)
|
||||||
|
}
|
||||||
|
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
|
||||||
|
routesUpdate := a.getRoutesToSync(peerID, peersToConnect)
|
||||||
|
|
||||||
|
dnsManagementStatus := a.getPeerDNSManagementStatus(peerID)
|
||||||
|
dnsUpdate := nbdns.Config{
|
||||||
|
ServiceEnable: dnsManagementStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
if dnsManagementStatus {
|
||||||
|
var zones []nbdns.CustomZone
|
||||||
|
peersCustomZone := getPeersCustomZone(a, dnsDomain)
|
||||||
|
if peersCustomZone.Domain != "" {
|
||||||
|
zones = append(zones, peersCustomZone)
|
||||||
|
}
|
||||||
|
dnsUpdate.CustomZones = zones
|
||||||
|
dnsUpdate.NameServerGroups = getPeerNSGroups(a, peerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &NetworkMap{
|
||||||
|
Peers: peersToConnect,
|
||||||
|
Network: a.Network.Copy(),
|
||||||
|
Routes: routesUpdate,
|
||||||
|
DNSConfig: dnsUpdate,
|
||||||
|
OfflinePeers: expiredPeers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetExpiredPeers returns peers that have been expired
|
// GetExpiredPeers returns peers that have been expired
|
||||||
func (a *Account) GetExpiredPeers() []*Peer {
|
func (a *Account) GetExpiredPeers() []*Peer {
|
||||||
var peers []*Peer
|
var peers []*Peer
|
||||||
@ -325,7 +328,6 @@ func (a *Account) GetExpiredPeers() []*Peer {
|
|||||||
// If there is no peer that expires this function returns false and a duration of 0.
|
// If there is no peer that expires this function returns false and a duration of 0.
|
||||||
// This function only considers peers that haven't been expired yet and that are connected.
|
// This function only considers peers that haven't been expired yet and that are connected.
|
||||||
func (a *Account) GetNextPeerExpiration() (time.Duration, bool) {
|
func (a *Account) GetNextPeerExpiration() (time.Duration, bool) {
|
||||||
|
|
||||||
peersWithExpiry := a.GetPeersWithExpiration()
|
peersWithExpiry := a.GetPeersWithExpiration()
|
||||||
if len(peersWithExpiry) == 0 {
|
if len(peersWithExpiry) == 0 {
|
||||||
return 0, false
|
return 0, false
|
||||||
@ -417,7 +419,6 @@ func (a *Account) FindPeerByPubKey(peerPubKey string) (*Peer, error) {
|
|||||||
|
|
||||||
// FindUserPeers returns a list of peers that user owns (created)
|
// FindUserPeers returns a list of peers that user owns (created)
|
||||||
func (a *Account) FindUserPeers(userID string) ([]*Peer, error) {
|
func (a *Account) FindUserPeers(userID string) ([]*Peer, error) {
|
||||||
|
|
||||||
peers := make([]*Peer, 0)
|
peers := make([]*Peer, 0)
|
||||||
for _, peer := range a.Peers {
|
for _, peer := range a.Peers {
|
||||||
if peer.UserID == userID {
|
if peer.UserID == userID {
|
||||||
@ -537,6 +538,11 @@ func (a *Account) Copy() *Account {
|
|||||||
rules[id] = rule.Copy()
|
rules[id] = rule.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
policies := []*Policy{}
|
||||||
|
for _, policy := range a.Policies {
|
||||||
|
policies = append(policies, policy.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
routes := map[string]*route.Route{}
|
routes := map[string]*route.Route{}
|
||||||
for id, route := range a.Routes {
|
for id, route := range a.Routes {
|
||||||
routes[id] = route.Copy()
|
routes[id] = route.Copy()
|
||||||
@ -569,6 +575,7 @@ func (a *Account) Copy() *Account {
|
|||||||
Users: users,
|
Users: users,
|
||||||
Groups: groups,
|
Groups: groups,
|
||||||
Rules: rules,
|
Rules: rules,
|
||||||
|
Policies: policies,
|
||||||
Routes: routes,
|
Routes: routes,
|
||||||
NameServerGroups: nsGroups,
|
NameServerGroups: nsGroups,
|
||||||
DNSSettings: dnsSettings,
|
DNSSettings: dnsSettings,
|
||||||
@ -626,7 +633,9 @@ func BuildManager(store Store, peersUpdateManager *PeersUpdateManager, idpManage
|
|||||||
|
|
||||||
_, err := account.GetGroupAll()
|
_, err := account.GetGroupAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
addAllGroup(account)
|
if err := addAllGroup(account); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
shouldSave = true
|
shouldSave = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -662,7 +671,6 @@ func BuildManager(store Store, peersUpdateManager *PeersUpdateManager, idpManage
|
|||||||
// User that performs the update has to belong to the account.
|
// User that performs the update has to belong to the account.
|
||||||
// Returns an updated Account
|
// Returns an updated Account
|
||||||
func (am *DefaultAccountManager) UpdateAccountSettings(accountID, userID string, newSettings *Settings) (*Account, error) {
|
func (am *DefaultAccountManager) UpdateAccountSettings(accountID, userID string, newSettings *Settings) (*Account, error) {
|
||||||
|
|
||||||
halfYearLimit := 180 * 24 * time.Hour
|
halfYearLimit := 180 * 24 * time.Hour
|
||||||
if newSettings.PeerLoginExpiration > halfYearLimit {
|
if newSettings.PeerLoginExpiration > halfYearLimit {
|
||||||
return nil, status.Errorf(status.InvalidArgument, "peer login expiration can't be larger than 180 days")
|
return nil, status.Errorf(status.InvalidArgument, "peer login expiration can't be larger than 180 days")
|
||||||
@ -747,7 +755,7 @@ func (am *DefaultAccountManager) peerLoginExpirationJob(accountID string) func()
|
|||||||
if len(peerIDs) != 0 {
|
if len(peerIDs) != 0 {
|
||||||
// this will trigger peer disconnect from the management service
|
// this will trigger peer disconnect from the management service
|
||||||
am.peersUpdateManager.CloseChannels(peerIDs)
|
am.peersUpdateManager.CloseChannels(peerIDs)
|
||||||
err := am.updateAccountPeers(account)
|
err = am.updateAccountPeers(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed updating account peers while expiring peers for account %s", accountID)
|
log.Errorf("failed updating account peers while expiring peers for account %s", accountID)
|
||||||
return account.GetNextPeerExpiration()
|
return account.GetNextPeerExpiration()
|
||||||
@ -803,11 +811,6 @@ func (am *DefaultAccountManager) warmupIDPCache() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountByPeerID returns account from the store by a provided peer ID
|
|
||||||
func (am *DefaultAccountManager) GetAccountByPeerID(peerID string) (*Account, error) {
|
|
||||||
return am.Store.GetAccountByPeerID(peerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAccountByUserOrAccountID looks for an account by user or accountID, if no account is provided and
|
// GetAccountByUserOrAccountID looks for an account by user or accountID, if no account is provided and
|
||||||
// userID doesn't have an account associated with it, one account is created
|
// userID doesn't have an account associated with it, one account is created
|
||||||
func (am *DefaultAccountManager) GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error) {
|
func (am *DefaultAccountManager) GetAccountByUserOrAccountID(userID, accountID, domain string) (*Account, error) {
|
||||||
@ -850,10 +853,6 @@ func (am *DefaultAccountManager) addAccountIDToIDPAppMeta(userID string, account
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = am.idpManager.UpdateUserAppMetadata(userID, idp.AppMetadata{WTAccountID: account.Id})
|
err = am.idpManager.UpdateUserAppMetadata(userID, idp.AppMetadata{WTAccountID: account.Id})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status.Errorf(status.Internal, "updating user's app metadata failed with: %v", err)
|
return status.Errorf(status.Internal, "updating user's app metadata failed with: %v", err)
|
||||||
}
|
}
|
||||||
@ -1242,7 +1241,7 @@ func (am *DefaultAccountManager) GetDNSDomain() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addAllGroup to account object if it doesn't exists
|
// addAllGroup to account object if it doesn't exists
|
||||||
func addAllGroup(account *Account) {
|
func addAllGroup(account *Account) error {
|
||||||
if len(account.Groups) == 0 {
|
if len(account.Groups) == 0 {
|
||||||
allGroup := &Group{
|
allGroup := &Group{
|
||||||
ID: xid.New().String(),
|
ID: xid.New().String(),
|
||||||
@ -1262,7 +1261,15 @@ func addAllGroup(account *Account) {
|
|||||||
Destination: []string{allGroup.ID},
|
Destination: []string{allGroup.ID},
|
||||||
}
|
}
|
||||||
account.Rules = map[string]*Rule{defaultRule.ID: defaultRule}
|
account.Rules = map[string]*Rule{defaultRule.ID: defaultRule}
|
||||||
|
|
||||||
|
// TODO: after migration we need to drop rule and create policy directly
|
||||||
|
defaultPolicy, err := RuleToPolicy(defaultRule)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("convert rule to policy: %w", err)
|
||||||
|
}
|
||||||
|
account.Policies = []*Policy{defaultPolicy}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newAccountWithId creates a new Account with a default SetupKey (doesn't store in a Store) and provided id
|
// newAccountWithId creates a new Account with a default SetupKey (doesn't store in a Store) and provided id
|
||||||
@ -1303,7 +1310,9 @@ func newAccountWithId(accountId, userId, domain string) *Account {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
addAllGroup(acc)
|
if err := addAllGroup(acc); err != nil {
|
||||||
|
log.Errorf("error adding all group to account %s: %v", acc.Id, err)
|
||||||
|
}
|
||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,8 @@ import (
|
|||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
|
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
@ -40,7 +42,7 @@ func verifyCanAddPeerToAccount(t *testing.T, manager AccountManager, account *Ac
|
|||||||
setupKey = key.Key
|
setupKey = key.Key
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := manager.AddPeer(setupKey, userID, peer)
|
_, _, err := manager.AddPeer(setupKey, userID, peer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("expected to add new peer successfully after creating new account, but failed", err)
|
t.Error("expected to add new peer successfully after creating new account, but failed", err)
|
||||||
}
|
}
|
||||||
@ -100,8 +102,123 @@ func verifyNewAccountHasDefaultFields(t *testing.T, account *Account, createdBy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewAccount(t *testing.T) {
|
func TestAccount_GetPeerNetworkMap(t *testing.T) {
|
||||||
|
peerID1 := "peer-1"
|
||||||
|
peerID2 := "peer-2"
|
||||||
|
tt := []struct {
|
||||||
|
name string
|
||||||
|
accountSettings Settings
|
||||||
|
peerID string
|
||||||
|
expectedPeers []string
|
||||||
|
expectedOfflinePeers []string
|
||||||
|
peers map[string]*Peer
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Should return ALL peers when global peer login expiration disabled",
|
||||||
|
accountSettings: Settings{PeerLoginExpirationEnabled: false, PeerLoginExpiration: time.Hour},
|
||||||
|
peerID: peerID1,
|
||||||
|
expectedPeers: []string{peerID2},
|
||||||
|
expectedOfflinePeers: []string{},
|
||||||
|
peers: map[string]*Peer{
|
||||||
|
"peer-1": {
|
||||||
|
ID: peerID1,
|
||||||
|
Key: "peer-1-key",
|
||||||
|
IP: net.IP{100, 64, 0, 1},
|
||||||
|
Name: peerID1,
|
||||||
|
DNSLabel: peerID1,
|
||||||
|
Status: &PeerStatus{
|
||||||
|
LastSeen: time.Now(),
|
||||||
|
Connected: false,
|
||||||
|
LoginExpired: true,
|
||||||
|
},
|
||||||
|
UserID: userID,
|
||||||
|
LastLogin: time.Now().Add(-time.Hour * 24 * 30 * 30),
|
||||||
|
},
|
||||||
|
"peer-2": {
|
||||||
|
ID: peerID2,
|
||||||
|
Key: "peer-2-key",
|
||||||
|
IP: net.IP{100, 64, 0, 1},
|
||||||
|
Name: peerID2,
|
||||||
|
DNSLabel: peerID2,
|
||||||
|
Status: &PeerStatus{
|
||||||
|
LastSeen: time.Now(),
|
||||||
|
Connected: false,
|
||||||
|
LoginExpired: false,
|
||||||
|
},
|
||||||
|
UserID: userID,
|
||||||
|
LastLogin: time.Now(),
|
||||||
|
LoginExpirationEnabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Should return no peers when global peer login expiration enabled and peers expired",
|
||||||
|
accountSettings: Settings{PeerLoginExpirationEnabled: true, PeerLoginExpiration: time.Hour},
|
||||||
|
peerID: peerID1,
|
||||||
|
expectedPeers: []string{},
|
||||||
|
expectedOfflinePeers: []string{peerID2},
|
||||||
|
peers: map[string]*Peer{
|
||||||
|
"peer-1": {
|
||||||
|
ID: peerID1,
|
||||||
|
Key: "peer-1-key",
|
||||||
|
IP: net.IP{100, 64, 0, 1},
|
||||||
|
Name: peerID1,
|
||||||
|
DNSLabel: peerID1,
|
||||||
|
Status: &PeerStatus{
|
||||||
|
LastSeen: time.Now(),
|
||||||
|
Connected: false,
|
||||||
|
LoginExpired: true,
|
||||||
|
},
|
||||||
|
UserID: userID,
|
||||||
|
LastLogin: time.Now().Add(-time.Hour * 24 * 30 * 30),
|
||||||
|
LoginExpirationEnabled: true,
|
||||||
|
},
|
||||||
|
"peer-2": {
|
||||||
|
ID: peerID2,
|
||||||
|
Key: "peer-2-key",
|
||||||
|
IP: net.IP{100, 64, 0, 1},
|
||||||
|
Name: peerID2,
|
||||||
|
DNSLabel: peerID2,
|
||||||
|
Status: &PeerStatus{
|
||||||
|
LastSeen: time.Now(),
|
||||||
|
Connected: false,
|
||||||
|
LoginExpired: true,
|
||||||
|
},
|
||||||
|
UserID: userID,
|
||||||
|
LastLogin: time.Now().Add(-time.Hour * 24 * 30 * 30),
|
||||||
|
LoginExpirationEnabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
netIP := net.IP{100, 64, 0, 0}
|
||||||
|
netMask := net.IPMask{255, 255, 0, 0}
|
||||||
|
network := &Network{
|
||||||
|
Id: "network",
|
||||||
|
Net: net.IPNet{IP: netIP, Mask: netMask},
|
||||||
|
Dns: "netbird.selfhosted",
|
||||||
|
Serial: 0,
|
||||||
|
mu: sync.Mutex{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range tt {
|
||||||
|
account := newAccountWithId("account-1", userID, "netbird.io")
|
||||||
|
account.Network = network
|
||||||
|
account.Peers = testCase.peers
|
||||||
|
for _, peer := range account.Peers {
|
||||||
|
all, _ := account.GetGroupAll()
|
||||||
|
account.Groups[all.ID].Peers = append(account.Groups[all.ID].Peers, peer.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
networkMap := account.GetPeerNetworkMap(testCase.peerID, "netbird.io")
|
||||||
|
assert.Len(t, networkMap.Peers, len(testCase.expectedPeers))
|
||||||
|
assert.Len(t, networkMap.OfflinePeers, len(testCase.expectedOfflinePeers))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewAccount(t *testing.T) {
|
||||||
domain := "netbird.io"
|
domain := "netbird.io"
|
||||||
userId := "account_creator"
|
userId := "account_creator"
|
||||||
accountID := "account_id"
|
accountID := "account_id"
|
||||||
@ -544,10 +661,9 @@ func TestAccountManager_AddPeer(t *testing.T) {
|
|||||||
expectedPeerKey := key.PublicKey().String()
|
expectedPeerKey := key.PublicKey().String()
|
||||||
expectedSetupKey := setupKey.Key
|
expectedSetupKey := setupKey.Key
|
||||||
|
|
||||||
peer, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
peer, _, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
||||||
Key: expectedPeerKey,
|
Key: expectedPeerKey,
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: expectedPeerKey},
|
||||||
Name: expectedPeerKey,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting peer to be added, got failure %v", err)
|
t.Errorf("expecting peer to be added, got failure %v", err)
|
||||||
@ -613,10 +729,9 @@ func TestAccountManager_AddPeerWithUserID(t *testing.T) {
|
|||||||
expectedPeerKey := key.PublicKey().String()
|
expectedPeerKey := key.PublicKey().String()
|
||||||
expectedUserID := userID
|
expectedUserID := userID
|
||||||
|
|
||||||
peer, err := manager.AddPeer("", userID, &Peer{
|
peer, _, err := manager.AddPeer("", userID, &Peer{
|
||||||
Key: expectedPeerKey,
|
Key: expectedPeerKey,
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: expectedPeerKey},
|
||||||
Name: expectedPeerKey,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting peer to be added, got failure %v, account users: %v", err, account.CreatedBy)
|
t.Errorf("expecting peer to be added, got failure %v, account users: %v", err, account.CreatedBy)
|
||||||
@ -696,10 +811,9 @@ func TestAccountManager_NetworkUpdates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
expectedPeerKey := key.PublicKey().String()
|
expectedPeerKey := key.PublicKey().String()
|
||||||
|
|
||||||
peer, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
peer, _, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
||||||
Key: expectedPeerKey,
|
Key: expectedPeerKey,
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: expectedPeerKey},
|
||||||
Name: expectedPeerKey,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expecting peer1 to be added, got failure %v", err)
|
t.Fatalf("expecting peer1 to be added, got failure %v", err)
|
||||||
@ -728,10 +842,20 @@ func TestAccountManager_NetworkUpdates(t *testing.T) {
|
|||||||
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
Peers: []string{peer1.ID, peer2.ID, peer3.ID},
|
||||||
}
|
}
|
||||||
|
|
||||||
rule := Rule{
|
policy := Policy{
|
||||||
Source: []string{"group-id"},
|
Enabled: true,
|
||||||
Destination: []string{"group-id"},
|
Rules: []*PolicyRule{
|
||||||
Flow: TrafficFlowBidirect,
|
{
|
||||||
|
Enabled: true,
|
||||||
|
Sources: []string{"group-id"},
|
||||||
|
Destinations: []string{"group-id"},
|
||||||
|
Action: PolicyTrafficActionAccept,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := policy.UpdateQueryFromRules(); err != nil {
|
||||||
|
t.Errorf("update policy query from rules: %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
@ -755,7 +879,7 @@ func TestAccountManager_NetworkUpdates(t *testing.T) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("delete rule update", func(t *testing.T) {
|
t.Run("delete policy update", func(t *testing.T) {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
@ -767,12 +891,7 @@ func TestAccountManager_NetworkUpdates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var defaultRule *Rule
|
if err := manager.DeletePolicy(account.Id, account.Policies[0].ID, userID); err != nil {
|
||||||
for _, r := range account.Rules {
|
|
||||||
defaultRule = r
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := manager.DeleteRule(account.Id, defaultRule.ID, userID); err != nil {
|
|
||||||
t.Errorf("delete default rule: %v", err)
|
t.Errorf("delete default rule: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -780,7 +899,7 @@ func TestAccountManager_NetworkUpdates(t *testing.T) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("save rule update", func(t *testing.T) {
|
t.Run("save policy update", func(t *testing.T) {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
@ -792,7 +911,7 @@ func TestAccountManager_NetworkUpdates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if err := manager.SaveRule(account.Id, userID, &rule); err != nil {
|
if err := manager.SavePolicy(account.Id, userID, &policy); err != nil {
|
||||||
t.Errorf("delete default rule: %v", err)
|
t.Errorf("delete default rule: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -833,7 +952,7 @@ func TestAccountManager_NetworkUpdates(t *testing.T) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
if err := manager.DeleteGroup(account.Id, group.ID); err != nil {
|
if err := manager.DeleteGroup(account.Id, group.ID); err != nil {
|
||||||
t.Errorf("delete group rule: %v", err)
|
t.Errorf("delete group: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,10 +985,9 @@ func TestAccountManager_DeletePeer(t *testing.T) {
|
|||||||
|
|
||||||
peerKey := key.PublicKey().String()
|
peerKey := key.PublicKey().String()
|
||||||
|
|
||||||
peer, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
peer, _, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
||||||
Key: peerKey,
|
Key: peerKey,
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: peerKey},
|
||||||
Name: peerKey,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting peer to be added, got failure %v", err)
|
t.Errorf("expecting peer to be added, got failure %v", err)
|
||||||
@ -901,6 +1019,7 @@ func TestAccountManager_DeletePeer(t *testing.T) {
|
|||||||
assert.Equal(t, peer.IP.String(), ev.TargetID)
|
assert.Equal(t, peer.IP.String(), ev.TargetID)
|
||||||
assert.Equal(t, peer.IP.String(), fmt.Sprint(ev.Meta["ip"]))
|
assert.Equal(t, peer.IP.String(), fmt.Sprint(ev.Meta["ip"]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEvent(t *testing.T, accountID string, manager AccountManager, eventType activity.Activity) *activity.Event {
|
func getEvent(t *testing.T, accountID string, manager AccountManager, eventType activity.Activity) *activity.Event {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -953,146 +1072,6 @@ func TestGetUsersFromAccount(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccountManager_UpdatePeerMeta(t *testing.T) {
|
|
||||||
manager, err := createManager(t)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
account, err := createAccount(manager, "test_account", "account_creator", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var setupKey *SetupKey
|
|
||||||
for _, key := range account.SetupKeys {
|
|
||||||
setupKey = key
|
|
||||||
}
|
|
||||||
|
|
||||||
key, err := wgtypes.GeneratePrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
peer, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
|
||||||
Key: key.PublicKey().String(),
|
|
||||||
Meta: PeerSystemMeta{
|
|
||||||
Hostname: "Hostname",
|
|
||||||
GoOS: "GoOS",
|
|
||||||
Kernel: "Kernel",
|
|
||||||
Core: "Core",
|
|
||||||
Platform: "Platform",
|
|
||||||
OS: "OS",
|
|
||||||
WtVersion: "WtVersion",
|
|
||||||
},
|
|
||||||
Name: key.PublicKey().String(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("expecting peer to be added, got failure %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
newMeta := PeerSystemMeta{
|
|
||||||
Hostname: "new-Hostname",
|
|
||||||
GoOS: "new-GoOS",
|
|
||||||
Kernel: "new-Kernel",
|
|
||||||
Core: "new-Core",
|
|
||||||
Platform: "new-Platform",
|
|
||||||
OS: "new-OS",
|
|
||||||
WtVersion: "new-WtVersion",
|
|
||||||
}
|
|
||||||
err = manager.UpdatePeerMeta(peer.ID, newMeta)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := manager.GetPeerByKey(peer.Key)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, newMeta, p.Meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccount_GetPeerRules(t *testing.T) {
|
|
||||||
|
|
||||||
groups := map[string]*Group{
|
|
||||||
"group_1": {
|
|
||||||
ID: "group_1",
|
|
||||||
Name: "group_1",
|
|
||||||
Peers: []string{"peer-1", "peer-2"},
|
|
||||||
},
|
|
||||||
"group_2": {
|
|
||||||
ID: "group_2",
|
|
||||||
Name: "group_2",
|
|
||||||
Peers: []string{"peer-2", "peer-3"},
|
|
||||||
},
|
|
||||||
"group_3": {
|
|
||||||
ID: "group_3",
|
|
||||||
Name: "group_3",
|
|
||||||
Peers: []string{"peer-4"},
|
|
||||||
},
|
|
||||||
"group_4": {
|
|
||||||
ID: "group_4",
|
|
||||||
Name: "group_4",
|
|
||||||
Peers: []string{"peer-1"},
|
|
||||||
},
|
|
||||||
"group_5": {
|
|
||||||
ID: "group_5",
|
|
||||||
Name: "group_5",
|
|
||||||
Peers: []string{"peer-1"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
rules := map[string]*Rule{
|
|
||||||
"rule-1": {
|
|
||||||
ID: "rule-1",
|
|
||||||
Name: "rule-1",
|
|
||||||
Description: "rule-1",
|
|
||||||
Disabled: false,
|
|
||||||
Source: []string{"group_1", "group_5"},
|
|
||||||
Destination: []string{"group_2"},
|
|
||||||
Flow: 0,
|
|
||||||
},
|
|
||||||
"rule-2": {
|
|
||||||
ID: "rule-2",
|
|
||||||
Name: "rule-2",
|
|
||||||
Description: "rule-2",
|
|
||||||
Disabled: false,
|
|
||||||
Source: []string{"group_1"},
|
|
||||||
Destination: []string{"group_1"},
|
|
||||||
Flow: 0,
|
|
||||||
},
|
|
||||||
"rule-3": {
|
|
||||||
ID: "rule-3",
|
|
||||||
Name: "rule-3",
|
|
||||||
Description: "rule-3",
|
|
||||||
Disabled: false,
|
|
||||||
Source: []string{"group_3"},
|
|
||||||
Destination: []string{"group_3"},
|
|
||||||
Flow: 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
account := &Account{
|
|
||||||
Groups: groups,
|
|
||||||
Rules: rules,
|
|
||||||
}
|
|
||||||
|
|
||||||
srcRules, dstRules := account.GetPeerRules("peer-1")
|
|
||||||
|
|
||||||
assert.Equal(t, 2, len(srcRules))
|
|
||||||
assert.Equal(t, 1, len(dstRules))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFileStore_GetRoutesByPrefix(t *testing.T) {
|
func TestFileStore_GetRoutesByPrefix(t *testing.T) {
|
||||||
_, prefix, err := route.ParseNetwork("192.168.64.0/24")
|
_, prefix, err := route.ParseNetwork("192.168.64.0/24")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1254,6 +1233,12 @@ func TestAccount_Copy(t *testing.T) {
|
|||||||
ID: "rule1",
|
ID: "rule1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Policies: []*Policy{
|
||||||
|
{
|
||||||
|
ID: "policy1",
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
Routes: map[string]*route.Route{
|
Routes: map[string]*route.Route{
|
||||||
"route1": {
|
"route1": {
|
||||||
ID: "route1",
|
ID: "route1",
|
||||||
@ -1296,6 +1281,7 @@ func hasNilField(x interface{}) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultAccountManager_DefaultAccountSettings(t *testing.T) {
|
func TestDefaultAccountManager_DefaultAccountSettings(t *testing.T) {
|
||||||
manager, err := createManager(t)
|
manager, err := createManager(t)
|
||||||
require.NoError(t, err, "unable to create account manager")
|
require.NoError(t, err, "unable to create account manager")
|
||||||
@ -1307,6 +1293,7 @@ func TestDefaultAccountManager_DefaultAccountSettings(t *testing.T) {
|
|||||||
assert.Equal(t, account.Settings.PeerLoginExpirationEnabled, true)
|
assert.Equal(t, account.Settings.PeerLoginExpirationEnabled, true)
|
||||||
assert.Equal(t, account.Settings.PeerLoginExpiration, 24*time.Hour)
|
assert.Equal(t, account.Settings.PeerLoginExpiration, 24*time.Hour)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultAccountManager_UpdatePeer_PeerLoginExpiration(t *testing.T) {
|
func TestDefaultAccountManager_UpdatePeer_PeerLoginExpiration(t *testing.T) {
|
||||||
manager, err := createManager(t)
|
manager, err := createManager(t)
|
||||||
require.NoError(t, err, "unable to create account manager")
|
require.NoError(t, err, "unable to create account manager")
|
||||||
@ -1315,10 +1302,9 @@ func TestDefaultAccountManager_UpdatePeer_PeerLoginExpiration(t *testing.T) {
|
|||||||
|
|
||||||
key, err := wgtypes.GenerateKey()
|
key, err := wgtypes.GenerateKey()
|
||||||
require.NoError(t, err, "unable to generate WireGuard key")
|
require.NoError(t, err, "unable to generate WireGuard key")
|
||||||
peer, err := manager.AddPeer("", userID, &Peer{
|
peer, _, err := manager.AddPeer("", userID, &Peer{
|
||||||
Key: key.PublicKey().String(),
|
Key: key.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer"},
|
||||||
Name: "test-peer",
|
|
||||||
LoginExpirationEnabled: true,
|
LoginExpirationEnabled: true,
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "unable to add peer")
|
require.NoError(t, err, "unable to add peer")
|
||||||
@ -1326,7 +1312,8 @@ func TestDefaultAccountManager_UpdatePeer_PeerLoginExpiration(t *testing.T) {
|
|||||||
require.NoError(t, err, "unable to mark peer connected")
|
require.NoError(t, err, "unable to mark peer connected")
|
||||||
account, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
account, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
||||||
PeerLoginExpiration: time.Hour,
|
PeerLoginExpiration: time.Hour,
|
||||||
PeerLoginExpirationEnabled: true})
|
PeerLoginExpirationEnabled: true,
|
||||||
|
})
|
||||||
require.NoError(t, err, "expecting to update account settings successfully but got error")
|
require.NoError(t, err, "expecting to update account settings successfully but got error")
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
@ -1364,16 +1351,16 @@ func TestDefaultAccountManager_MarkPeerConnected_PeerLoginExpiration(t *testing.
|
|||||||
|
|
||||||
key, err := wgtypes.GenerateKey()
|
key, err := wgtypes.GenerateKey()
|
||||||
require.NoError(t, err, "unable to generate WireGuard key")
|
require.NoError(t, err, "unable to generate WireGuard key")
|
||||||
_, err = manager.AddPeer("", userID, &Peer{
|
_, _, err = manager.AddPeer("", userID, &Peer{
|
||||||
Key: key.PublicKey().String(),
|
Key: key.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer"},
|
||||||
Name: "test-peer",
|
|
||||||
LoginExpirationEnabled: true,
|
LoginExpirationEnabled: true,
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "unable to add peer")
|
require.NoError(t, err, "unable to add peer")
|
||||||
_, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
_, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
||||||
PeerLoginExpiration: time.Hour,
|
PeerLoginExpiration: time.Hour,
|
||||||
PeerLoginExpirationEnabled: true})
|
PeerLoginExpirationEnabled: true,
|
||||||
|
})
|
||||||
require.NoError(t, err, "expecting to update account settings successfully but got error")
|
require.NoError(t, err, "expecting to update account settings successfully but got error")
|
||||||
|
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
@ -1395,7 +1382,6 @@ func TestDefaultAccountManager_MarkPeerConnected_PeerLoginExpiration(t *testing.
|
|||||||
if failed {
|
if failed {
|
||||||
t.Fatal("timeout while waiting for test to finish")
|
t.Fatal("timeout while waiting for test to finish")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultAccountManager_UpdateAccountSettings_PeerLoginExpiration(t *testing.T) {
|
func TestDefaultAccountManager_UpdateAccountSettings_PeerLoginExpiration(t *testing.T) {
|
||||||
@ -1406,10 +1392,9 @@ func TestDefaultAccountManager_UpdateAccountSettings_PeerLoginExpiration(t *test
|
|||||||
|
|
||||||
key, err := wgtypes.GenerateKey()
|
key, err := wgtypes.GenerateKey()
|
||||||
require.NoError(t, err, "unable to generate WireGuard key")
|
require.NoError(t, err, "unable to generate WireGuard key")
|
||||||
_, err = manager.AddPeer("", userID, &Peer{
|
_, _, err = manager.AddPeer("", userID, &Peer{
|
||||||
Key: key.PublicKey().String(),
|
Key: key.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer"},
|
||||||
Name: "test-peer",
|
|
||||||
LoginExpirationEnabled: true,
|
LoginExpirationEnabled: true,
|
||||||
})
|
})
|
||||||
require.NoError(t, err, "unable to add peer")
|
require.NoError(t, err, "unable to add peer")
|
||||||
@ -1429,7 +1414,8 @@ func TestDefaultAccountManager_UpdateAccountSettings_PeerLoginExpiration(t *test
|
|||||||
// enabling PeerLoginExpirationEnabled should trigger the expiration job
|
// enabling PeerLoginExpirationEnabled should trigger the expiration job
|
||||||
account, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
account, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
||||||
PeerLoginExpiration: time.Hour,
|
PeerLoginExpiration: time.Hour,
|
||||||
PeerLoginExpirationEnabled: true})
|
PeerLoginExpirationEnabled: true,
|
||||||
|
})
|
||||||
require.NoError(t, err, "expecting to update account settings successfully but got error")
|
require.NoError(t, err, "expecting to update account settings successfully but got error")
|
||||||
|
|
||||||
failed := waitTimeout(wg, time.Second)
|
failed := waitTimeout(wg, time.Second)
|
||||||
@ -1441,7 +1427,8 @@ func TestDefaultAccountManager_UpdateAccountSettings_PeerLoginExpiration(t *test
|
|||||||
// disabling PeerLoginExpirationEnabled should trigger cancel
|
// disabling PeerLoginExpirationEnabled should trigger cancel
|
||||||
_, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
_, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
||||||
PeerLoginExpiration: time.Hour,
|
PeerLoginExpiration: time.Hour,
|
||||||
PeerLoginExpirationEnabled: false})
|
PeerLoginExpirationEnabled: false,
|
||||||
|
})
|
||||||
require.NoError(t, err, "expecting to update account settings successfully but got error")
|
require.NoError(t, err, "expecting to update account settings successfully but got error")
|
||||||
failed = waitTimeout(wg, time.Second)
|
failed = waitTimeout(wg, time.Second)
|
||||||
if failed {
|
if failed {
|
||||||
@ -1458,7 +1445,8 @@ func TestDefaultAccountManager_UpdateAccountSettings(t *testing.T) {
|
|||||||
|
|
||||||
updated, err := manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
updated, err := manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
||||||
PeerLoginExpiration: time.Hour,
|
PeerLoginExpiration: time.Hour,
|
||||||
PeerLoginExpirationEnabled: false})
|
PeerLoginExpirationEnabled: false,
|
||||||
|
})
|
||||||
require.NoError(t, err, "expecting to update account settings successfully but got error")
|
require.NoError(t, err, "expecting to update account settings successfully but got error")
|
||||||
assert.False(t, updated.Settings.PeerLoginExpirationEnabled)
|
assert.False(t, updated.Settings.PeerLoginExpirationEnabled)
|
||||||
assert.Equal(t, updated.Settings.PeerLoginExpiration, time.Hour)
|
assert.Equal(t, updated.Settings.PeerLoginExpiration, time.Hour)
|
||||||
@ -1471,12 +1459,14 @@ func TestDefaultAccountManager_UpdateAccountSettings(t *testing.T) {
|
|||||||
|
|
||||||
_, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
_, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
||||||
PeerLoginExpiration: time.Second,
|
PeerLoginExpiration: time.Second,
|
||||||
PeerLoginExpirationEnabled: false})
|
PeerLoginExpirationEnabled: false,
|
||||||
|
})
|
||||||
require.Error(t, err, "expecting to fail when providing PeerLoginExpiration less than one hour")
|
require.Error(t, err, "expecting to fail when providing PeerLoginExpiration less than one hour")
|
||||||
|
|
||||||
_, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
_, err = manager.UpdateAccountSettings(account.Id, userID, &Settings{
|
||||||
PeerLoginExpiration: time.Hour * 24 * 181,
|
PeerLoginExpiration: time.Hour * 24 * 181,
|
||||||
PeerLoginExpirationEnabled: false})
|
PeerLoginExpirationEnabled: false,
|
||||||
|
})
|
||||||
require.Error(t, err, "expecting to fail when providing PeerLoginExpiration more than 180 days")
|
require.Error(t, err, "expecting to fail when providing PeerLoginExpiration more than 180 days")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1511,6 +1501,7 @@ func TestAccount_GetExpiredPeers(t *testing.T) {
|
|||||||
LoginExpired: false,
|
LoginExpired: false,
|
||||||
},
|
},
|
||||||
LastLogin: time.Now().Add(-30 * time.Minute),
|
LastLogin: time.Now().Add(-30 * time.Minute),
|
||||||
|
UserID: userID,
|
||||||
},
|
},
|
||||||
"peer-2": {
|
"peer-2": {
|
||||||
ID: "peer-2",
|
ID: "peer-2",
|
||||||
@ -1521,6 +1512,7 @@ func TestAccount_GetExpiredPeers(t *testing.T) {
|
|||||||
LoginExpired: false,
|
LoginExpired: false,
|
||||||
},
|
},
|
||||||
LastLogin: time.Now().Add(-2 * time.Hour),
|
LastLogin: time.Now().Add(-2 * time.Hour),
|
||||||
|
UserID: userID,
|
||||||
},
|
},
|
||||||
|
|
||||||
"peer-3": {
|
"peer-3": {
|
||||||
@ -1532,6 +1524,7 @@ func TestAccount_GetExpiredPeers(t *testing.T) {
|
|||||||
LoginExpired: false,
|
LoginExpired: false,
|
||||||
},
|
},
|
||||||
LastLogin: time.Now().Add(-1 * time.Hour),
|
LastLogin: time.Now().Add(-1 * time.Hour),
|
||||||
|
UserID: userID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedPeers: map[string]struct{}{
|
expectedPeers: map[string]struct{}{
|
||||||
@ -1560,7 +1553,6 @@ func TestAccount_GetExpiredPeers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccount_GetPeersWithExpiration(t *testing.T) {
|
func TestAccount_GetPeersWithExpiration(t *testing.T) {
|
||||||
@ -1626,11 +1618,9 @@ func TestAccount_GetPeersWithExpiration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccount_GetNextPeerExpiration(t *testing.T) {
|
func TestAccount_GetNextPeerExpiration(t *testing.T) {
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
name string
|
name string
|
||||||
peers map[string]*Peer
|
peers map[string]*Peer
|
||||||
@ -1754,10 +1744,8 @@ func TestAccount_GetNextPeerExpiration(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
assert.Equal(t, expiration, testCase.expectedNextExpiration)
|
assert.Equal(t, expiration, testCase.expectedNextExpiration)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createManager(t *testing.T) (*DefaultAccountManager, error) {
|
func createManager(t *testing.T) (*DefaultAccountManager, error) {
|
||||||
|
@ -19,6 +19,12 @@ const (
|
|||||||
RuleUpdated
|
RuleUpdated
|
||||||
// RuleRemoved indicates that a user removed a rule
|
// RuleRemoved indicates that a user removed a rule
|
||||||
RuleRemoved
|
RuleRemoved
|
||||||
|
// PolicyAdded indicates that a user added a new policy
|
||||||
|
PolicyAdded
|
||||||
|
// PolicyUpdated indicates that a user updated a policy
|
||||||
|
PolicyUpdated
|
||||||
|
// PolicyRemoved indicates that a user removed a policy
|
||||||
|
PolicyRemoved
|
||||||
// SetupKeyCreated indicates that a user created a new setup key
|
// SetupKeyCreated indicates that a user created a new setup key
|
||||||
SetupKeyCreated
|
SetupKeyCreated
|
||||||
// SetupKeyUpdated indicates that a user updated a setup key
|
// SetupKeyUpdated indicates that a user updated a setup key
|
||||||
@ -84,11 +90,11 @@ const (
|
|||||||
PeerAddedByUserMessage string = "Peer added"
|
PeerAddedByUserMessage string = "Peer added"
|
||||||
// PeerAddedWithSetupKeyMessage is a human-readable text message of the PeerAddedWithSetupKey activity
|
// PeerAddedWithSetupKeyMessage is a human-readable text message of the PeerAddedWithSetupKey activity
|
||||||
PeerAddedWithSetupKeyMessage = PeerAddedByUserMessage
|
PeerAddedWithSetupKeyMessage = PeerAddedByUserMessage
|
||||||
//UserJoinedMessage is a human-readable text message of the UserJoined activity
|
// UserJoinedMessage is a human-readable text message of the UserJoined activity
|
||||||
UserJoinedMessage string = "User joined"
|
UserJoinedMessage string = "User joined"
|
||||||
//UserInvitedMessage is a human-readable text message of the UserInvited activity
|
// UserInvitedMessage is a human-readable text message of the UserInvited activity
|
||||||
UserInvitedMessage string = "User invited"
|
UserInvitedMessage string = "User invited"
|
||||||
//AccountCreatedMessage is a human-readable text message of the AccountCreated activity
|
// AccountCreatedMessage is a human-readable text message of the AccountCreated activity
|
||||||
AccountCreatedMessage string = "Account created"
|
AccountCreatedMessage string = "Account created"
|
||||||
// PeerRemovedByUserMessage is a human-readable text message of the PeerRemovedByUser activity
|
// PeerRemovedByUserMessage is a human-readable text message of the PeerRemovedByUser activity
|
||||||
PeerRemovedByUserMessage string = "Peer deleted"
|
PeerRemovedByUserMessage string = "Peer deleted"
|
||||||
@ -98,6 +104,12 @@ const (
|
|||||||
RuleRemovedMessage string = "Rule deleted"
|
RuleRemovedMessage string = "Rule deleted"
|
||||||
// RuleUpdatedMessage is a human-readable text message of the RuleRemoved activity
|
// RuleUpdatedMessage is a human-readable text message of the RuleRemoved activity
|
||||||
RuleUpdatedMessage string = "Rule updated"
|
RuleUpdatedMessage string = "Rule updated"
|
||||||
|
// PolicyAddedMessage is a human-readable text message of the PolicyAdded activity
|
||||||
|
PolicyAddedMessage string = "Policy added"
|
||||||
|
// PolicyRemovedMessage is a human-readable text message of the PolicyRemoved activity
|
||||||
|
PolicyRemovedMessage string = "Policy deleted"
|
||||||
|
// PolicyUpdatedMessage is a human-readable text message of the PolicyRemoved activity
|
||||||
|
PolicyUpdatedMessage string = "Policy updated"
|
||||||
// SetupKeyCreatedMessage is a human-readable text message of the SetupKeyCreated activity
|
// SetupKeyCreatedMessage is a human-readable text message of the SetupKeyCreated activity
|
||||||
SetupKeyCreatedMessage string = "Setup key created"
|
SetupKeyCreatedMessage string = "Setup key created"
|
||||||
// SetupKeyUpdatedMessage is a human-readable text message of the SetupKeyUpdated activity
|
// SetupKeyUpdatedMessage is a human-readable text message of the SetupKeyUpdated activity
|
||||||
@ -182,6 +194,12 @@ func (a Activity) Message() string {
|
|||||||
return RuleRemovedMessage
|
return RuleRemovedMessage
|
||||||
case RuleUpdated:
|
case RuleUpdated:
|
||||||
return RuleUpdatedMessage
|
return RuleUpdatedMessage
|
||||||
|
case PolicyAdded:
|
||||||
|
return PolicyAddedMessage
|
||||||
|
case PolicyRemoved:
|
||||||
|
return PolicyRemovedMessage
|
||||||
|
case PolicyUpdated:
|
||||||
|
return PolicyUpdatedMessage
|
||||||
case SetupKeyCreated:
|
case SetupKeyCreated:
|
||||||
return SetupKeyCreatedMessage
|
return SetupKeyCreatedMessage
|
||||||
case SetupKeyUpdated:
|
case SetupKeyUpdated:
|
||||||
@ -266,6 +284,12 @@ func (a Activity) StringCode() string {
|
|||||||
return "rule.delete"
|
return "rule.delete"
|
||||||
case RuleUpdated:
|
case RuleUpdated:
|
||||||
return "rule.update"
|
return "rule.update"
|
||||||
|
case PolicyAdded:
|
||||||
|
return "policy.add"
|
||||||
|
case PolicyRemoved:
|
||||||
|
return "policy.delete"
|
||||||
|
case PolicyUpdated:
|
||||||
|
return "policy.update"
|
||||||
case SetupKeyCreated:
|
case SetupKeyCreated:
|
||||||
return "setupkey.add"
|
return "setupkey.add"
|
||||||
case SetupKeyRevoked:
|
case SetupKeyRevoked:
|
||||||
|
@ -244,11 +244,11 @@ func initTestDNSAccount(t *testing.T, am *DefaultAccountManager) (*Account, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = am.AddPeer("", dnsAdminUserID, peer1)
|
_, _, err = am.AddPeer("", dnsAdminUserID, peer1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = am.AddPeer("", dnsAdminUserID, peer2)
|
_, _, err = am.AddPeer("", dnsAdminUserID, peer2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
|
||||||
"github.com/rs/xid"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/rs/xid"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/util"
|
"github.com/netbirdio/netbird/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -81,7 +82,6 @@ func restore(file string) (*FileStore, error) {
|
|||||||
store.PeerID2AccountID = make(map[string]string)
|
store.PeerID2AccountID = make(map[string]string)
|
||||||
|
|
||||||
for accountID, account := range store.Accounts {
|
for accountID, account := range store.Accounts {
|
||||||
|
|
||||||
if account.Settings == nil {
|
if account.Settings == nil {
|
||||||
account.Settings = &Settings{
|
account.Settings = &Settings{
|
||||||
PeerLoginExpirationEnabled: false,
|
PeerLoginExpirationEnabled: false,
|
||||||
@ -113,6 +113,19 @@ func restore(file string) (*FileStore, error) {
|
|||||||
store.PrivateDomain2AccountID[account.Domain] = accountID
|
store.PrivateDomain2AccountID[account.Domain] = accountID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if no policies are defined, that means we need to migrate Rules to policies
|
||||||
|
if len(account.Policies) == 0 {
|
||||||
|
account.Policies = make([]*Policy, 0)
|
||||||
|
for _, rule := range account.Rules {
|
||||||
|
policy, err := RuleToPolicy(rule)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("unable to migrate rule to policy: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
account.Policies = append(account.Policies, policy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// for data migration. Can be removed once most base will be with labels
|
// for data migration. Can be removed once most base will be with labels
|
||||||
existingLabels := account.getPeerDNSLabels()
|
existingLabels := account.getPeerDNSLabels()
|
||||||
if len(existingLabels) != len(account.Peers) {
|
if len(existingLabels) != len(account.Peers) {
|
||||||
@ -140,6 +153,10 @@ func restore(file string) (*FileStore, error) {
|
|||||||
// Swap Peer.Key with Peer.ID in the Account.Peers map.
|
// Swap Peer.Key with Peer.ID in the Account.Peers map.
|
||||||
migrationPeers := make(map[string]*Peer) // key to Peer
|
migrationPeers := make(map[string]*Peer) // key to Peer
|
||||||
for key, peer := range account.Peers {
|
for key, peer := range account.Peers {
|
||||||
|
// set LastLogin for the peers that were onboarded before the peer login expiration feature
|
||||||
|
if peer.LastLogin.IsZero() {
|
||||||
|
peer.LastLogin = time.Now()
|
||||||
|
}
|
||||||
if peer.ID != "" {
|
if peer.ID != "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -149,7 +166,6 @@ func restore(file string) (*FileStore, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(migrationPeers) > 0 {
|
if len(migrationPeers) > 0 {
|
||||||
|
|
||||||
// swap Peer.Key with Peer.ID in the Account.Peers map.
|
// swap Peer.Key with Peer.ID in the Account.Peers map.
|
||||||
for key, peer := range migrationPeers {
|
for key, peer := range migrationPeers {
|
||||||
delete(account.Peers, key)
|
delete(account.Peers, key)
|
||||||
@ -165,6 +181,7 @@ func restore(file string) (*FileStore, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect routes that have Peer.Key as a reference and replace it with ID.
|
// detect routes that have Peer.Key as a reference and replace it with ID.
|
||||||
for _, route := range account.Routes {
|
for _, route := range account.Routes {
|
||||||
if peer, ok := migrationPeers[route.Peer]; ok {
|
if peer, ok := migrationPeers[route.Peer]; ok {
|
||||||
@ -247,6 +264,15 @@ func (s *FileStore) SaveAccount(account *Account) error {
|
|||||||
s.PrivateDomain2AccountID[accountCopy.Domain] = accountCopy.Id
|
s.PrivateDomain2AccountID[accountCopy.Domain] = accountCopy.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if accountCopy.Rules == nil {
|
||||||
|
accountCopy.Rules = make(map[string]*Rule)
|
||||||
|
}
|
||||||
|
for _, policy := range accountCopy.Policies {
|
||||||
|
for _, rule := range policy.Rules {
|
||||||
|
accountCopy.Rules[rule.ID] = rule.ToRule()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return s.persist(s.storeFile)
|
return s.persist(s.storeFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/netbirdio/netbird/util"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/util"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type accounts struct {
|
type accounts struct {
|
||||||
@ -70,7 +71,6 @@ func TestNewStore(t *testing.T) {
|
|||||||
if store.UserID2AccountID == nil || len(store.UserID2AccountID) != 0 {
|
if store.UserID2AccountID == nil || len(store.UserID2AccountID) != 0 {
|
||||||
t.Errorf("expected to create a new empty UserID2AccountID map when creating a new FileStore")
|
t.Errorf("expected to create a new empty UserID2AccountID map when creating a new FileStore")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSaveAccount(t *testing.T) {
|
func TestSaveAccount(t *testing.T) {
|
||||||
@ -109,7 +109,6 @@ func TestSaveAccount(t *testing.T) {
|
|||||||
if store.SetupKeyID2AccountID[setupKey.Key] == "" {
|
if store.SetupKeyID2AccountID[setupKey.Key] == "" {
|
||||||
t.Errorf("expecting SetupKeyID2AccountID index updated after SaveAccount()")
|
t.Errorf("expecting SetupKeyID2AccountID index updated after SaveAccount()")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStore(t *testing.T) {
|
func TestStore(t *testing.T) {
|
||||||
@ -124,6 +123,38 @@ func TestStore(t *testing.T) {
|
|||||||
Name: "peer name",
|
Name: "peer name",
|
||||||
Status: &PeerStatus{Connected: true, LastSeen: time.Now()},
|
Status: &PeerStatus{Connected: true, LastSeen: time.Now()},
|
||||||
}
|
}
|
||||||
|
account.Groups["all"] = &Group{
|
||||||
|
ID: "all",
|
||||||
|
Name: "all",
|
||||||
|
Peers: []string{"testpeer"},
|
||||||
|
}
|
||||||
|
account.Rules["all"] = &Rule{
|
||||||
|
ID: "all",
|
||||||
|
Name: "all",
|
||||||
|
Source: []string{"all"},
|
||||||
|
Destination: []string{"all"},
|
||||||
|
Flow: TrafficFlowBidirect,
|
||||||
|
}
|
||||||
|
account.Policies = append(account.Policies, &Policy{
|
||||||
|
ID: "all",
|
||||||
|
Name: "all",
|
||||||
|
Enabled: true,
|
||||||
|
Rules: []*PolicyRule{account.Rules["all"].ToPolicyRule()},
|
||||||
|
})
|
||||||
|
account.Policies = append(account.Policies, &Policy{
|
||||||
|
ID: "dmz",
|
||||||
|
Name: "dmz",
|
||||||
|
Enabled: true,
|
||||||
|
Rules: []*PolicyRule{
|
||||||
|
{
|
||||||
|
ID: "dmz",
|
||||||
|
Name: "dmz",
|
||||||
|
Enabled: true,
|
||||||
|
Sources: []string{"all"},
|
||||||
|
Destinations: []string{"all"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// SaveAccount should trigger persist
|
// SaveAccount should trigger persist
|
||||||
err := store.SaveAccount(account)
|
err := store.SaveAccount(account)
|
||||||
@ -139,24 +170,48 @@ func TestStore(t *testing.T) {
|
|||||||
restoredAccount := restored.Accounts[account.Id]
|
restoredAccount := restored.Accounts[account.Id]
|
||||||
if restoredAccount == nil {
|
if restoredAccount == nil {
|
||||||
t.Errorf("failed to restore a FileStore file - missing Account %s", account.Id)
|
t.Errorf("failed to restore a FileStore file - missing Account %s", account.Id)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if restoredAccount != nil && restoredAccount.Peers["testpeer"] == nil {
|
if restoredAccount.Peers["testpeer"] == nil {
|
||||||
t.Errorf("failed to restore a FileStore file - missing Peer testpeer")
|
t.Errorf("failed to restore a FileStore file - missing Peer testpeer")
|
||||||
}
|
}
|
||||||
|
|
||||||
if restoredAccount != nil && restoredAccount.CreatedBy != "testuser" {
|
if restoredAccount.CreatedBy != "testuser" {
|
||||||
t.Errorf("failed to restore a FileStore file - missing Account CreatedBy")
|
t.Errorf("failed to restore a FileStore file - missing Account CreatedBy")
|
||||||
}
|
}
|
||||||
|
|
||||||
if restoredAccount != nil && restoredAccount.Users["testuser"] == nil {
|
if restoredAccount.Users["testuser"] == nil {
|
||||||
t.Errorf("failed to restore a FileStore file - missing User testuser")
|
t.Errorf("failed to restore a FileStore file - missing User testuser")
|
||||||
}
|
}
|
||||||
|
|
||||||
if restoredAccount != nil && restoredAccount.Network == nil {
|
if restoredAccount.Network == nil {
|
||||||
t.Errorf("failed to restore a FileStore file - missing Network")
|
t.Errorf("failed to restore a FileStore file - missing Network")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if restoredAccount.Groups["all"] == nil {
|
||||||
|
t.Errorf("failed to restore a FileStore file - missing Group all")
|
||||||
|
}
|
||||||
|
|
||||||
|
if restoredAccount.Rules["all"] == nil {
|
||||||
|
t.Errorf("failed to restore a FileStore file - missing Rule all")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if restoredAccount.Rules["dmz"] == nil {
|
||||||
|
t.Errorf("failed to restore a FileStore file - missing Rule dmz")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.Equal(t, account.Rules["all"], restoredAccount.Rules["all"], "failed to restore a FileStore file - missing Rule all")
|
||||||
|
assert.Equal(t, account.Rules["dmz"], restoredAccount.Rules["dmz"], "failed to restore a FileStore file - missing Rule dmz")
|
||||||
|
|
||||||
|
if len(restoredAccount.Policies) != 2 {
|
||||||
|
t.Errorf("failed to restore a FileStore file - missing Policies")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, account.Policies[0], restoredAccount.Policies[0], "failed to restore a FileStore file - missing Policy all")
|
||||||
|
assert.Equal(t, account.Policies[1], restoredAccount.Policies[1], "failed to restore a FileStore file - missing Policy dmz")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRestore(t *testing.T) {
|
func TestRestore(t *testing.T) {
|
||||||
@ -191,6 +246,43 @@ func TestRestore(t *testing.T) {
|
|||||||
require.Len(t, store.PrivateDomain2AccountID, 1, "failed to restore a FileStore wrong PrivateDomain2AccountID mapping length")
|
require.Len(t, store.PrivateDomain2AccountID, 1, "failed to restore a FileStore wrong PrivateDomain2AccountID mapping length")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRestorePolicies_Migration(t *testing.T) {
|
||||||
|
storeDir := t.TempDir()
|
||||||
|
|
||||||
|
err := util.CopyFileContents("testdata/store_policy_migrate.json", filepath.Join(storeDir, "store.json"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
store, err := NewFileStore(storeDir)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
account := store.Accounts["bf1c8084-ba50-4ce7-9439-34653001fc3b"]
|
||||||
|
require.Len(t, account.Groups, 1, "failed to restore a FileStore file - missing Account Groups")
|
||||||
|
require.Len(t, account.Rules, 1, "failed to restore a FileStore file - missing Account Rules")
|
||||||
|
require.Len(t, account.Policies, 1, "failed to restore a FileStore file - missing Account Policies")
|
||||||
|
|
||||||
|
policy := account.Policies[0]
|
||||||
|
require.Equal(t, policy.Name, "Default", "failed to restore a FileStore file - missing Account Policies Name")
|
||||||
|
require.Equal(t, policy.Description,
|
||||||
|
"This is a default rule that allows connections between all the resources",
|
||||||
|
"failed to restore a FileStore file - missing Account Policies Description")
|
||||||
|
expectedPolicy := policy.Copy()
|
||||||
|
err = expectedPolicy.UpdateQueryFromRules()
|
||||||
|
require.NoError(t, err, "failed to upldate query")
|
||||||
|
require.Equal(t, policy.Query, expectedPolicy.Query, "failed to restore a FileStore file - missing Account Policies Query")
|
||||||
|
require.Len(t, policy.Rules, 1, "failed to restore a FileStore file - missing Account Policy Rules")
|
||||||
|
require.Equal(t, policy.Rules[0].Action, PolicyTrafficActionAccept, "failed to restore a FileStore file - missing Account Policies Action")
|
||||||
|
require.Equal(t, policy.Rules[0].Destinations,
|
||||||
|
[]string{"cfefqs706sqkneg59g3g"},
|
||||||
|
"failed to restore a FileStore file - missing Account Policies Destinations")
|
||||||
|
require.Equal(t, policy.Rules[0].Sources,
|
||||||
|
[]string{"cfefqs706sqkneg59g3g"},
|
||||||
|
"failed to restore a FileStore file - missing Account Policies Sources")
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetAccountByPrivateDomain(t *testing.T) {
|
func TestGetAccountByPrivateDomain(t *testing.T) {
|
||||||
storeDir := t.TempDir()
|
storeDir := t.TempDir()
|
||||||
|
|
||||||
|
@ -53,7 +53,6 @@ func (g *Group) Copy() *Group {
|
|||||||
|
|
||||||
// GetGroup object of the peers
|
// GetGroup object of the peers
|
||||||
func (am *DefaultAccountManager) GetGroup(accountID, groupID string) (*Group, error) {
|
func (am *DefaultAccountManager) GetGroup(accountID, groupID string) (*Group, error) {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -72,7 +71,6 @@ func (am *DefaultAccountManager) GetGroup(accountID, groupID string) (*Group, er
|
|||||||
|
|
||||||
// SaveGroup object of the peers
|
// SaveGroup object of the peers
|
||||||
func (am *DefaultAccountManager) SaveGroup(accountID, userID string, newGroup *Group) error {
|
func (am *DefaultAccountManager) SaveGroup(accountID, userID string, newGroup *Group) error {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -112,8 +110,10 @@ func (am *DefaultAccountManager) SaveGroup(accountID, userID string, newGroup *G
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
am.storeEvent(userID, peer.ID, accountID, activity.GroupAddedToPeer,
|
am.storeEvent(userID, peer.ID, accountID, activity.GroupAddedToPeer,
|
||||||
map[string]any{"group": newGroup.Name, "group_id": newGroup.ID, "peer_ip": peer.IP.String(),
|
map[string]any{
|
||||||
"peer_fqdn": peer.FQDN(am.GetDNSDomain())})
|
"group": newGroup.Name, "group_id": newGroup.ID, "peer_ip": peer.IP.String(),
|
||||||
|
"peer_fqdn": peer.FQDN(am.GetDNSDomain()),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range removedPeers {
|
for _, p := range removedPeers {
|
||||||
@ -123,8 +123,10 @@ func (am *DefaultAccountManager) SaveGroup(accountID, userID string, newGroup *G
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
am.storeEvent(userID, peer.ID, accountID, activity.GroupRemovedFromPeer,
|
am.storeEvent(userID, peer.ID, accountID, activity.GroupRemovedFromPeer,
|
||||||
map[string]any{"group": newGroup.Name, "group_id": newGroup.ID, "peer_ip": peer.IP.String(),
|
map[string]any{
|
||||||
"peer_fqdn": peer.FQDN(am.GetDNSDomain())})
|
"group": newGroup.Name, "group_id": newGroup.ID, "peer_ip": peer.IP.String(),
|
||||||
|
"peer_fqdn": peer.FQDN(am.GetDNSDomain()),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -147,8 +149,8 @@ func difference(a, b []string) []string {
|
|||||||
|
|
||||||
// UpdateGroup updates a group using a list of operations
|
// UpdateGroup updates a group using a list of operations
|
||||||
func (am *DefaultAccountManager) UpdateGroup(accountID string,
|
func (am *DefaultAccountManager) UpdateGroup(accountID string,
|
||||||
groupID string, operations []GroupUpdateOperation) (*Group, error) {
|
groupID string, operations []GroupUpdateOperation,
|
||||||
|
) (*Group, error) {
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -198,7 +200,6 @@ func (am *DefaultAccountManager) UpdateGroup(accountID string,
|
|||||||
|
|
||||||
// DeleteGroup object of the peers
|
// DeleteGroup object of the peers
|
||||||
func (am *DefaultAccountManager) DeleteGroup(accountID, groupID string) error {
|
func (am *DefaultAccountManager) DeleteGroup(accountID, groupID string) error {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -219,7 +220,6 @@ func (am *DefaultAccountManager) DeleteGroup(accountID, groupID string) error {
|
|||||||
|
|
||||||
// ListGroups objects of the peers
|
// ListGroups objects of the peers
|
||||||
func (am *DefaultAccountManager) ListGroups(accountID string) ([]*Group, error) {
|
func (am *DefaultAccountManager) ListGroups(accountID string) ([]*Group, error) {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -238,7 +238,6 @@ func (am *DefaultAccountManager) ListGroups(accountID string) ([]*Group, error)
|
|||||||
|
|
||||||
// GroupAddPeer appends peer to the group
|
// GroupAddPeer appends peer to the group
|
||||||
func (am *DefaultAccountManager) GroupAddPeer(accountID, groupID, peerID string) error {
|
func (am *DefaultAccountManager) GroupAddPeer(accountID, groupID, peerID string) error {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -273,7 +272,6 @@ func (am *DefaultAccountManager) GroupAddPeer(accountID, groupID, peerID string)
|
|||||||
|
|
||||||
// GroupDeletePeer removes peer from the group
|
// GroupDeletePeer removes peer from the group
|
||||||
func (am *DefaultAccountManager) GroupDeletePeer(accountID, groupID, peerKey string) error {
|
func (am *DefaultAccountManager) GroupDeletePeer(accountID, groupID, peerKey string) error {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -302,7 +300,6 @@ func (am *DefaultAccountManager) GroupDeletePeer(accountID, groupID, peerKey str
|
|||||||
|
|
||||||
// GroupListPeers returns list of the peers from the group
|
// GroupListPeers returns list of the peers from the group
|
||||||
func (am *DefaultAccountManager) GroupListPeers(accountID, groupID string) ([]*Peer, error) {
|
func (am *DefaultAccountManager) GroupListPeers(accountID, groupID string) ([]*Peer, error) {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
pb "github.com/golang/protobuf/proto" //nolint
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -118,44 +119,18 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
|
|||||||
log.Debugf("Sync request from peer [%s] [%s]", req.WgPubKey, p.Addr.String())
|
log.Debugf("Sync request from peer [%s] [%s]", req.WgPubKey, p.Addr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
peerKey, err := wgtypes.ParseKey(req.GetWgPubKey())
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("error while parsing peer's Wireguard public key %s on Sync request.", peerKey.String())
|
|
||||||
return status.Errorf(codes.InvalidArgument, "provided wgPubKey %s is invalid", peerKey.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
peer, err := s.accountManager.GetPeerByKey(peerKey.String())
|
|
||||||
if err != nil {
|
|
||||||
p, _ := gRPCPeer.FromContext(srv.Context())
|
|
||||||
msg := status.Errorf(codes.PermissionDenied, "provided peer with the key wgPubKey %s is not registered, remote addr is %s", peerKey.String(), p.Addr.String())
|
|
||||||
log.Debug(msg)
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
|
|
||||||
account, err := s.accountManager.GetAccountByPeerID(peer.ID)
|
|
||||||
if err != nil {
|
|
||||||
return status.Error(codes.Internal, "internal server error")
|
|
||||||
}
|
|
||||||
expired, left := peer.LoginExpired(account.Settings.PeerLoginExpiration)
|
|
||||||
expired = account.Settings.PeerLoginExpirationEnabled && expired
|
|
||||||
if peer.UserID != "" && (expired || peer.Status.LoginExpired) {
|
|
||||||
err = s.accountManager.MarkPeerLoginExpired(peerKey.String(), true)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed marking peer login expired %s %v", peerKey, err)
|
|
||||||
}
|
|
||||||
return status.Errorf(codes.PermissionDenied, "peer login has expired %v ago. Please log in once more", left)
|
|
||||||
}
|
|
||||||
|
|
||||||
syncReq := &proto.SyncRequest{}
|
syncReq := &proto.SyncRequest{}
|
||||||
err = encryption.DecryptMessage(peerKey, s.wgKey, req.Body, syncReq)
|
peerKey, err := s.parseRequest(req, syncReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p, _ := gRPCPeer.FromContext(srv.Context())
|
return err
|
||||||
msg := status.Errorf(codes.InvalidArgument, "invalid request message from %s,remote addr is %s", peerKey.String(), p.Addr.String())
|
|
||||||
log.Debug(msg)
|
|
||||||
return msg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.sendInitialSync(peerKey, peer, srv)
|
peer, netMap, err := s.accountManager.SyncPeer(PeerSync{WireGuardPubKey: peerKey.String()})
|
||||||
|
if err != nil {
|
||||||
|
return mapError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.sendInitialSync(peerKey, peer, netMap, srv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("error while sending initial sync for %s: %v", peerKey.String(), err)
|
log.Debugf("error while sending initial sync for %s: %v", peerKey.String(), err)
|
||||||
return err
|
return err
|
||||||
@ -218,7 +193,7 @@ func (s *GRPCServer) validateToken(jwtToken string) (string, error) {
|
|||||||
|
|
||||||
token, err := s.jwtMiddleware.ValidateAndParse(jwtToken)
|
token, err := s.jwtMiddleware.ValidateAndParse(jwtToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", status.Errorf(codes.Internal, "invalid jwt token, err: %v", err)
|
return "", status.Errorf(codes.InvalidArgument, "invalid jwt token, err: %v", err)
|
||||||
}
|
}
|
||||||
claims := s.jwtClaimsExtractor.FromToken(token)
|
claims := s.jwtClaimsExtractor.FromToken(token)
|
||||||
// we need to call this method because if user is new, we will automatically add it to existing or create a new account
|
// we need to call this method because if user is new, we will automatically add it to existing or create a new account
|
||||||
@ -230,84 +205,52 @@ func (s *GRPCServer) validateToken(jwtToken string) (string, error) {
|
|||||||
return claims.UserId, nil
|
return claims.UserId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *GRPCServer) registerPeer(peerKey wgtypes.Key, req *proto.LoginRequest) (*Peer, error) {
|
// maps internal internalStatus.Error to gRPC status.Error
|
||||||
var (
|
func mapError(err error) error {
|
||||||
reqSetupKey string
|
if e, ok := internalStatus.FromError(err); ok {
|
||||||
userID string
|
switch e.Type() {
|
||||||
err error
|
case internalStatus.PermissionDenied:
|
||||||
)
|
return status.Errorf(codes.PermissionDenied, e.Message)
|
||||||
|
case internalStatus.Unauthorized:
|
||||||
if req.GetJwtToken() != "" {
|
return status.Errorf(codes.PermissionDenied, e.Message)
|
||||||
log.Debugln("using jwt token to register peer")
|
case internalStatus.Unauthenticated:
|
||||||
userID, err = s.validateToken(req.JwtToken)
|
return status.Errorf(codes.PermissionDenied, e.Message)
|
||||||
if err != nil {
|
case internalStatus.PreconditionFailed:
|
||||||
return nil, err
|
return status.Errorf(codes.FailedPrecondition, e.Message)
|
||||||
|
case internalStatus.NotFound:
|
||||||
|
return status.Errorf(codes.NotFound, e.Message)
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log.Debugln("using setup key to register peer")
|
|
||||||
reqSetupKey = req.GetSetupKey()
|
|
||||||
userID = ""
|
|
||||||
}
|
}
|
||||||
|
return status.Errorf(codes.Internal, "failed handling request")
|
||||||
|
}
|
||||||
|
|
||||||
meta := req.GetMeta()
|
func extractPeerMeta(loginReq *proto.LoginRequest) PeerSystemMeta {
|
||||||
if meta == nil {
|
return PeerSystemMeta{
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "peer meta data was not provided")
|
Hostname: loginReq.GetMeta().GetHostname(),
|
||||||
|
GoOS: loginReq.GetMeta().GetGoOS(),
|
||||||
|
Kernel: loginReq.GetMeta().GetKernel(),
|
||||||
|
Core: loginReq.GetMeta().GetCore(),
|
||||||
|
Platform: loginReq.GetMeta().GetPlatform(),
|
||||||
|
OS: loginReq.GetMeta().GetOS(),
|
||||||
|
WtVersion: loginReq.GetMeta().GetWiretrusteeVersion(),
|
||||||
|
UIVersion: loginReq.GetMeta().GetUiVersion(),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var sshKey []byte
|
func (s *GRPCServer) parseRequest(req *proto.EncryptedMessage, parsed pb.Message) (wgtypes.Key, error) {
|
||||||
if req.GetPeerKeys() != nil {
|
peerKey, err := wgtypes.ParseKey(req.GetWgPubKey())
|
||||||
sshKey = req.GetPeerKeys().GetSshPubKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
peer, err := s.accountManager.AddPeer(reqSetupKey, userID, &Peer{
|
|
||||||
Key: peerKey.String(),
|
|
||||||
Name: meta.GetHostname(),
|
|
||||||
SSHKey: string(sshKey),
|
|
||||||
Meta: PeerSystemMeta{
|
|
||||||
Hostname: meta.GetHostname(),
|
|
||||||
GoOS: meta.GetGoOS(),
|
|
||||||
Kernel: meta.GetKernel(),
|
|
||||||
Core: meta.GetCore(),
|
|
||||||
Platform: meta.GetPlatform(),
|
|
||||||
OS: meta.GetOS(),
|
|
||||||
WtVersion: meta.GetWiretrusteeVersion(),
|
|
||||||
UIVersion: meta.GetUiVersion(),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if e, ok := internalStatus.FromError(err); ok {
|
log.Warnf("error while parsing peer's WireGuard public key %s.", req.WgPubKey)
|
||||||
switch e.Type() {
|
return wgtypes.Key{}, status.Errorf(codes.InvalidArgument, "provided wgPubKey %s is invalid", req.WgPubKey)
|
||||||
case internalStatus.PreconditionFailed:
|
|
||||||
return nil, status.Errorf(codes.FailedPrecondition, e.Message)
|
|
||||||
case internalStatus.NotFound:
|
|
||||||
return nil, status.Errorf(codes.NotFound, e.Message)
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, status.Errorf(codes.Internal, "failed registering new peer")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo move to DefaultAccountManager the code below
|
err = encryption.DecryptMessage(peerKey, s.wgKey, req.Body, parsed)
|
||||||
networkMap, err := s.accountManager.GetNetworkMap(peer.ID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "unable to fetch network map after registering peer, error: %v", err)
|
return wgtypes.Key{}, status.Errorf(codes.InvalidArgument, "invalid request message")
|
||||||
}
|
|
||||||
// notify other peers of our registration
|
|
||||||
for _, remotePeer := range networkMap.Peers {
|
|
||||||
remotePeerNetworkMap, err := s.accountManager.GetNetworkMap(remotePeer.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(codes.Internal, "unable to fetch network map after registering peer, error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
update := toSyncResponse(s.config, remotePeer, nil, remotePeerNetworkMap, s.accountManager.GetDNSDomain())
|
|
||||||
err = s.peersUpdateManager.SendUpdate(remotePeer.ID, &UpdateMessage{Update: update})
|
|
||||||
if err != nil {
|
|
||||||
// todo rethink if we should keep this return
|
|
||||||
return nil, status.Errorf(codes.Internal, "unable to send update after registering peer, error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return peer, nil
|
return peerKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login endpoint first checks whether peer is registered under any account
|
// Login endpoint first checks whether peer is registered under any account
|
||||||
@ -323,113 +266,55 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p
|
|||||||
log.Debugf("Login request from peer [%s] [%s]", req.WgPubKey, p.Addr.String())
|
log.Debugf("Login request from peer [%s] [%s]", req.WgPubKey, p.Addr.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
peerKey, err := wgtypes.ParseKey(req.GetWgPubKey())
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("error while parsing peer's Wireguard public key %s on Sync request.", req.WgPubKey)
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "provided wgPubKey %s is invalid", req.WgPubKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
loginReq := &proto.LoginRequest{}
|
loginReq := &proto.LoginRequest{}
|
||||||
err = encryption.DecryptMessage(peerKey, s.wgKey, req.Body, loginReq)
|
peerKey, err := s.parseRequest(req, loginReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "invalid request message")
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peer, err := s.accountManager.GetPeerByKey(peerKey.String())
|
if loginReq.GetMeta() == nil {
|
||||||
if err != nil {
|
msg := status.Errorf(codes.FailedPrecondition,
|
||||||
if errStatus, ok := internalStatus.FromError(err); ok && errStatus.Type() == internalStatus.NotFound {
|
"peer system meta has to be provided to log in. Peer %s, remote addr %s", peerKey.String(),
|
||||||
// peer doesn't exist -> check if setup key was provided
|
p.Addr.String())
|
||||||
if loginReq.GetJwtToken() == "" && loginReq.GetSetupKey() == "" {
|
log.Warn(msg)
|
||||||
// absent setup key or jwt -> permission denied
|
return nil, msg
|
||||||
p, _ := gRPCPeer.FromContext(ctx)
|
}
|
||||||
msg := status.Errorf(codes.PermissionDenied,
|
|
||||||
"provided peer with the key wgPubKey %s is not registered and no setup key or jwt was provided,"+
|
|
||||||
" remote addr is %s", peerKey.String(), p.Addr.String())
|
|
||||||
log.Debug(msg)
|
|
||||||
return nil, msg
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup key or jwt is present -> try normal registration flow
|
userID := ""
|
||||||
peer, err = s.registerPeer(peerKey, loginReq)
|
// JWT token is not always provided, it is fine for userID to be empty cuz it might be that peer is already registered,
|
||||||
if err != nil {
|
// or it uses a setup key to register.
|
||||||
return nil, err
|
if loginReq.GetJwtToken() != "" {
|
||||||
}
|
userID, err = s.validateToken(loginReq.GetJwtToken())
|
||||||
|
|
||||||
} else {
|
|
||||||
return nil, status.Error(codes.Internal, "internal server error")
|
|
||||||
}
|
|
||||||
} else if loginReq.GetMeta() != nil {
|
|
||||||
// update peer's system meta data on Login
|
|
||||||
err = s.accountManager.UpdatePeerMeta(peer.ID, PeerSystemMeta{
|
|
||||||
Hostname: loginReq.GetMeta().GetHostname(),
|
|
||||||
GoOS: loginReq.GetMeta().GetGoOS(),
|
|
||||||
Kernel: loginReq.GetMeta().GetKernel(),
|
|
||||||
Core: loginReq.GetMeta().GetCore(),
|
|
||||||
Platform: loginReq.GetMeta().GetPlatform(),
|
|
||||||
OS: loginReq.GetMeta().GetOS(),
|
|
||||||
WtVersion: loginReq.GetMeta().GetWiretrusteeVersion(),
|
|
||||||
UIVersion: loginReq.GetMeta().GetUiVersion(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed updating peer system meta data %s", peerKey.String())
|
log.Warnf("failed validating JWT token sent from peer %s", peerKey)
|
||||||
return nil, status.Error(codes.Internal, "internal server error")
|
return nil, mapError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if peer login has expired
|
|
||||||
account, err := s.accountManager.GetAccountByPeerID(peer.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Error(codes.Internal, "internal server error")
|
|
||||||
}
|
|
||||||
|
|
||||||
expired, left := peer.LoginExpired(account.Settings.PeerLoginExpiration)
|
|
||||||
expired = account.Settings.PeerLoginExpirationEnabled && expired
|
|
||||||
if peer.UserID != "" && (expired || peer.Status.LoginExpired) {
|
|
||||||
// it might be that peer expired but user has logged in already, check token then
|
|
||||||
if loginReq.GetJwtToken() == "" {
|
|
||||||
err = s.accountManager.MarkPeerLoginExpired(peerKey.String(), true)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("failed marking peer login expired %s %v", peerKey, err)
|
|
||||||
}
|
|
||||||
return nil, status.Errorf(codes.PermissionDenied,
|
|
||||||
"peer login has expired %v ago. Please log in once more", left)
|
|
||||||
}
|
|
||||||
_, err = s.validateToken(loginReq.GetJwtToken())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.accountManager.UpdatePeerLastLogin(peer.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sshKey []byte
|
var sshKey []byte
|
||||||
if loginReq.GetPeerKeys() != nil {
|
if loginReq.GetPeerKeys() != nil {
|
||||||
sshKey = loginReq.GetPeerKeys().GetSshPubKey()
|
sshKey = loginReq.GetPeerKeys().GetSshPubKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sshKey) > 0 {
|
peer, netMap, err := s.accountManager.LoginPeer(PeerLogin{
|
||||||
err = s.accountManager.UpdatePeerSSHKey(peer.ID, string(sshKey))
|
WireGuardPubKey: peerKey.String(),
|
||||||
if err != nil {
|
SSHKey: string(sshKey),
|
||||||
return nil, err
|
Meta: extractPeerMeta(loginReq),
|
||||||
}
|
UserID: userID,
|
||||||
}
|
SetupKey: loginReq.GetSetupKey(),
|
||||||
|
})
|
||||||
network, err := s.accountManager.GetPeerNetwork(peer.ID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "failed getting peer network on login")
|
log.Warnf("failed logging in peer %s", peerKey)
|
||||||
|
return nil, mapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if peer has reached this point then it has logged in
|
// if peer has reached this point then it has logged in
|
||||||
loginResp := &proto.LoginResponse{
|
loginResp := &proto.LoginResponse{
|
||||||
WiretrusteeConfig: toWiretrusteeConfig(s.config, nil),
|
WiretrusteeConfig: toWiretrusteeConfig(s.config, nil),
|
||||||
PeerConfig: toPeerConfig(peer, network, s.accountManager.GetDNSDomain()),
|
PeerConfig: toPeerConfig(peer, netMap.Network, s.accountManager.GetDNSDomain()),
|
||||||
}
|
}
|
||||||
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, loginResp)
|
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, loginResp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Warnf("failed encrypting peer %s message", peer.ID)
|
||||||
return nil, status.Errorf(codes.Internal, "failed logging in peer")
|
return nil, status.Errorf(codes.Internal, "failed logging in peer")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,6 +418,8 @@ func toSyncResponse(config *Config, peer *Peer, turnCredentials *TURNCredentials
|
|||||||
|
|
||||||
dnsUpdate := toProtocolDNSConfig(networkMap.DNSConfig)
|
dnsUpdate := toProtocolDNSConfig(networkMap.DNSConfig)
|
||||||
|
|
||||||
|
offlinePeers := toRemotePeerConfig(networkMap.OfflinePeers, dnsName)
|
||||||
|
|
||||||
return &proto.SyncResponse{
|
return &proto.SyncResponse{
|
||||||
WiretrusteeConfig: wtConfig,
|
WiretrusteeConfig: wtConfig,
|
||||||
PeerConfig: pConfig,
|
PeerConfig: pConfig,
|
||||||
@ -542,6 +429,7 @@ func toSyncResponse(config *Config, peer *Peer, turnCredentials *TURNCredentials
|
|||||||
Serial: networkMap.Network.CurrentSerial(),
|
Serial: networkMap.Network.CurrentSerial(),
|
||||||
PeerConfig: pConfig,
|
PeerConfig: pConfig,
|
||||||
RemotePeers: remotePeers,
|
RemotePeers: remotePeers,
|
||||||
|
OfflinePeers: offlinePeers,
|
||||||
RemotePeersIsEmpty: len(remotePeers) == 0,
|
RemotePeersIsEmpty: len(remotePeers) == 0,
|
||||||
Routes: routesUpdate,
|
Routes: routesUpdate,
|
||||||
DNSConfig: dnsUpdate,
|
DNSConfig: dnsUpdate,
|
||||||
@ -555,13 +443,7 @@ func (s *GRPCServer) IsHealthy(ctx context.Context, req *proto.Empty) (*proto.Em
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sendInitialSync sends initial proto.SyncResponse to the peer requesting synchronization
|
// sendInitialSync sends initial proto.SyncResponse to the peer requesting synchronization
|
||||||
func (s *GRPCServer) sendInitialSync(peerKey wgtypes.Key, peer *Peer, srv proto.ManagementService_SyncServer) error {
|
func (s *GRPCServer) sendInitialSync(peerKey wgtypes.Key, peer *Peer, networkMap *NetworkMap, srv proto.ManagementService_SyncServer) error {
|
||||||
networkMap, err := s.accountManager.GetNetworkMap(peer.ID)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("error getting a list of peers for a peer %s", peer.ID)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// make secret time based TURN credentials optional
|
// make secret time based TURN credentials optional
|
||||||
var turnCredentials *TURNCredentials
|
var turnCredentials *TURNCredentials
|
||||||
if s.config.TURNConfig.TimeBasedCredentials {
|
if s.config.TURNConfig.TimeBasedCredentials {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
openapi: 3.0.1
|
openapi: 3.0.1
|
||||||
info:
|
info:
|
||||||
title: NetBird REST API
|
title: NetBird REST API
|
||||||
description: API to manipulate groups, rules and retrieve information about peers and users
|
description: API to manipulate groups, rules, policies and retrieve information about peers and users
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
tags:
|
tags:
|
||||||
- name: Users
|
- name: Users
|
||||||
@ -14,6 +14,8 @@ tags:
|
|||||||
description: Interact with and view information about groups.
|
description: Interact with and view information about groups.
|
||||||
- name: Rules
|
- name: Rules
|
||||||
description: Interact with and view information about rules.
|
description: Interact with and view information about rules.
|
||||||
|
- name: Policies
|
||||||
|
description: Interact with and view information about policies.
|
||||||
- name: Routes
|
- name: Routes
|
||||||
description: Interact with and view information about routes.
|
description: Interact with and view information about routes.
|
||||||
- name: DNS
|
- name: DNS
|
||||||
@ -393,6 +395,77 @@ components:
|
|||||||
enum: [ "name","description","disabled","flow","sources","destinations" ]
|
enum: [ "name","description","disabled","flow","sources","destinations" ]
|
||||||
required:
|
required:
|
||||||
- path
|
- path
|
||||||
|
PolicyRule:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: Rule ID
|
||||||
|
type: string
|
||||||
|
name:
|
||||||
|
description: Rule name identifier
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
description: Rule friendly description
|
||||||
|
type: string
|
||||||
|
enabled:
|
||||||
|
description: Rules status
|
||||||
|
type: boolean
|
||||||
|
sources:
|
||||||
|
description: policy source groups
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/GroupMinimum'
|
||||||
|
destinations:
|
||||||
|
description: policy destination groups
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/GroupMinimum'
|
||||||
|
action:
|
||||||
|
description: policy accept or drops packets
|
||||||
|
type: string
|
||||||
|
enum: ["accept","drop"]
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- sources
|
||||||
|
- destinations
|
||||||
|
- action
|
||||||
|
- enabled
|
||||||
|
PolicyMinimum:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
description: Policy name identifier
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
description: Policy friendly description
|
||||||
|
type: string
|
||||||
|
enabled:
|
||||||
|
description: Policy status
|
||||||
|
type: boolean
|
||||||
|
query:
|
||||||
|
description: Policy Rego query
|
||||||
|
type: string
|
||||||
|
rules:
|
||||||
|
description: Policy rule object for policy UI editor
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/PolicyRule'
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- description
|
||||||
|
- enabled
|
||||||
|
- query
|
||||||
|
- rules
|
||||||
|
Policy:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/PolicyMinimum'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
description: Policy ID
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- id
|
||||||
RouteRequest:
|
RouteRequest:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@ -574,12 +647,12 @@ components:
|
|||||||
"setupkey.peer.add", "setupkey.add", "setupkey.update", "setupkey.revoke", "setupkey.overuse",
|
"setupkey.peer.add", "setupkey.add", "setupkey.update", "setupkey.revoke", "setupkey.overuse",
|
||||||
"setupkey.group.delete", "setupkey.group.add",
|
"setupkey.group.delete", "setupkey.group.add",
|
||||||
"rule.add", "rule.delete", "rule.update",
|
"rule.add", "rule.delete", "rule.update",
|
||||||
|
"policy.add", "policy.delete", "policy.update",
|
||||||
"group.add", "group.update", "dns.setting.disabled.management.group.add", "dns.setting.disabled.management.group.delete",
|
"group.add", "group.update", "dns.setting.disabled.management.group.add", "dns.setting.disabled.management.group.delete",
|
||||||
"account.create", "account.setting.peer.login.expiration.update", "account.setting.peer.login.expiration.disable", "account.setting.peer.login.expiration.enable",
|
"account.create", "account.setting.peer.login.expiration.update", "account.setting.peer.login.expiration.disable", "account.setting.peer.login.expiration.enable",
|
||||||
"route.add", "route.delete", "route.update",
|
"route.add", "route.delete", "route.update",
|
||||||
"nameserver.group.add", "nameserver.group.delete", "nameserver.group.update",
|
"nameserver.group.add", "nameserver.group.delete", "nameserver.group.update",
|
||||||
"peer.ssh.disable", "peer.ssh.enable", "peer.rename", "peer.login.expiration.disable", "peer.login.expiration.enable"
|
"peer.ssh.disable", "peer.ssh.enable", "peer.rename", "peer.login.expiration.disable", "peer.login.expiration.enable" ]
|
||||||
]
|
|
||||||
initiator_id:
|
initiator_id:
|
||||||
description: The ID of the initiator of the event. E.g., an ID of a user that triggered the event.
|
description: The ID of the initiator of the event. E.g., an ID of a user that triggered the event.
|
||||||
type: string
|
type: string
|
||||||
@ -1337,41 +1410,6 @@ paths:
|
|||||||
"$ref": "#/components/responses/forbidden"
|
"$ref": "#/components/responses/forbidden"
|
||||||
'500':
|
'500':
|
||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
patch:
|
|
||||||
summary: Update information about a Rule
|
|
||||||
tags: [ Rules ]
|
|
||||||
security:
|
|
||||||
- BearerAuth: [ ]
|
|
||||||
parameters:
|
|
||||||
- in: path
|
|
||||||
name: id
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
description: The Rule ID
|
|
||||||
requestBody:
|
|
||||||
description: Update Rule request using a list of json patch objects
|
|
||||||
content:
|
|
||||||
'application/json':
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/RulePatchOperation'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: A Rule object
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/Rule'
|
|
||||||
'400':
|
|
||||||
"$ref": "#/components/responses/bad_request"
|
|
||||||
'401':
|
|
||||||
"$ref": "#/components/responses/requires_authentication"
|
|
||||||
'403':
|
|
||||||
"$ref": "#/components/responses/forbidden"
|
|
||||||
'500':
|
|
||||||
"$ref": "#/components/responses/internal_error"
|
|
||||||
delete:
|
delete:
|
||||||
summary: Delete a Rule
|
summary: Delete a Rule
|
||||||
tags: [ Rules ]
|
tags: [ Rules ]
|
||||||
@ -1396,7 +1434,134 @@ paths:
|
|||||||
"$ref": "#/components/responses/forbidden"
|
"$ref": "#/components/responses/forbidden"
|
||||||
'500':
|
'500':
|
||||||
"$ref": "#/components/responses/internal_error"
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
/api/policies:
|
||||||
|
get:
|
||||||
|
summary: Returns a list of all Policies
|
||||||
|
tags: [ Policies ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A JSON Array of Policies
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Policy'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
post:
|
||||||
|
summary: Creates a Policy
|
||||||
|
tags: [ Policies ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
requestBody:
|
||||||
|
description: New Policy request
|
||||||
|
content:
|
||||||
|
'application/json':
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/PolicyMinimum'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Policy Object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Policy'
|
||||||
|
/api/policies/{id}:
|
||||||
|
get:
|
||||||
|
summary: Get information about a Policies
|
||||||
|
tags: [ Policies ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The Policy ID
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Policy object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Policy'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
put:
|
||||||
|
summary: Update/Replace a Policy
|
||||||
|
tags: [ Policies ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The Policy ID
|
||||||
|
requestBody:
|
||||||
|
description: Update Policy request
|
||||||
|
content:
|
||||||
|
'application/json':
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/PolicyMinimum'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: A Policy object
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Policy'
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
|
delete:
|
||||||
|
summary: Delete a Policy
|
||||||
|
tags: [ Policies ]
|
||||||
|
security:
|
||||||
|
- BearerAuth: [ ]
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: The Policy ID
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Delete status code
|
||||||
|
content: { }
|
||||||
|
'400':
|
||||||
|
"$ref": "#/components/responses/bad_request"
|
||||||
|
'401':
|
||||||
|
"$ref": "#/components/responses/requires_authentication"
|
||||||
|
'403':
|
||||||
|
"$ref": "#/components/responses/forbidden"
|
||||||
|
'500':
|
||||||
|
"$ref": "#/components/responses/internal_error"
|
||||||
/api/routes:
|
/api/routes:
|
||||||
get:
|
get:
|
||||||
summary: Returns a list of all routes
|
summary: Returns a list of all routes
|
||||||
|
@ -29,6 +29,9 @@ const (
|
|||||||
EventActivityCodePeerRename EventActivityCode = "peer.rename"
|
EventActivityCodePeerRename EventActivityCode = "peer.rename"
|
||||||
EventActivityCodePeerSshDisable EventActivityCode = "peer.ssh.disable"
|
EventActivityCodePeerSshDisable EventActivityCode = "peer.ssh.disable"
|
||||||
EventActivityCodePeerSshEnable EventActivityCode = "peer.ssh.enable"
|
EventActivityCodePeerSshEnable EventActivityCode = "peer.ssh.enable"
|
||||||
|
EventActivityCodePolicyAdd EventActivityCode = "policy.add"
|
||||||
|
EventActivityCodePolicyDelete EventActivityCode = "policy.delete"
|
||||||
|
EventActivityCodePolicyUpdate EventActivityCode = "policy.update"
|
||||||
EventActivityCodeRouteAdd EventActivityCode = "route.add"
|
EventActivityCodeRouteAdd EventActivityCode = "route.add"
|
||||||
EventActivityCodeRouteDelete EventActivityCode = "route.delete"
|
EventActivityCodeRouteDelete EventActivityCode = "route.delete"
|
||||||
EventActivityCodeRouteUpdate EventActivityCode = "route.update"
|
EventActivityCodeRouteUpdate EventActivityCode = "route.update"
|
||||||
@ -94,6 +97,12 @@ const (
|
|||||||
PatchMinimumOpReplace PatchMinimumOp = "replace"
|
PatchMinimumOpReplace PatchMinimumOp = "replace"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Defines values for PolicyRuleAction.
|
||||||
|
const (
|
||||||
|
PolicyRuleActionAccept PolicyRuleAction = "accept"
|
||||||
|
PolicyRuleActionDrop PolicyRuleAction = "drop"
|
||||||
|
)
|
||||||
|
|
||||||
// Defines values for RoutePatchOperationOp.
|
// Defines values for RoutePatchOperationOp.
|
||||||
const (
|
const (
|
||||||
RoutePatchOperationOpAdd RoutePatchOperationOp = "add"
|
RoutePatchOperationOpAdd RoutePatchOperationOp = "add"
|
||||||
@ -113,23 +122,6 @@ const (
|
|||||||
RoutePatchOperationPathPeer RoutePatchOperationPath = "peer"
|
RoutePatchOperationPathPeer RoutePatchOperationPath = "peer"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defines values for RulePatchOperationOp.
|
|
||||||
const (
|
|
||||||
RulePatchOperationOpAdd RulePatchOperationOp = "add"
|
|
||||||
RulePatchOperationOpRemove RulePatchOperationOp = "remove"
|
|
||||||
RulePatchOperationOpReplace RulePatchOperationOp = "replace"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Defines values for RulePatchOperationPath.
|
|
||||||
const (
|
|
||||||
RulePatchOperationPathDescription RulePatchOperationPath = "description"
|
|
||||||
RulePatchOperationPathDestinations RulePatchOperationPath = "destinations"
|
|
||||||
RulePatchOperationPathDisabled RulePatchOperationPath = "disabled"
|
|
||||||
RulePatchOperationPathFlow RulePatchOperationPath = "flow"
|
|
||||||
RulePatchOperationPathName RulePatchOperationPath = "name"
|
|
||||||
RulePatchOperationPathSources RulePatchOperationPath = "sources"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Defines values for UserStatus.
|
// Defines values for UserStatus.
|
||||||
const (
|
const (
|
||||||
UserStatusActive UserStatus = "active"
|
UserStatusActive UserStatus = "active"
|
||||||
@ -387,6 +379,72 @@ type PeerMinimum struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Policy defines model for Policy.
|
||||||
|
type Policy struct {
|
||||||
|
// Description Policy friendly description
|
||||||
|
Description string `json:"description"`
|
||||||
|
|
||||||
|
// Enabled Policy status
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
|
||||||
|
// Id Policy ID
|
||||||
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// Name Policy name identifier
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Query Policy Rego query
|
||||||
|
Query string `json:"query"`
|
||||||
|
|
||||||
|
// Rules Policy rule object for policy UI editor
|
||||||
|
Rules []PolicyRule `json:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyMinimum defines model for PolicyMinimum.
|
||||||
|
type PolicyMinimum struct {
|
||||||
|
// Description Policy friendly description
|
||||||
|
Description string `json:"description"`
|
||||||
|
|
||||||
|
// Enabled Policy status
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
|
||||||
|
// Name Policy name identifier
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Query Policy Rego query
|
||||||
|
Query string `json:"query"`
|
||||||
|
|
||||||
|
// Rules Policy rule object for policy UI editor
|
||||||
|
Rules []PolicyRule `json:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyRule defines model for PolicyRule.
|
||||||
|
type PolicyRule struct {
|
||||||
|
// Action policy accept or drops packets
|
||||||
|
Action PolicyRuleAction `json:"action"`
|
||||||
|
|
||||||
|
// Description Rule friendly description
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
|
||||||
|
// Destinations policy destination groups
|
||||||
|
Destinations []GroupMinimum `json:"destinations"`
|
||||||
|
|
||||||
|
// Enabled Rules status
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
|
||||||
|
// Id Rule ID
|
||||||
|
Id *string `json:"id,omitempty"`
|
||||||
|
|
||||||
|
// Name Rule name identifier
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Sources policy source groups
|
||||||
|
Sources []GroupMinimum `json:"sources"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolicyRuleAction policy accept or drops packets
|
||||||
|
type PolicyRuleAction string
|
||||||
|
|
||||||
// Route defines model for Route.
|
// Route defines model for Route.
|
||||||
type Route struct {
|
type Route struct {
|
||||||
// Description Route description
|
// Description Route description
|
||||||
@ -504,24 +562,6 @@ type RuleMinimum struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RulePatchOperation defines model for RulePatchOperation.
|
|
||||||
type RulePatchOperation struct {
|
|
||||||
// Op Patch operation type
|
|
||||||
Op RulePatchOperationOp `json:"op"`
|
|
||||||
|
|
||||||
// Path Rule field to update in form /<field>
|
|
||||||
Path RulePatchOperationPath `json:"path"`
|
|
||||||
|
|
||||||
// Value Values to be applied
|
|
||||||
Value []string `json:"value"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RulePatchOperationOp Patch operation type
|
|
||||||
type RulePatchOperationOp string
|
|
||||||
|
|
||||||
// RulePatchOperationPath Rule field to update in form /<field>
|
|
||||||
type RulePatchOperationPath string
|
|
||||||
|
|
||||||
// SetupKey defines model for SetupKey.
|
// SetupKey defines model for SetupKey.
|
||||||
type SetupKey struct {
|
type SetupKey struct {
|
||||||
// AutoGroups Setup key groups to auto-assign to peers registered with this key
|
// AutoGroups Setup key groups to auto-assign to peers registered with this key
|
||||||
@ -666,6 +706,12 @@ type PutApiPeersIdJSONBody struct {
|
|||||||
SshEnabled bool `json:"ssh_enabled"`
|
SshEnabled bool `json:"ssh_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostApiPoliciesJSONBody defines parameters for PostApiPolicies.
|
||||||
|
type PostApiPoliciesJSONBody = PolicyMinimum
|
||||||
|
|
||||||
|
// PutApiPoliciesIdJSONBody defines parameters for PutApiPoliciesId.
|
||||||
|
type PutApiPoliciesIdJSONBody = PolicyMinimum
|
||||||
|
|
||||||
// PatchApiRoutesIdJSONBody defines parameters for PatchApiRoutesId.
|
// PatchApiRoutesIdJSONBody defines parameters for PatchApiRoutesId.
|
||||||
type PatchApiRoutesIdJSONBody = []RoutePatchOperation
|
type PatchApiRoutesIdJSONBody = []RoutePatchOperation
|
||||||
|
|
||||||
@ -686,9 +732,6 @@ type PostApiRulesJSONBody struct {
|
|||||||
Sources *[]string `json:"sources,omitempty"`
|
Sources *[]string `json:"sources,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PatchApiRulesIdJSONBody defines parameters for PatchApiRulesId.
|
|
||||||
type PatchApiRulesIdJSONBody = []RulePatchOperation
|
|
||||||
|
|
||||||
// PutApiRulesIdJSONBody defines parameters for PutApiRulesId.
|
// PutApiRulesIdJSONBody defines parameters for PutApiRulesId.
|
||||||
type PutApiRulesIdJSONBody struct {
|
type PutApiRulesIdJSONBody struct {
|
||||||
// Description Rule friendly description
|
// Description Rule friendly description
|
||||||
@ -733,6 +776,12 @@ type PutApiGroupsIdJSONRequestBody PutApiGroupsIdJSONBody
|
|||||||
// PutApiPeersIdJSONRequestBody defines body for PutApiPeersId for application/json ContentType.
|
// PutApiPeersIdJSONRequestBody defines body for PutApiPeersId for application/json ContentType.
|
||||||
type PutApiPeersIdJSONRequestBody PutApiPeersIdJSONBody
|
type PutApiPeersIdJSONRequestBody PutApiPeersIdJSONBody
|
||||||
|
|
||||||
|
// PostApiPoliciesJSONRequestBody defines body for PostApiPolicies for application/json ContentType.
|
||||||
|
type PostApiPoliciesJSONRequestBody = PostApiPoliciesJSONBody
|
||||||
|
|
||||||
|
// PutApiPoliciesIdJSONRequestBody defines body for PutApiPoliciesId for application/json ContentType.
|
||||||
|
type PutApiPoliciesIdJSONRequestBody = PutApiPoliciesIdJSONBody
|
||||||
|
|
||||||
// PostApiRoutesJSONRequestBody defines body for PostApiRoutes for application/json ContentType.
|
// PostApiRoutesJSONRequestBody defines body for PostApiRoutes for application/json ContentType.
|
||||||
type PostApiRoutesJSONRequestBody = RouteRequest
|
type PostApiRoutesJSONRequestBody = RouteRequest
|
||||||
|
|
||||||
@ -745,9 +794,6 @@ type PutApiRoutesIdJSONRequestBody = RouteRequest
|
|||||||
// PostApiRulesJSONRequestBody defines body for PostApiRules for application/json ContentType.
|
// PostApiRulesJSONRequestBody defines body for PostApiRules for application/json ContentType.
|
||||||
type PostApiRulesJSONRequestBody PostApiRulesJSONBody
|
type PostApiRulesJSONRequestBody PostApiRulesJSONBody
|
||||||
|
|
||||||
// PatchApiRulesIdJSONRequestBody defines body for PatchApiRulesId for application/json ContentType.
|
|
||||||
type PatchApiRulesIdJSONRequestBody = PatchApiRulesIdJSONBody
|
|
||||||
|
|
||||||
// PutApiRulesIdJSONRequestBody defines body for PutApiRulesId for application/json ContentType.
|
// PutApiRulesIdJSONRequestBody defines body for PutApiRulesId for application/json ContentType.
|
||||||
type PutApiRulesIdJSONRequestBody PutApiRulesIdJSONBody
|
type PutApiRulesIdJSONRequestBody PutApiRulesIdJSONBody
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@ func APIHandler(accountManager s.AccountManager, appMetrics telemetry.AppMetrics
|
|||||||
api.addUsersEndpoint()
|
api.addUsersEndpoint()
|
||||||
api.addSetupKeysEndpoint()
|
api.addSetupKeysEndpoint()
|
||||||
api.addRulesEndpoint()
|
api.addRulesEndpoint()
|
||||||
|
api.addPoliciesEndpoint()
|
||||||
api.addGroupsEndpoint()
|
api.addGroupsEndpoint()
|
||||||
api.addRoutesEndpoint()
|
api.addRoutesEndpoint()
|
||||||
api.addDNSNameserversEndpoint()
|
api.addDNSNameserversEndpoint()
|
||||||
@ -122,11 +123,19 @@ func (apiHandler *apiHandler) addRulesEndpoint() {
|
|||||||
apiHandler.Router.HandleFunc("/rules", rulesHandler.GetAllRules).Methods("GET", "OPTIONS")
|
apiHandler.Router.HandleFunc("/rules", rulesHandler.GetAllRules).Methods("GET", "OPTIONS")
|
||||||
apiHandler.Router.HandleFunc("/rules", rulesHandler.CreateRule).Methods("POST", "OPTIONS")
|
apiHandler.Router.HandleFunc("/rules", rulesHandler.CreateRule).Methods("POST", "OPTIONS")
|
||||||
apiHandler.Router.HandleFunc("/rules/{id}", rulesHandler.UpdateRule).Methods("PUT", "OPTIONS")
|
apiHandler.Router.HandleFunc("/rules/{id}", rulesHandler.UpdateRule).Methods("PUT", "OPTIONS")
|
||||||
apiHandler.Router.HandleFunc("/rules/{id}", rulesHandler.PatchRule).Methods("PATCH", "OPTIONS")
|
|
||||||
apiHandler.Router.HandleFunc("/rules/{id}", rulesHandler.GetRule).Methods("GET", "OPTIONS")
|
apiHandler.Router.HandleFunc("/rules/{id}", rulesHandler.GetRule).Methods("GET", "OPTIONS")
|
||||||
apiHandler.Router.HandleFunc("/rules/{id}", rulesHandler.DeleteRule).Methods("DELETE", "OPTIONS")
|
apiHandler.Router.HandleFunc("/rules/{id}", rulesHandler.DeleteRule).Methods("DELETE", "OPTIONS")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (apiHandler *apiHandler) addPoliciesEndpoint() {
|
||||||
|
policiesHandler := NewPoliciesHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||||
|
apiHandler.Router.HandleFunc("/policies", policiesHandler.GetAllPolicies).Methods("GET", "OPTIONS")
|
||||||
|
apiHandler.Router.HandleFunc("/policies", policiesHandler.CreatePolicy).Methods("POST", "OPTIONS")
|
||||||
|
apiHandler.Router.HandleFunc("/policies/{id}", policiesHandler.UpdatePolicy).Methods("PUT", "OPTIONS")
|
||||||
|
apiHandler.Router.HandleFunc("/policies/{id}", policiesHandler.GetPolicy).Methods("GET", "OPTIONS")
|
||||||
|
apiHandler.Router.HandleFunc("/policies/{id}", policiesHandler.DeletePolicy).Methods("DELETE", "OPTIONS")
|
||||||
|
}
|
||||||
|
|
||||||
func (apiHandler *apiHandler) addGroupsEndpoint() {
|
func (apiHandler *apiHandler) addGroupsEndpoint() {
|
||||||
groupsHandler := NewGroupsHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
groupsHandler := NewGroupsHandler(apiHandler.AccountManager, apiHandler.AuthCfg)
|
||||||
apiHandler.Router.HandleFunc("/groups", groupsHandler.GetAllGroups).Methods("GET", "OPTIONS")
|
apiHandler.Router.HandleFunc("/groups", groupsHandler.GetAllGroups).Methods("GET", "OPTIONS")
|
||||||
|
326
management/server/http/policies.go
Normal file
326
management/server/http/policies.go
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/rs/xid"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
|
"github.com/netbirdio/netbird/management/server/http/util"
|
||||||
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
||||||
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Policies is a handler that returns policy of the account
|
||||||
|
type Policies struct {
|
||||||
|
accountManager server.AccountManager
|
||||||
|
claimsExtractor *jwtclaims.ClaimsExtractor
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPoliciesHandler creates a new Policies handler
|
||||||
|
func NewPoliciesHandler(accountManager server.AccountManager, authCfg AuthCfg) *Policies {
|
||||||
|
return &Policies{
|
||||||
|
accountManager: accountManager,
|
||||||
|
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
||||||
|
jwtclaims.WithAudience(authCfg.Audience),
|
||||||
|
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllPolicies list for the account
|
||||||
|
func (h *Policies) GetAllPolicies(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
accountPolicies, err := h.accountManager.ListPolicies(account.Id, user.Id)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(w, accountPolicies)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePolicy handles update to a policy identified by a given ID
|
||||||
|
func (h *Policies) UpdatePolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
policyID := vars["id"]
|
||||||
|
if len(policyID) == 0 {
|
||||||
|
util.WriteError(status.Errorf(status.InvalidArgument, "invalid policy ID"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policyIdx := -1
|
||||||
|
for i, policy := range account.Policies {
|
||||||
|
if policy.ID == policyID {
|
||||||
|
policyIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if policyIdx < 0 {
|
||||||
|
util.WriteError(status.Errorf(status.NotFound, "couldn't find policy id %s", policyID), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req api.PutApiPoliciesIdJSONRequestBody
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Name == "" {
|
||||||
|
util.WriteError(status.Errorf(status.InvalidArgument, "policy name shouldn't be empty"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policy := server.Policy{
|
||||||
|
ID: policyID,
|
||||||
|
Name: req.Name,
|
||||||
|
Enabled: req.Enabled,
|
||||||
|
Description: req.Description,
|
||||||
|
Query: req.Query,
|
||||||
|
}
|
||||||
|
if req.Rules != nil {
|
||||||
|
for _, r := range req.Rules {
|
||||||
|
pr := server.PolicyRule{
|
||||||
|
Destinations: groupMinimumsToStrings(account, r.Destinations),
|
||||||
|
Sources: groupMinimumsToStrings(account, r.Sources),
|
||||||
|
Name: r.Name,
|
||||||
|
}
|
||||||
|
pr.Enabled = r.Enabled
|
||||||
|
if r.Description != nil {
|
||||||
|
pr.Description = *r.Description
|
||||||
|
}
|
||||||
|
if r.Id != nil {
|
||||||
|
pr.ID = *r.Id
|
||||||
|
}
|
||||||
|
switch r.Action {
|
||||||
|
case api.PolicyRuleActionAccept:
|
||||||
|
pr.Action = server.PolicyTrafficActionAccept
|
||||||
|
case api.PolicyRuleActionDrop:
|
||||||
|
pr.Action = server.PolicyTrafficActionDrop
|
||||||
|
default:
|
||||||
|
util.WriteError(status.Errorf(status.InvalidArgument, "unknown action type"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
policy.Rules = append(policy.Rules, &pr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := policy.UpdateQueryFromRules(); err != nil {
|
||||||
|
log.Errorf("failed to update policy query: %v", err)
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = h.accountManager.SavePolicy(account.Id, user.Id, &policy); err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(w, toPolicyResponse(account, &policy))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePolicy handles policy creation request
|
||||||
|
func (h *Policies) CreatePolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req api.PostApiPoliciesJSONRequestBody
|
||||||
|
err = json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Name == "" {
|
||||||
|
util.WriteError(status.Errorf(status.InvalidArgument, "policy name shouldn't be empty"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policy := &server.Policy{
|
||||||
|
ID: xid.New().String(),
|
||||||
|
Name: req.Name,
|
||||||
|
Enabled: req.Enabled,
|
||||||
|
Description: req.Description,
|
||||||
|
Query: req.Query,
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Rules != nil {
|
||||||
|
for _, r := range req.Rules {
|
||||||
|
pr := server.PolicyRule{
|
||||||
|
ID: xid.New().String(),
|
||||||
|
Destinations: groupMinimumsToStrings(account, r.Destinations),
|
||||||
|
Sources: groupMinimumsToStrings(account, r.Sources),
|
||||||
|
Name: r.Name,
|
||||||
|
}
|
||||||
|
pr.Enabled = r.Enabled
|
||||||
|
if r.Description != nil {
|
||||||
|
pr.Description = *r.Description
|
||||||
|
}
|
||||||
|
switch r.Action {
|
||||||
|
case api.PolicyRuleActionAccept:
|
||||||
|
pr.Action = server.PolicyTrafficActionAccept
|
||||||
|
case api.PolicyRuleActionDrop:
|
||||||
|
pr.Action = server.PolicyTrafficActionDrop
|
||||||
|
default:
|
||||||
|
util.WriteError(status.Errorf(status.InvalidArgument, "unknown action type"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
policy.Rules = append(policy.Rules, &pr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := policy.UpdateQueryFromRules(); err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = h.accountManager.SavePolicy(account.Id, user.Id, policy); err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(w, toPolicyResponse(account, policy))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePolicy handles policy deletion request
|
||||||
|
func (h *Policies) DeletePolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
aID := account.Id
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
policyID := vars["id"]
|
||||||
|
if len(policyID) == 0 {
|
||||||
|
util.WriteError(status.Errorf(status.InvalidArgument, "invalid policy ID"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = h.accountManager.DeletePolicy(aID, policyID, user.Id); err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(w, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPolicy handles a group Get request identified by ID
|
||||||
|
func (h *Policies) GetPolicy(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims := h.claimsExtractor.FromRequestContext(r)
|
||||||
|
account, user, err := h.accountManager.GetAccountFromToken(claims)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch r.Method {
|
||||||
|
case http.MethodGet:
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
policyID := vars["id"]
|
||||||
|
if len(policyID) == 0 {
|
||||||
|
util.WriteError(status.Errorf(status.InvalidArgument, "invalid policy ID"), w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
policy, err := h.accountManager.GetPolicy(account.Id, policyID, user.Id)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteJSONObject(w, toPolicyResponse(account, policy))
|
||||||
|
default:
|
||||||
|
util.WriteError(status.Errorf(status.NotFound, "method not found"), w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toPolicyResponse(account *server.Account, policy *server.Policy) *api.Policy {
|
||||||
|
cache := make(map[string]api.GroupMinimum)
|
||||||
|
ap := &api.Policy{
|
||||||
|
Id: policy.ID,
|
||||||
|
Name: policy.Name,
|
||||||
|
Description: policy.Description,
|
||||||
|
Enabled: policy.Enabled,
|
||||||
|
Query: policy.Query,
|
||||||
|
}
|
||||||
|
if len(policy.Rules) == 0 {
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range policy.Rules {
|
||||||
|
rule := api.PolicyRule{
|
||||||
|
Id: &r.ID,
|
||||||
|
Name: r.Name,
|
||||||
|
Enabled: r.Enabled,
|
||||||
|
Description: &r.Description,
|
||||||
|
}
|
||||||
|
for _, gid := range r.Sources {
|
||||||
|
_, ok := cache[gid]
|
||||||
|
if ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if group, ok := account.Groups[gid]; ok {
|
||||||
|
minimum := api.GroupMinimum{
|
||||||
|
Id: group.ID,
|
||||||
|
Name: group.Name,
|
||||||
|
PeersCount: len(group.Peers),
|
||||||
|
}
|
||||||
|
rule.Sources = append(rule.Sources, minimum)
|
||||||
|
cache[gid] = minimum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, gid := range r.Destinations {
|
||||||
|
cachedMinimum, ok := cache[gid]
|
||||||
|
if ok {
|
||||||
|
rule.Destinations = append(rule.Destinations, cachedMinimum)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if group, ok := account.Groups[gid]; ok {
|
||||||
|
minimum := api.GroupMinimum{
|
||||||
|
Id: group.ID,
|
||||||
|
Name: group.Name,
|
||||||
|
PeersCount: len(group.Peers),
|
||||||
|
}
|
||||||
|
rule.Destinations = append(rule.Destinations, minimum)
|
||||||
|
cache[gid] = minimum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ap.Rules = append(ap.Rules, rule)
|
||||||
|
}
|
||||||
|
return ap
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupMinimumsToStrings(account *server.Account, gm []api.GroupMinimum) []string {
|
||||||
|
result := make([]string, 0, len(gm))
|
||||||
|
for _, gm := range gm {
|
||||||
|
if _, ok := account.Groups[gm.Id]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, gm.Id)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
@ -40,14 +40,16 @@ func (h *RulesHandler) GetAllRules(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accountRules, err := h.accountManager.ListRules(account.Id, user.Id)
|
accountPolicies, err := h.accountManager.ListPolicies(account.Id, user.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(err, w)
|
util.WriteError(err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rules := []*api.Rule{}
|
rules := []*api.Rule{}
|
||||||
for _, r := range accountRules {
|
for _, policy := range accountPolicies {
|
||||||
rules = append(rules, toRuleResponse(account, r))
|
for _, r := range policy.Rules {
|
||||||
|
rules = append(rules, toRuleResponse(account, r.ToRule()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
util.WriteJSONObject(w, rules)
|
util.WriteJSONObject(w, rules)
|
||||||
@ -69,9 +71,9 @@ func (h *RulesHandler) UpdateRule(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok := account.Rules[ruleID]
|
policy, err := h.accountManager.GetPolicy(account.Id, ruleID, user.Id)
|
||||||
if !ok {
|
if err != nil {
|
||||||
util.WriteError(status.Errorf(status.NotFound, "couldn't find rule id %s", ruleID), w)
|
util.WriteError(err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,173 +98,40 @@ func (h *RulesHandler) UpdateRule(w http.ResponseWriter, r *http.Request) {
|
|||||||
reqDestinations = *req.Destinations
|
reqDestinations = *req.Destinations
|
||||||
}
|
}
|
||||||
|
|
||||||
rule := server.Rule{
|
if len(policy.Rules) != 1 {
|
||||||
ID: ruleID,
|
util.WriteError(status.Errorf(status.Internal, "policy should contain exactly one rule"), w)
|
||||||
Name: req.Name,
|
return
|
||||||
Source: reqSources,
|
}
|
||||||
Destination: reqDestinations,
|
|
||||||
Disabled: req.Disabled,
|
policy.Name = req.Name
|
||||||
Description: req.Description,
|
policy.Description = req.Description
|
||||||
|
policy.Enabled = !req.Disabled
|
||||||
|
policy.Rules[0].ID = ruleID
|
||||||
|
policy.Rules[0].Name = req.Name
|
||||||
|
policy.Rules[0].Sources = reqSources
|
||||||
|
policy.Rules[0].Destinations = reqDestinations
|
||||||
|
policy.Rules[0].Enabled = !req.Disabled
|
||||||
|
policy.Rules[0].Description = req.Description
|
||||||
|
if err := policy.UpdateQueryFromRules(); err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch req.Flow {
|
switch req.Flow {
|
||||||
case server.TrafficFlowBidirectString:
|
case server.TrafficFlowBidirectString:
|
||||||
rule.Flow = server.TrafficFlowBidirect
|
policy.Rules[0].Action = server.PolicyTrafficActionAccept
|
||||||
default:
|
default:
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument, "unknown flow type"), w)
|
util.WriteError(status.Errorf(status.InvalidArgument, "unknown flow type"), w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.accountManager.SaveRule(account.Id, user.Id, &rule)
|
err = h.accountManager.SavePolicy(account.Id, user.Id, policy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(err, w)
|
util.WriteError(err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := toRuleResponse(account, &rule)
|
resp := toRuleResponse(account, policy.Rules[0].ToRule())
|
||||||
|
|
||||||
util.WriteJSONObject(w, &resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PatchRule handles patch updates to a rule identified by a given ID
|
|
||||||
func (h *RulesHandler) PatchRule(w http.ResponseWriter, r *http.Request) {
|
|
||||||
claims := h.claimsExtractor.FromRequestContext(r)
|
|
||||||
account, _, err := h.accountManager.GetAccountFromToken(claims)
|
|
||||||
if err != nil {
|
|
||||||
util.WriteError(err, w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
|
||||||
ruleID := vars["id"]
|
|
||||||
if len(ruleID) == 0 {
|
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument, "invalid rule ID"), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, ok := account.Rules[ruleID]
|
|
||||||
if !ok {
|
|
||||||
util.WriteError(status.Errorf(status.NotFound, "couldn't find rule ID %s", ruleID), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var req api.PatchApiRulesIdJSONRequestBody
|
|
||||||
err = json.NewDecoder(r.Body).Decode(&req)
|
|
||||||
if err != nil {
|
|
||||||
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(req) == 0 {
|
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument, "no patch instruction received"), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var operations []server.RuleUpdateOperation
|
|
||||||
|
|
||||||
for _, patch := range req {
|
|
||||||
switch patch.Path {
|
|
||||||
case api.RulePatchOperationPathName:
|
|
||||||
if patch.Op != api.RulePatchOperationOpReplace {
|
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument,
|
|
||||||
"name field only accepts replace operation, got %s", patch.Op), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(patch.Value) == 0 || patch.Value[0] == "" {
|
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument, "rule name shouldn't be empty"), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
operations = append(operations, server.RuleUpdateOperation{
|
|
||||||
Type: server.UpdateRuleName,
|
|
||||||
Values: patch.Value,
|
|
||||||
})
|
|
||||||
case api.RulePatchOperationPathDescription:
|
|
||||||
if patch.Op != api.RulePatchOperationOpReplace {
|
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument,
|
|
||||||
"description field only accepts replace operation, got %s", patch.Op), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
operations = append(operations, server.RuleUpdateOperation{
|
|
||||||
Type: server.UpdateRuleDescription,
|
|
||||||
Values: patch.Value,
|
|
||||||
})
|
|
||||||
case api.RulePatchOperationPathFlow:
|
|
||||||
if patch.Op != api.RulePatchOperationOpReplace {
|
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument,
|
|
||||||
"flow field only accepts replace operation, got %s", patch.Op), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
operations = append(operations, server.RuleUpdateOperation{
|
|
||||||
Type: server.UpdateRuleFlow,
|
|
||||||
Values: patch.Value,
|
|
||||||
})
|
|
||||||
case api.RulePatchOperationPathDisabled:
|
|
||||||
if patch.Op != api.RulePatchOperationOpReplace {
|
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument,
|
|
||||||
"disabled field only accepts replace operation, got %s", patch.Op), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
operations = append(operations, server.RuleUpdateOperation{
|
|
||||||
Type: server.UpdateRuleStatus,
|
|
||||||
Values: patch.Value,
|
|
||||||
})
|
|
||||||
case api.RulePatchOperationPathSources:
|
|
||||||
switch patch.Op {
|
|
||||||
case api.RulePatchOperationOpReplace:
|
|
||||||
operations = append(operations, server.RuleUpdateOperation{
|
|
||||||
Type: server.UpdateSourceGroups,
|
|
||||||
Values: patch.Value,
|
|
||||||
})
|
|
||||||
case api.RulePatchOperationOpRemove:
|
|
||||||
operations = append(operations, server.RuleUpdateOperation{
|
|
||||||
Type: server.RemoveGroupsFromSource,
|
|
||||||
Values: patch.Value,
|
|
||||||
})
|
|
||||||
case api.RulePatchOperationOpAdd:
|
|
||||||
operations = append(operations, server.RuleUpdateOperation{
|
|
||||||
Type: server.InsertGroupsToSource,
|
|
||||||
Values: patch.Value,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument,
|
|
||||||
"invalid operation \"%s\" on Source field", patch.Op), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case api.RulePatchOperationPathDestinations:
|
|
||||||
switch patch.Op {
|
|
||||||
case api.RulePatchOperationOpReplace:
|
|
||||||
operations = append(operations, server.RuleUpdateOperation{
|
|
||||||
Type: server.UpdateDestinationGroups,
|
|
||||||
Values: patch.Value,
|
|
||||||
})
|
|
||||||
case api.RulePatchOperationOpRemove:
|
|
||||||
operations = append(operations, server.RuleUpdateOperation{
|
|
||||||
Type: server.RemoveGroupsFromDestination,
|
|
||||||
Values: patch.Value,
|
|
||||||
})
|
|
||||||
case api.RulePatchOperationOpAdd:
|
|
||||||
operations = append(operations, server.RuleUpdateOperation{
|
|
||||||
Type: server.InsertGroupsToDestination,
|
|
||||||
Values: patch.Value,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument,
|
|
||||||
"invalid operation \"%s\" on Destination field", patch.Op), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
util.WriteError(status.Errorf(status.InvalidArgument, "invalid patch path"), w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rule, err := h.accountManager.UpdateRule(account.Id, ruleID, operations)
|
|
||||||
if err != nil {
|
|
||||||
util.WriteError(err, w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resp := toRuleResponse(account, rule)
|
|
||||||
|
|
||||||
util.WriteJSONObject(w, &resp)
|
util.WriteJSONObject(w, &resp)
|
||||||
}
|
}
|
||||||
@ -315,7 +184,12 @@ func (h *RulesHandler) CreateRule(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.accountManager.SaveRule(account.Id, user.Id, &rule)
|
policy, err := server.RuleToPolicy(&rule)
|
||||||
|
if err != nil {
|
||||||
|
util.WriteError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = h.accountManager.SavePolicy(account.Id, user.Id, policy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(err, w)
|
util.WriteError(err, w)
|
||||||
return
|
return
|
||||||
@ -342,7 +216,7 @@ func (h *RulesHandler) DeleteRule(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = h.accountManager.DeleteRule(aID, rID, user.Id)
|
err = h.accountManager.DeletePolicy(aID, rID, user.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(err, w)
|
util.WriteError(err, w)
|
||||||
return
|
return
|
||||||
@ -368,13 +242,13 @@ func (h *RulesHandler) GetRule(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rule, err := h.accountManager.GetRule(account.Id, ruleID, user.Id)
|
policy, err := h.accountManager.GetPolicy(account.Id, ruleID, user.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(status.Errorf(status.NotFound, "rule not found"), w)
|
util.WriteError(err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
util.WriteJSONObject(w, toRuleResponse(account, rule))
|
util.WriteJSONObject(w, toRuleResponse(account, policy.Rules[0].ToRule()))
|
||||||
default:
|
default:
|
||||||
util.WriteError(status.Errorf(status.NotFound, "method not found"), w)
|
util.WriteError(status.Errorf(status.NotFound, "method not found"), w)
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/http/api"
|
"github.com/netbirdio/netbird/management/server/http/api"
|
||||||
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
@ -23,8 +24,32 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func initRulesTestData(rules ...*server.Rule) *RulesHandler {
|
func initRulesTestData(rules ...*server.Rule) *RulesHandler {
|
||||||
|
testPolicies := make(map[string]*server.Policy, len(rules))
|
||||||
|
for _, rule := range rules {
|
||||||
|
policy, err := server.RuleToPolicy(rule)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err := policy.UpdateQueryFromRules(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
testPolicies[policy.ID] = policy
|
||||||
|
}
|
||||||
return &RulesHandler{
|
return &RulesHandler{
|
||||||
accountManager: &mock_server.MockAccountManager{
|
accountManager: &mock_server.MockAccountManager{
|
||||||
|
GetPolicyFunc: func(_, policyID, _ string) (*server.Policy, error) {
|
||||||
|
policy, ok := testPolicies[policyID]
|
||||||
|
if !ok {
|
||||||
|
return nil, status.Errorf(status.NotFound, "policy not found")
|
||||||
|
}
|
||||||
|
return policy, nil
|
||||||
|
},
|
||||||
|
SavePolicyFunc: func(_, _ string, policy *server.Policy) error {
|
||||||
|
if !strings.HasPrefix(policy.ID, "id-") {
|
||||||
|
policy.ID = "id-was-set"
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
SaveRuleFunc: func(_, _ string, rule *server.Rule) error {
|
SaveRuleFunc: func(_, _ string, rule *server.Rule) error {
|
||||||
if !strings.HasPrefix(rule.ID, "id-") {
|
if !strings.HasPrefix(rule.ID, "id-") {
|
||||||
rule.ID = "id-was-set"
|
rule.ID = "id-was-set"
|
||||||
@ -43,32 +68,6 @@ func initRulesTestData(rules ...*server.Rule) *RulesHandler {
|
|||||||
Flow: server.TrafficFlowBidirect,
|
Flow: server.TrafficFlowBidirect,
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
UpdateRuleFunc: func(_ string, ruleID string, operations []server.RuleUpdateOperation) (*server.Rule, error) {
|
|
||||||
var rule server.Rule
|
|
||||||
rule.ID = ruleID
|
|
||||||
for _, operation := range operations {
|
|
||||||
switch operation.Type {
|
|
||||||
case server.UpdateRuleName:
|
|
||||||
rule.Name = operation.Values[0]
|
|
||||||
case server.UpdateRuleDescription:
|
|
||||||
rule.Description = operation.Values[0]
|
|
||||||
case server.UpdateRuleFlow:
|
|
||||||
if server.TrafficFlowBidirectString == operation.Values[0] {
|
|
||||||
rule.Flow = server.TrafficFlowBidirect
|
|
||||||
} else {
|
|
||||||
rule.Flow = 100
|
|
||||||
}
|
|
||||||
case server.UpdateSourceGroups, server.InsertGroupsToSource:
|
|
||||||
rule.Source = operation.Values
|
|
||||||
case server.UpdateDestinationGroups, server.InsertGroupsToDestination:
|
|
||||||
rule.Destination = operation.Values
|
|
||||||
case server.RemoveGroupsFromSource, server.RemoveGroupsFromDestination:
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("no operation")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &rule, nil
|
|
||||||
},
|
|
||||||
GetAccountFromTokenFunc: func(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User, error) {
|
GetAccountFromTokenFunc: func(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User, error) {
|
||||||
user := server.NewAdminUser("test_user")
|
user := server.NewAdminUser("test_user")
|
||||||
return &server.Account{
|
return &server.Account{
|
||||||
@ -221,58 +220,13 @@ func TestRulesWriteRule(t *testing.T) {
|
|||||||
[]byte(`{"Name":"","Flow":"bidirect"}`)),
|
[]byte(`{"Name":"","Flow":"bidirect"}`)),
|
||||||
expectedStatus: http.StatusUnprocessableEntity,
|
expectedStatus: http.StatusUnprocessableEntity,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Write Rule PATCH Name OK",
|
|
||||||
requestType: http.MethodPatch,
|
|
||||||
requestPath: "/api/rules/id-existed",
|
|
||||||
requestBody: bytes.NewBuffer(
|
|
||||||
[]byte(`[{"op":"replace","path":"name","value":["Default POSTed Rule"]}]`)),
|
|
||||||
expectedStatus: http.StatusOK,
|
|
||||||
expectedBody: true,
|
|
||||||
expectedRule: &api.Rule{
|
|
||||||
Id: "id-existed",
|
|
||||||
Name: "Default POSTed Rule",
|
|
||||||
Flow: server.TrafficFlowBidirectString,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Write Rule PATCH Invalid Name OP",
|
|
||||||
requestType: http.MethodPatch,
|
|
||||||
requestPath: "/api/rules/id-existed",
|
|
||||||
requestBody: bytes.NewBuffer(
|
|
||||||
[]byte(`[{"op":"insert","path":"name","value":[""]}]`)),
|
|
||||||
expectedStatus: http.StatusUnprocessableEntity,
|
|
||||||
expectedBody: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Write Rule PATCH Invalid Name",
|
|
||||||
requestType: http.MethodPatch,
|
|
||||||
requestPath: "/api/rules/id-existed",
|
|
||||||
requestBody: bytes.NewBuffer(
|
|
||||||
[]byte(`[{"op":"replace","path":"name","value":[]}]`)),
|
|
||||||
expectedStatus: http.StatusUnprocessableEntity,
|
|
||||||
expectedBody: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Write Rule PATCH Sources OK",
|
|
||||||
requestType: http.MethodPatch,
|
|
||||||
requestPath: "/api/rules/id-existed",
|
|
||||||
requestBody: bytes.NewBuffer(
|
|
||||||
[]byte(`[{"op":"replace","path":"sources","value":["G","F"]}]`)),
|
|
||||||
expectedStatus: http.StatusOK,
|
|
||||||
expectedBody: true,
|
|
||||||
expectedRule: &api.Rule{
|
|
||||||
Id: "id-existed",
|
|
||||||
Flow: server.TrafficFlowBidirectString,
|
|
||||||
Sources: []api.GroupMinimum{
|
|
||||||
{Id: "G"},
|
|
||||||
{Id: "F"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p := initRulesTestData()
|
p := initRulesTestData(&server.Rule{
|
||||||
|
ID: "id-existed",
|
||||||
|
Name: "Default POSTed Rule",
|
||||||
|
Flow: server.TrafficFlowBidirect,
|
||||||
|
})
|
||||||
|
|
||||||
for _, tc := range tt {
|
for _, tc := range tt {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
@ -282,7 +236,6 @@ func TestRulesWriteRule(t *testing.T) {
|
|||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
router.HandleFunc("/api/rules", p.CreateRule).Methods("POST")
|
router.HandleFunc("/api/rules", p.CreateRule).Methods("POST")
|
||||||
router.HandleFunc("/api/rules/{id}", p.UpdateRule).Methods("PUT")
|
router.HandleFunc("/api/rules/{id}", p.UpdateRule).Methods("PUT")
|
||||||
router.HandleFunc("/api/rules/{id}", p.PatchRule).Methods("PATCH")
|
|
||||||
router.ServeHTTP(recorder, req)
|
router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
res := recorder.Result()
|
res := recorder.Result()
|
||||||
@ -307,6 +260,7 @@ func TestRulesWriteRule(t *testing.T) {
|
|||||||
if err = json.Unmarshal(content, &got); err != nil {
|
if err = json.Unmarshal(content, &got); err != nil {
|
||||||
t.Fatalf("Sent content is not in correct json format; %v", err)
|
t.Fatalf("Sent content is not in correct json format; %v", err)
|
||||||
}
|
}
|
||||||
|
tc.expectedRule.Id = got.Id
|
||||||
|
|
||||||
assert.Equal(t, got, tc.expectedRule)
|
assert.Equal(t, got, tc.expectedRule)
|
||||||
})
|
})
|
||||||
|
@ -75,7 +75,7 @@ func getServerKey(client mgmtProto.ManagementServiceClient) (*wgtypes.Key, error
|
|||||||
|
|
||||||
func Test_SyncProtocol(t *testing.T) {
|
func Test_SyncProtocol(t *testing.T) {
|
||||||
dir := t.TempDir()
|
dir := t.TempDir()
|
||||||
err := util.CopyFileContents("testdata/store.json", filepath.Join(dir, "store.json"))
|
err := util.CopyFileContents("testdata/store_with_expired_peers.json", filepath.Join(dir, "store.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -117,6 +117,7 @@ func Test_SyncProtocol(t *testing.T) {
|
|||||||
|
|
||||||
defer clientConn.Close()
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
// there are two peers already in the store, add two more
|
||||||
peers, err := registerPeers(2, client)
|
peers, err := registerPeers(2, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -129,7 +130,7 @@ func Test_SyncProtocol(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// take the first registered peer as a base for the test
|
// take the first registered peer as a base for the test. Total four.
|
||||||
key := *peers[0]
|
key := *peers[0]
|
||||||
|
|
||||||
message, err := encryption.EncryptMessage(*serverKey, key, &mgmtProto.SyncRequest{})
|
message, err := encryption.EncryptMessage(*serverKey, key, &mgmtProto.SyncRequest{})
|
||||||
@ -242,8 +243,18 @@ func Test_SyncProtocol(t *testing.T) {
|
|||||||
t.Fatal("expecting SyncResponse to have non-nil NetworkMap")
|
t.Fatal("expecting SyncResponse to have non-nil NetworkMap")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(networkMap.GetRemotePeers()) != 1 {
|
if len(networkMap.GetRemotePeers()) != 3 {
|
||||||
t.Fatal("expecting SyncResponse to have NetworkMap with 1 remote peer")
|
t.Fatalf("expecting SyncResponse to have NetworkMap with 3 remote peers, got %d", len(networkMap.GetRemotePeers()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// expired peers come separately.
|
||||||
|
if len(networkMap.GetOfflinePeers()) != 1 {
|
||||||
|
t.Fatal("expecting SyncResponse to have NetworkMap with 1 offline peer")
|
||||||
|
}
|
||||||
|
|
||||||
|
expiredPeerPubKey := "RlSy2vzoG2HyMBTUImXOiVhCBiiBa5qD5xzMxkiFDW4="
|
||||||
|
if networkMap.GetOfflinePeers()[0].WgPubKey != expiredPeerPubKey {
|
||||||
|
t.Fatalf("expecting SyncResponse to have NetworkMap with 1 offline peer with a key %s", expiredPeerPubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if networkMap.GetPeerConfig() == nil {
|
if networkMap.GetPeerConfig() == nil {
|
||||||
|
@ -2,7 +2,6 @@ package server_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
@ -11,6 +10,8 @@ import (
|
|||||||
sync2 "sync"
|
sync2 "sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
|
||||||
server "github.com/netbirdio/netbird/management/server"
|
server "github.com/netbirdio/netbird/management/server"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ValidSetupKey = "A2C8E62B-38F5-4553-B31E-DD66C696CEBB"
|
ValidSetupKey = "A2C8E62B-38F5-4553-B31E-DD66C696CEBB"
|
||||||
|
AccountKey = "bf1c8084-ba50-4ce7-9439-34653001fc3b"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Management service", func() {
|
var _ = Describe("Management service", func() {
|
||||||
@ -127,6 +129,7 @@ var _ = Describe("Management service", func() {
|
|||||||
actualTURN := resp.WiretrusteeConfig.Turns[0]
|
actualTURN := resp.WiretrusteeConfig.Turns[0]
|
||||||
Expect(len(actualTURN.User) > 0).To(BeTrue())
|
Expect(len(actualTURN.User) > 0).To(BeTrue())
|
||||||
Expect(actualTURN.HostConfig).To(BeEquivalentTo(expectedTRUNHost))
|
Expect(actualTURN.HostConfig).To(BeEquivalentTo(expectedTRUNHost))
|
||||||
|
Expect(len(resp.NetworkMap.OfflinePeers) == 0).To(BeTrue())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -240,7 +243,8 @@ var _ = Describe("Management service", func() {
|
|||||||
Context("with an invalid setup key", func() {
|
Context("with an invalid setup key", func() {
|
||||||
Specify("an error is returned", func() {
|
Specify("an error is returned", func() {
|
||||||
key, _ := wgtypes.GenerateKey()
|
key, _ := wgtypes.GenerateKey()
|
||||||
message, err := encryption.EncryptMessage(serverPubKey, key, &mgmtProto.LoginRequest{SetupKey: "invalid setup key"})
|
message, err := encryption.EncryptMessage(serverPubKey, key, &mgmtProto.LoginRequest{SetupKey: "invalid setup key",
|
||||||
|
Meta: &mgmtProto.PeerSystemMeta{}})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
resp, err := client.Login(context.TODO(), &mgmtProto.EncryptedMessage{
|
resp, err := client.Login(context.TODO(), &mgmtProto.EncryptedMessage{
|
||||||
@ -269,7 +273,7 @@ var _ = Describe("Management service", func() {
|
|||||||
Expect(regResp).NotTo(BeNil())
|
Expect(regResp).NotTo(BeNil())
|
||||||
|
|
||||||
// just login without registration
|
// just login without registration
|
||||||
message, err := encryption.EncryptMessage(serverPubKey, key, &mgmtProto.LoginRequest{})
|
message, err := encryption.EncryptMessage(serverPubKey, key, &mgmtProto.LoginRequest{Meta: &mgmtProto.PeerSystemMeta{}})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
loginResp, err := client.Login(context.TODO(), &mgmtProto.EncryptedMessage{
|
loginResp, err := client.Login(context.TODO(), &mgmtProto.EncryptedMessage{
|
||||||
WgPubKey: key.PublicKey().String(),
|
WgPubKey: key.PublicKey().String(),
|
||||||
|
@ -5,16 +5,18 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hashicorp/go-version"
|
|
||||||
"github.com/netbirdio/netbird/client/system"
|
|
||||||
"github.com/netbirdio/netbird/management/server"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-version"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server"
|
||||||
|
nbversion "github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -176,7 +178,7 @@ func (w *Worker) generateProperties() properties {
|
|||||||
osUIClients = make(map[string]int)
|
osUIClients = make(map[string]int)
|
||||||
uptime = time.Since(w.startupTime).Seconds()
|
uptime = time.Since(w.startupTime).Seconds()
|
||||||
connections := w.connManager.GetAllConnectedPeers()
|
connections := w.connManager.GetAllConnectedPeers()
|
||||||
version = system.NetbirdVersion()
|
version = nbversion.NetbirdVersion()
|
||||||
|
|
||||||
for _, account := range w.dataSource.GetAllAccounts() {
|
for _, account := range w.dataSource.GetAllAccounts() {
|
||||||
accounts++
|
accounts++
|
||||||
|
@ -25,12 +25,11 @@ type MockAccountManager struct {
|
|||||||
GetPeerByKeyFunc func(peerKey string) (*server.Peer, error)
|
GetPeerByKeyFunc func(peerKey string) (*server.Peer, error)
|
||||||
GetPeersFunc func(accountID, userID string) ([]*server.Peer, error)
|
GetPeersFunc func(accountID, userID string) ([]*server.Peer, error)
|
||||||
MarkPeerConnectedFunc func(peerKey string, connected bool) error
|
MarkPeerConnectedFunc func(peerKey string, connected bool) error
|
||||||
MarkPeerLoginExpiredFunc func(peerPubKey string, loginExpired bool) error
|
|
||||||
DeletePeerFunc func(accountID, peerKey, userID string) (*server.Peer, error)
|
DeletePeerFunc func(accountID, peerKey, userID string) (*server.Peer, error)
|
||||||
GetPeerByIPFunc func(accountId string, peerIP string) (*server.Peer, error)
|
GetPeerByIPFunc func(accountId string, peerIP string) (*server.Peer, error)
|
||||||
GetNetworkMapFunc func(peerKey string) (*server.NetworkMap, error)
|
GetNetworkMapFunc func(peerKey string) (*server.NetworkMap, error)
|
||||||
GetPeerNetworkFunc func(peerKey string) (*server.Network, error)
|
GetPeerNetworkFunc func(peerKey string) (*server.Network, error)
|
||||||
AddPeerFunc func(setupKey string, userId string, peer *server.Peer) (*server.Peer, error)
|
AddPeerFunc func(setupKey string, userId string, peer *server.Peer) (*server.Peer, *server.NetworkMap, error)
|
||||||
GetGroupFunc func(accountID, groupID string) (*server.Group, error)
|
GetGroupFunc func(accountID, groupID string) (*server.Group, error)
|
||||||
SaveGroupFunc func(accountID, userID string, group *server.Group) error
|
SaveGroupFunc func(accountID, userID string, group *server.Group) error
|
||||||
UpdateGroupFunc func(accountID string, groupID string, operations []server.GroupUpdateOperation) (*server.Group, error)
|
UpdateGroupFunc func(accountID string, groupID string, operations []server.GroupUpdateOperation) (*server.Group, error)
|
||||||
@ -41,9 +40,12 @@ type MockAccountManager struct {
|
|||||||
GroupListPeersFunc func(accountID, groupID string) ([]*server.Peer, error)
|
GroupListPeersFunc func(accountID, groupID string) ([]*server.Peer, error)
|
||||||
GetRuleFunc func(accountID, ruleID, userID string) (*server.Rule, error)
|
GetRuleFunc func(accountID, ruleID, userID string) (*server.Rule, error)
|
||||||
SaveRuleFunc func(accountID, userID string, rule *server.Rule) error
|
SaveRuleFunc func(accountID, userID string, rule *server.Rule) error
|
||||||
UpdateRuleFunc func(accountID string, ruleID string, operations []server.RuleUpdateOperation) (*server.Rule, error)
|
|
||||||
DeleteRuleFunc func(accountID, ruleID, userID string) error
|
DeleteRuleFunc func(accountID, ruleID, userID string) error
|
||||||
ListRulesFunc func(accountID, userID string) ([]*server.Rule, error)
|
ListRulesFunc func(accountID, userID string) ([]*server.Rule, error)
|
||||||
|
GetPolicyFunc func(accountID, policyID, userID string) (*server.Policy, error)
|
||||||
|
SavePolicyFunc func(accountID, userID string, policy *server.Policy) error
|
||||||
|
DeletePolicyFunc func(accountID, policyID, userID string) error
|
||||||
|
ListPoliciesFunc func(accountID, userID string) ([]*server.Policy, error)
|
||||||
GetUsersFromAccountFunc func(accountID, userID string) ([]*server.UserInfo, error)
|
GetUsersFromAccountFunc func(accountID, userID string) ([]*server.UserInfo, error)
|
||||||
UpdatePeerMetaFunc func(peerID string, meta server.PeerSystemMeta) error
|
UpdatePeerMetaFunc func(peerID string, meta server.PeerSystemMeta) error
|
||||||
UpdatePeerSSHKeyFunc func(peerID string, sshKey string) error
|
UpdatePeerSSHKeyFunc func(peerID string, sshKey string) error
|
||||||
@ -70,9 +72,9 @@ type MockAccountManager struct {
|
|||||||
GetDNSSettingsFunc func(accountID, userID string) (*server.DNSSettings, error)
|
GetDNSSettingsFunc func(accountID, userID string) (*server.DNSSettings, error)
|
||||||
SaveDNSSettingsFunc func(accountID, userID string, dnsSettingsToSave *server.DNSSettings) error
|
SaveDNSSettingsFunc func(accountID, userID string, dnsSettingsToSave *server.DNSSettings) error
|
||||||
GetPeerFunc func(accountID, peerID, userID string) (*server.Peer, error)
|
GetPeerFunc func(accountID, peerID, userID string) (*server.Peer, error)
|
||||||
GetAccountByPeerIDFunc func(peerID string) (*server.Account, error)
|
|
||||||
UpdatePeerLastLoginFunc func(peerID string) error
|
|
||||||
UpdateAccountSettingsFunc func(accountID, userID string, newSettings *server.Settings) (*server.Account, error)
|
UpdateAccountSettingsFunc func(accountID, userID string, newSettings *server.Settings) (*server.Account, error)
|
||||||
|
LoginPeerFunc func(login server.PeerLogin) (*server.Peer, *server.NetworkMap, error)
|
||||||
|
SyncPeerFunc func(sync server.PeerSync) (*server.Peer, *server.NetworkMap, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUsersFromAccount mock implementation of GetUsersFromAccount from server.AccountManager interface
|
// GetUsersFromAccount mock implementation of GetUsersFromAccount from server.AccountManager interface
|
||||||
@ -165,14 +167,6 @@ func (am *MockAccountManager) MarkPeerConnected(peerKey string, connected bool)
|
|||||||
return status.Errorf(codes.Unimplemented, "method MarkPeerConnected is not implemented")
|
return status.Errorf(codes.Unimplemented, "method MarkPeerConnected is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkPeerLoginExpired mock implementation of MarkPeerLoginExpired from server.AccountManager interface
|
|
||||||
func (am *MockAccountManager) MarkPeerLoginExpired(peerPubKey string, loginExpired bool) error {
|
|
||||||
if am.MarkPeerLoginExpiredFunc != nil {
|
|
||||||
return am.MarkPeerLoginExpiredFunc(peerPubKey, loginExpired)
|
|
||||||
}
|
|
||||||
return status.Errorf(codes.Unimplemented, "method MarkPeerLoginExpired is not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPeerByIP mock implementation of GetPeerByIP from server.AccountManager interface
|
// GetPeerByIP mock implementation of GetPeerByIP from server.AccountManager interface
|
||||||
func (am *MockAccountManager) GetPeerByIP(accountId string, peerIP string) (*server.Peer, error) {
|
func (am *MockAccountManager) GetPeerByIP(accountId string, peerIP string) (*server.Peer, error) {
|
||||||
if am.GetPeerByIPFunc != nil {
|
if am.GetPeerByIPFunc != nil {
|
||||||
@ -202,11 +196,11 @@ func (am *MockAccountManager) AddPeer(
|
|||||||
setupKey string,
|
setupKey string,
|
||||||
userId string,
|
userId string,
|
||||||
peer *server.Peer,
|
peer *server.Peer,
|
||||||
) (*server.Peer, error) {
|
) (*server.Peer, *server.NetworkMap, error) {
|
||||||
if am.AddPeerFunc != nil {
|
if am.AddPeerFunc != nil {
|
||||||
return am.AddPeerFunc(setupKey, userId, peer)
|
return am.AddPeerFunc(setupKey, userId, peer)
|
||||||
}
|
}
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method AddPeer is not implemented")
|
return nil, nil, status.Errorf(codes.Unimplemented, "method AddPeer is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGroup mock implementation of GetGroup from server.AccountManager interface
|
// GetGroup mock implementation of GetGroup from server.AccountManager interface
|
||||||
@ -289,14 +283,6 @@ func (am *MockAccountManager) SaveRule(accountID, userID string, rule *server.Ru
|
|||||||
return status.Errorf(codes.Unimplemented, "method SaveRule is not implemented")
|
return status.Errorf(codes.Unimplemented, "method SaveRule is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRule mock implementation of UpdateRule from server.AccountManager interface
|
|
||||||
func (am *MockAccountManager) UpdateRule(accountID string, ruleID string, operations []server.RuleUpdateOperation) (*server.Rule, error) {
|
|
||||||
if am.UpdateRuleFunc != nil {
|
|
||||||
return am.UpdateRuleFunc(accountID, ruleID, operations)
|
|
||||||
}
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateRule not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteRule mock implementation of DeleteRule from server.AccountManager interface
|
// DeleteRule mock implementation of DeleteRule from server.AccountManager interface
|
||||||
func (am *MockAccountManager) DeleteRule(accountID, ruleID, userID string) error {
|
func (am *MockAccountManager) DeleteRule(accountID, ruleID, userID string) error {
|
||||||
if am.DeleteRuleFunc != nil {
|
if am.DeleteRuleFunc != nil {
|
||||||
@ -313,6 +299,38 @@ func (am *MockAccountManager) ListRules(accountID, userID string) ([]*server.Rul
|
|||||||
return nil, status.Errorf(codes.Unimplemented, "method ListRules is not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method ListRules is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetPolicy mock implementation of GetPolicy from server.AccountManager interface
|
||||||
|
func (am *MockAccountManager) GetPolicy(accountID, policyID, userID string) (*server.Policy, error) {
|
||||||
|
if am.GetPolicyFunc != nil {
|
||||||
|
return am.GetPolicyFunc(accountID, policyID, userID)
|
||||||
|
}
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetPolicy is not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePolicy mock implementation of SavePolicy from server.AccountManager interface
|
||||||
|
func (am *MockAccountManager) SavePolicy(accountID, userID string, policy *server.Policy) error {
|
||||||
|
if am.SavePolicyFunc != nil {
|
||||||
|
return am.SavePolicyFunc(accountID, userID, policy)
|
||||||
|
}
|
||||||
|
return status.Errorf(codes.Unimplemented, "method SavePolicy is not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePolicy mock implementation of DeletePolicy from server.AccountManager interface
|
||||||
|
func (am *MockAccountManager) DeletePolicy(accountID, policyID, userID string) error {
|
||||||
|
if am.DeletePolicyFunc != nil {
|
||||||
|
return am.DeletePolicyFunc(accountID, policyID, userID)
|
||||||
|
}
|
||||||
|
return status.Errorf(codes.Unimplemented, "method DeletePolicy is not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPolicies mock implementation of ListPolicies from server.AccountManager interface
|
||||||
|
func (am *MockAccountManager) ListPolicies(accountID, userID string) ([]*server.Policy, error) {
|
||||||
|
if am.ListPoliciesFunc != nil {
|
||||||
|
return am.ListPoliciesFunc(accountID, userID)
|
||||||
|
}
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method ListPolicies is not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
// UpdatePeerMeta mock implementation of UpdatePeerMeta from server.AccountManager interface
|
// UpdatePeerMeta mock implementation of UpdatePeerMeta from server.AccountManager interface
|
||||||
func (am *MockAccountManager) UpdatePeerMeta(peerID string, meta server.PeerSystemMeta) error {
|
func (am *MockAccountManager) UpdatePeerMeta(peerID string, meta server.PeerSystemMeta) error {
|
||||||
if am.UpdatePeerMetaFunc != nil {
|
if am.UpdatePeerMetaFunc != nil {
|
||||||
@ -486,7 +504,8 @@ func (am *MockAccountManager) CreateUser(accountID, userID string, invite *serve
|
|||||||
|
|
||||||
// GetAccountFromToken mocks GetAccountFromToken of the AccountManager interface
|
// GetAccountFromToken mocks GetAccountFromToken of the AccountManager interface
|
||||||
func (am *MockAccountManager) GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User,
|
func (am *MockAccountManager) GetAccountFromToken(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User,
|
||||||
error) {
|
error,
|
||||||
|
) {
|
||||||
if am.GetAccountFromTokenFunc != nil {
|
if am.GetAccountFromTokenFunc != nil {
|
||||||
return am.GetAccountFromTokenFunc(claims)
|
return am.GetAccountFromTokenFunc(claims)
|
||||||
}
|
}
|
||||||
@ -541,22 +560,6 @@ func (am *MockAccountManager) GetPeer(accountID, peerID, userID string) (*server
|
|||||||
return nil, status.Errorf(codes.Unimplemented, "method GetPeer is not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method GetPeer is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccountByPeerID mocks GetAccountByPeerID of the AccountManager interface
|
|
||||||
func (am *MockAccountManager) GetAccountByPeerID(peerID string) (*server.Account, error) {
|
|
||||||
if am.GetAccountByPeerIDFunc != nil {
|
|
||||||
return am.GetAccountByPeerIDFunc(peerID)
|
|
||||||
}
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method GetAccountByPeerID is not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePeerLastLogin mocks UpdatePeerLastLogin of the AccountManager interface
|
|
||||||
func (am *MockAccountManager) UpdatePeerLastLogin(peerID string) error {
|
|
||||||
if am.UpdatePeerLastLoginFunc != nil {
|
|
||||||
return am.UpdatePeerLastLoginFunc(peerID)
|
|
||||||
}
|
|
||||||
return status.Errorf(codes.Unimplemented, "method UpdatePeerLastLogin is not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateAccountSettings mocks UpdateAccountSettings of the AccountManager interface
|
// UpdateAccountSettings mocks UpdateAccountSettings of the AccountManager interface
|
||||||
func (am *MockAccountManager) UpdateAccountSettings(accountID, userID string, newSettings *server.Settings) (*server.Account, error) {
|
func (am *MockAccountManager) UpdateAccountSettings(accountID, userID string, newSettings *server.Settings) (*server.Account, error) {
|
||||||
if am.UpdateAccountSettingsFunc != nil {
|
if am.UpdateAccountSettingsFunc != nil {
|
||||||
@ -564,3 +567,19 @@ func (am *MockAccountManager) UpdateAccountSettings(accountID, userID string, ne
|
|||||||
}
|
}
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateAccountSettings is not implemented")
|
return nil, status.Errorf(codes.Unimplemented, "method UpdateAccountSettings is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoginPeer mocks LoginPeer of the AccountManager interface
|
||||||
|
func (am *MockAccountManager) LoginPeer(login server.PeerLogin) (*server.Peer, *server.NetworkMap, error) {
|
||||||
|
if am.LoginPeerFunc != nil {
|
||||||
|
return am.LoginPeerFunc(login)
|
||||||
|
}
|
||||||
|
return nil, nil, status.Errorf(codes.Unimplemented, "method LoginPeer is not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncPeer mocks SyncPeer of the AccountManager interface
|
||||||
|
func (am *MockAccountManager) SyncPeer(sync server.PeerSync) (*server.Peer, *server.NetworkMap, error) {
|
||||||
|
if am.SyncPeerFunc != nil {
|
||||||
|
return am.SyncPeerFunc(sync)
|
||||||
|
}
|
||||||
|
return nil, nil, status.Errorf(codes.Unimplemented, "method SyncPeer is not implemented")
|
||||||
|
}
|
||||||
|
@ -1147,11 +1147,11 @@ func initTestNSAccount(t *testing.T, am *DefaultAccountManager) (*Account, error
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = am.AddPeer("", userID, peer1)
|
_, _, err = am.AddPeer("", userID, peer1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, err = am.AddPeer("", userID, peer2)
|
_, _, err = am.AddPeer("", userID, peer2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -23,10 +23,11 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type NetworkMap struct {
|
type NetworkMap struct {
|
||||||
Peers []*Peer
|
Peers []*Peer
|
||||||
Network *Network
|
Network *Network
|
||||||
Routes []*route.Route
|
Routes []*route.Route
|
||||||
DNSConfig nbdns.Config
|
DNSConfig nbdns.Config
|
||||||
|
OfflinePeers []*Peer
|
||||||
}
|
}
|
||||||
|
|
||||||
type Network struct {
|
type Network struct {
|
||||||
|
@ -2,14 +2,14 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
nbdns "github.com/netbirdio/netbird/dns"
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
|
||||||
"github.com/rs/xid"
|
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
"github.com/rs/xid"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/proto"
|
"github.com/netbirdio/netbird/management/proto"
|
||||||
@ -36,6 +36,26 @@ type PeerStatus struct {
|
|||||||
LoginExpired bool
|
LoginExpired bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PeerSync used as a data object between the gRPC API and AccountManager on Sync request.
|
||||||
|
type PeerSync struct {
|
||||||
|
// WireGuardPubKey is a peers WireGuard public key
|
||||||
|
WireGuardPubKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerLogin used as a data object between the gRPC API and AccountManager on Login request.
|
||||||
|
type PeerLogin struct {
|
||||||
|
// WireGuardPubKey is a peers WireGuard public key
|
||||||
|
WireGuardPubKey string
|
||||||
|
// SSHKey is a peer's ssh key. Can be empty (e.g., old version do not provide it, or this feature is disabled)
|
||||||
|
SSHKey string
|
||||||
|
// Meta is the system information passed by peer, must be always present.
|
||||||
|
Meta PeerSystemMeta
|
||||||
|
// UserID indicates that JWT was used to log in, and it was valid. Can be empty when SetupKey is used or auth is not required.
|
||||||
|
UserID string
|
||||||
|
// SetupKey references to a server.SetupKey to log in. Can be empty when UserID is used or auth is not required.
|
||||||
|
SetupKey string
|
||||||
|
}
|
||||||
|
|
||||||
// Peer represents a machine connected to the network.
|
// Peer represents a machine connected to the network.
|
||||||
// The Peer is a WireGuard peer identified by a public key
|
// The Peer is a WireGuard peer identified by a public key
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
@ -93,6 +113,15 @@ func (p *Peer) Copy() *Peer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateMeta updates peer's system meta data
|
||||||
|
func (p *Peer) UpdateMeta(meta PeerSystemMeta) {
|
||||||
|
// Avoid overwriting UIVersion if the update was triggered sole by the CLI client
|
||||||
|
if meta.UIVersion == "" {
|
||||||
|
meta.UIVersion = p.Meta.UIVersion
|
||||||
|
}
|
||||||
|
p.Meta = meta
|
||||||
|
}
|
||||||
|
|
||||||
// MarkLoginExpired marks peer's status expired or not
|
// MarkLoginExpired marks peer's status expired or not
|
||||||
func (p *Peer) MarkLoginExpired(expired bool) {
|
func (p *Peer) MarkLoginExpired(expired bool) {
|
||||||
newStatus := p.Status.Copy()
|
newStatus := p.Status.Copy()
|
||||||
@ -108,11 +137,15 @@ func (p *Peer) MarkLoginExpired(expired bool) {
|
|||||||
// Return true if a login has expired, false otherwise, and time left to expiration (negative when expired).
|
// Return true if a login has expired, false otherwise, and time left to expiration (negative when expired).
|
||||||
// Login expiration can be disabled/enabled on a Peer level via Peer.LoginExpirationEnabled property.
|
// Login expiration can be disabled/enabled on a Peer level via Peer.LoginExpirationEnabled property.
|
||||||
// Login expiration can also be disabled/enabled globally on the Account level via Settings.PeerLoginExpirationEnabled.
|
// Login expiration can also be disabled/enabled globally on the Account level via Settings.PeerLoginExpirationEnabled.
|
||||||
|
// Only peers added by interactive SSO login can be expired.
|
||||||
func (p *Peer) LoginExpired(expiresIn time.Duration) (bool, time.Duration) {
|
func (p *Peer) LoginExpired(expiresIn time.Duration) (bool, time.Duration) {
|
||||||
|
if !p.AddedWithSSOLogin() || !p.LoginExpirationEnabled {
|
||||||
|
return false, 0
|
||||||
|
}
|
||||||
expiresAt := p.LastLogin.Add(expiresIn)
|
expiresAt := p.LastLogin.Add(expiresIn)
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
timeLeft := expiresAt.Sub(now)
|
timeLeft := expiresAt.Sub(now)
|
||||||
return p.LoginExpirationEnabled && (timeLeft <= 0), timeLeft
|
return timeLeft <= 0, timeLeft
|
||||||
}
|
}
|
||||||
|
|
||||||
// FQDN returns peers FQDN combined of the peer's DNS label and the system's DNS domain
|
// FQDN returns peers FQDN combined of the peer's DNS label and the system's DNS domain
|
||||||
@ -139,7 +172,6 @@ func (p *PeerStatus) Copy() *PeerStatus {
|
|||||||
|
|
||||||
// GetPeerByKey looks up peer by its public WireGuard key
|
// GetPeerByKey looks up peer by its public WireGuard key
|
||||||
func (am *DefaultAccountManager) GetPeerByKey(peerPubKey string) (*Peer, error) {
|
func (am *DefaultAccountManager) GetPeerByKey(peerPubKey string) (*Peer, error) {
|
||||||
|
|
||||||
account, err := am.Store.GetAccountByPeerPubKey(peerPubKey)
|
account, err := am.Store.GetAccountByPeerPubKey(peerPubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -151,7 +183,6 @@ func (am *DefaultAccountManager) GetPeerByKey(peerPubKey string) (*Peer, error)
|
|||||||
// GetPeers returns a list of peers under the given account filtering out peers that do not belong to a user if
|
// GetPeers returns a list of peers under the given account filtering out peers that do not belong to a user if
|
||||||
// the current user is not an admin.
|
// the current user is not an admin.
|
||||||
func (am *DefaultAccountManager) GetPeers(accountID, userID string) ([]*Peer, error) {
|
func (am *DefaultAccountManager) GetPeers(accountID, userID string) ([]*Peer, error) {
|
||||||
|
|
||||||
account, err := am.Store.GetAccount(accountID)
|
account, err := am.Store.GetAccount(accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -176,7 +207,8 @@ func (am *DefaultAccountManager) GetPeers(accountID, userID string) ([]*Peer, er
|
|||||||
|
|
||||||
// fetch all the peers that have access to the user's peers
|
// fetch all the peers that have access to the user's peers
|
||||||
for _, peer := range peers {
|
for _, peer := range peers {
|
||||||
aclPeers := account.getPeersByACL(peer.ID)
|
// TODO: use firewall rules
|
||||||
|
aclPeers, _ := account.getPeersByPolicy(peer.ID)
|
||||||
for _, p := range aclPeers {
|
for _, p := range aclPeers {
|
||||||
peersMap[p.ID] = p
|
peersMap[p.ID] = p
|
||||||
}
|
}
|
||||||
@ -190,40 +222,8 @@ func (am *DefaultAccountManager) GetPeers(accountID, userID string) ([]*Peer, er
|
|||||||
return peers, nil
|
return peers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarkPeerLoginExpired when peer login has expired
|
|
||||||
func (am *DefaultAccountManager) MarkPeerLoginExpired(peerPubKey string, loginExpired bool) error {
|
|
||||||
account, err := am.Store.GetAccountByPeerPubKey(peerPubKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(account.Id)
|
|
||||||
defer unlock()
|
|
||||||
|
|
||||||
// ensure that we consider modification happened meanwhile (because we were outside the account lock when we fetched the account)
|
|
||||||
account, err = am.Store.GetAccount(account.Id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
peer, err := account.FindPeerByPubKey(peerPubKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
peer.MarkLoginExpired(loginExpired)
|
|
||||||
account.UpdatePeer(peer)
|
|
||||||
|
|
||||||
err = am.Store.SavePeerStatus(account.Id, peer.ID, *peer.Status)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkPeerConnected marks peer as connected (true) or disconnected (false)
|
// MarkPeerConnected marks peer as connected (true) or disconnected (false)
|
||||||
func (am *DefaultAccountManager) MarkPeerConnected(peerPubKey string, connected bool) error {
|
func (am *DefaultAccountManager) MarkPeerConnected(peerPubKey string, connected bool) error {
|
||||||
|
|
||||||
account, err := am.Store.GetAccountByPeerPubKey(peerPubKey)
|
account, err := am.Store.GetAccountByPeerPubKey(peerPubKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -265,7 +265,7 @@ func (am *DefaultAccountManager) MarkPeerConnected(peerPubKey string, connected
|
|||||||
|
|
||||||
if oldStatus.LoginExpired {
|
if oldStatus.LoginExpired {
|
||||||
// we need to update other peers because when peer login expires all other peers are notified to disconnect from
|
// we need to update other peers because when peer login expires all other peers are notified to disconnect from
|
||||||
//the expired one. Here we notify them that connection is now allowed again.
|
// the expired one. Here we notify them that connection is now allowed again.
|
||||||
err = am.updateAccountPeers(account)
|
err = am.updateAccountPeers(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -277,7 +277,6 @@ func (am *DefaultAccountManager) MarkPeerConnected(peerPubKey string, connected
|
|||||||
|
|
||||||
// UpdatePeer updates peer. Only Peer.Name, Peer.SSHEnabled, and Peer.LoginExpirationEnabled can be updated.
|
// UpdatePeer updates peer. Only Peer.Name, Peer.SSHEnabled, and Peer.LoginExpirationEnabled can be updated.
|
||||||
func (am *DefaultAccountManager) UpdatePeer(accountID, userID string, update *Peer) (*Peer, error) {
|
func (am *DefaultAccountManager) UpdatePeer(accountID, userID string, update *Peer) (*Peer, error) {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -351,7 +350,6 @@ func (am *DefaultAccountManager) UpdatePeer(accountID, userID string, update *Pe
|
|||||||
|
|
||||||
// DeletePeer removes peer from the account by its IP
|
// DeletePeer removes peer from the account by its IP
|
||||||
func (am *DefaultAccountManager) DeletePeer(accountID, peerID, userID string) (*Peer, error) {
|
func (am *DefaultAccountManager) DeletePeer(accountID, peerID, userID string) (*Peer, error) {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -401,7 +399,6 @@ func (am *DefaultAccountManager) DeletePeer(accountID, peerID, userID string) (*
|
|||||||
|
|
||||||
// GetPeerByIP returns peer by its IP
|
// GetPeerByIP returns peer by its IP
|
||||||
func (am *DefaultAccountManager) GetPeerByIP(accountID string, peerIP string) (*Peer, error) {
|
func (am *DefaultAccountManager) GetPeerByIP(accountID string, peerIP string) (*Peer, error) {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -421,7 +418,6 @@ func (am *DefaultAccountManager) GetPeerByIP(accountID string, peerIP string) (*
|
|||||||
|
|
||||||
// GetNetworkMap returns Network map for a given peer (omits original peer from the Peers result)
|
// GetNetworkMap returns Network map for a given peer (omits original peer from the Peers result)
|
||||||
func (am *DefaultAccountManager) GetNetworkMap(peerID string) (*NetworkMap, error) {
|
func (am *DefaultAccountManager) GetNetworkMap(peerID string) (*NetworkMap, error) {
|
||||||
|
|
||||||
account, err := am.Store.GetAccountByPeerID(peerID)
|
account, err := am.Store.GetAccountByPeerID(peerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -431,37 +427,11 @@ func (am *DefaultAccountManager) GetNetworkMap(peerID string) (*NetworkMap, erro
|
|||||||
if peer == nil {
|
if peer == nil {
|
||||||
return nil, status.Errorf(status.NotFound, "peer with ID %s not found", peerID)
|
return nil, status.Errorf(status.NotFound, "peer with ID %s not found", peerID)
|
||||||
}
|
}
|
||||||
|
return account.GetPeerNetworkMap(peer.ID, am.dnsDomain), nil
|
||||||
aclPeers := account.getPeersByACL(peerID)
|
|
||||||
// Please mind, that the returned route.Route objects will contain Peer.Key instead of Peer.ID.
|
|
||||||
routesUpdate := account.getRoutesToSync(peerID, aclPeers)
|
|
||||||
|
|
||||||
dnsManagementStatus := account.getPeerDNSManagementStatus(peerID)
|
|
||||||
dnsUpdate := nbdns.Config{
|
|
||||||
ServiceEnable: dnsManagementStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
if dnsManagementStatus {
|
|
||||||
var zones []nbdns.CustomZone
|
|
||||||
peersCustomZone := getPeersCustomZone(account, am.dnsDomain)
|
|
||||||
if peersCustomZone.Domain != "" {
|
|
||||||
zones = append(zones, peersCustomZone)
|
|
||||||
}
|
|
||||||
dnsUpdate.CustomZones = zones
|
|
||||||
dnsUpdate.NameServerGroups = getPeerNSGroups(account, peerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &NetworkMap{
|
|
||||||
Peers: aclPeers,
|
|
||||||
Network: account.Network.Copy(),
|
|
||||||
Routes: routesUpdate,
|
|
||||||
DNSConfig: dnsUpdate,
|
|
||||||
}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPeerNetwork returns the Network for a given peer
|
// GetPeerNetwork returns the Network for a given peer
|
||||||
func (am *DefaultAccountManager) GetPeerNetwork(peerID string) (*Network, error) {
|
func (am *DefaultAccountManager) GetPeerNetwork(peerID string) (*Network, error) {
|
||||||
|
|
||||||
account, err := am.Store.GetAccountByPeerID(peerID)
|
account, err := am.Store.GetAccountByPeerID(peerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -471,13 +441,17 @@ func (am *DefaultAccountManager) GetPeerNetwork(peerID string) (*Network, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddPeer adds a new peer to the Store.
|
// AddPeer adds a new peer to the Store.
|
||||||
// Each Account has a list of pre-authorised SetupKey and if no Account has a given key err with a code codes.Unauthenticated
|
// Each Account has a list of pre-authorized SetupKey and if no Account has a given key err with a code status.PermissionDenied
|
||||||
// will be returned, meaning the key is invalid
|
// will be returned, meaning the setup key is invalid or not found.
|
||||||
// If a User ID is provided, it means that we passed the authentication using JWT, then we look for account by User ID and register the peer
|
// If a User ID is provided, it means that we passed the authentication using JWT, then we look for account by User ID and register the peer
|
||||||
// to it. We also add the User ID to the peer metadata to identify registrant.
|
// to it. We also add the User ID to the peer metadata to identify registrant. If no userID provided, then fail with status.PermissionDenied
|
||||||
// Each new Peer will be assigned a new next net.IP from the Account.Network and Account.Network.LastIP will be updated (IP's are not reused).
|
// Each new Peer will be assigned a new next net.IP from the Account.Network and Account.Network.LastIP will be updated (IP's are not reused).
|
||||||
// The peer property is just a placeholder for the Peer properties to pass further
|
// The peer property is just a placeholder for the Peer properties to pass further
|
||||||
func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*Peer, error) {
|
func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*Peer, *NetworkMap, error) {
|
||||||
|
if setupKey == "" && userID == "" {
|
||||||
|
// no auth method provided => reject access
|
||||||
|
return nil, nil, status.Errorf(status.Unauthenticated, "no peer auth method provided, please use a setup key or interactive SSO login")
|
||||||
|
}
|
||||||
|
|
||||||
upperKey := strings.ToUpper(setupKey)
|
upperKey := strings.ToUpper(setupKey)
|
||||||
var account *Account
|
var account *Account
|
||||||
@ -490,7 +464,7 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
|
|||||||
account, err = am.Store.GetAccountBySetupKey(setupKey)
|
account, err = am.Store.GetAccountBySetupKey(setupKey)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(status.NotFound, "failed adding new peer: account not found")
|
return nil, nil, status.Errorf(status.NotFound, "failed adding new peer: account not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(account.Id)
|
unlock := am.Store.AcquireAccountLock(account.Id)
|
||||||
@ -499,7 +473,7 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
|
|||||||
// ensure that we consider modification happened meanwhile (because we were outside the account lock when we fetched the account)
|
// ensure that we consider modification happened meanwhile (because we were outside the account lock when we fetched the account)
|
||||||
account, err = am.Store.GetAccount(account.Id)
|
account, err = am.Store.GetAccount(account.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
opEvent := &activity.Event{
|
opEvent := &activity.Event{
|
||||||
@ -511,11 +485,11 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
|
|||||||
// validate the setup key if adding with a key
|
// validate the setup key if adding with a key
|
||||||
sk, err := account.FindSetupKey(upperKey)
|
sk, err := account.FindSetupKey(upperKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sk.IsValid() {
|
if !sk.IsValid() {
|
||||||
return nil, status.Errorf(status.PreconditionFailed, "couldn't add peer: setup key is invalid")
|
return nil, nil, status.Errorf(status.PreconditionFailed, "couldn't add peer: setup key is invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
account.SetupKeys[sk.Key] = sk.IncrementUsage()
|
account.SetupKeys[sk.Key] = sk.IncrementUsage()
|
||||||
@ -529,16 +503,16 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
|
|||||||
takenIps := account.getTakenIPs()
|
takenIps := account.getTakenIPs()
|
||||||
existingLabels := account.getPeerDNSLabels()
|
existingLabels := account.getPeerDNSLabels()
|
||||||
|
|
||||||
newLabel, err := getPeerHostLabel(peer.Name, existingLabels)
|
newLabel, err := getPeerHostLabel(peer.Meta.Hostname, existingLabels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.DNSLabel = newLabel
|
peer.DNSLabel = newLabel
|
||||||
network := account.Network
|
network := account.Network
|
||||||
nextIp, err := AllocatePeerIP(network.Net, takenIps)
|
nextIp, err := AllocatePeerIP(network.Net, takenIps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newPeer := &Peer{
|
newPeer := &Peer{
|
||||||
@ -547,7 +521,7 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
|
|||||||
SetupKey: upperKey,
|
SetupKey: upperKey,
|
||||||
IP: nextIp,
|
IP: nextIp,
|
||||||
Meta: peer.Meta,
|
Meta: peer.Meta,
|
||||||
Name: peer.Name,
|
Name: peer.Meta.Hostname,
|
||||||
DNSLabel: newLabel,
|
DNSLabel: newLabel,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
Status: &PeerStatus{Connected: false, LastSeen: time.Now()},
|
Status: &PeerStatus{Connected: false, LastSeen: time.Now()},
|
||||||
@ -560,7 +534,7 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
|
|||||||
// add peer to 'All' group
|
// add peer to 'All' group
|
||||||
group, err := account.GetGroupAll()
|
group, err := account.GetGroupAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
group.Peers = append(group.Peers, newPeer.ID)
|
group.Peers = append(group.Peers, newPeer.ID)
|
||||||
|
|
||||||
@ -568,12 +542,12 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
|
|||||||
if addedByUser {
|
if addedByUser {
|
||||||
groupsToAdd, err = account.getUserGroups(userID)
|
groupsToAdd, err = account.getUserGroups(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
groupsToAdd, err = account.getSetupKeyGroups(upperKey)
|
groupsToAdd, err = account.getSetupKeyGroups(upperKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,55 +563,184 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (*
|
|||||||
account.Network.IncSerial()
|
account.Network.IncSerial()
|
||||||
err = am.Store.SaveAccount(account)
|
err = am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
opEvent.TargetID = newPeer.ID
|
opEvent.TargetID = newPeer.ID
|
||||||
opEvent.Meta = newPeer.EventMeta(am.GetDNSDomain())
|
opEvent.Meta = newPeer.EventMeta(am.GetDNSDomain())
|
||||||
am.storeEvent(opEvent.InitiatorID, opEvent.TargetID, opEvent.AccountID, opEvent.Activity, opEvent.Meta)
|
am.storeEvent(opEvent.InitiatorID, opEvent.TargetID, opEvent.AccountID, opEvent.Activity, opEvent.Meta)
|
||||||
|
|
||||||
return newPeer, nil
|
err = am.updateAccountPeers(account)
|
||||||
}
|
|
||||||
|
|
||||||
// UpdatePeerLastLogin sets Peer.LastLogin to the current timestamp.
|
|
||||||
func (am *DefaultAccountManager) UpdatePeerLastLogin(peerID string) error {
|
|
||||||
account, err := am.Store.GetAccountByPeerID(peerID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
networkMap := account.GetPeerNetworkMap(peer.ID, am.dnsDomain)
|
||||||
|
return newPeer, networkMap, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SyncPeer checks whether peer is eligible for receiving NetworkMap (authenticated) and returns its NetworkMap if eligible
|
||||||
|
func (am *DefaultAccountManager) SyncPeer(sync PeerSync) (*Peer, *NetworkMap, error) {
|
||||||
|
account, err := am.Store.GetAccountByPeerPubKey(sync.WireGuardPubKey)
|
||||||
|
if err != nil {
|
||||||
|
if errStatus, ok := status.FromError(err); ok && errStatus.Type() == status.NotFound {
|
||||||
|
return nil, nil, status.Errorf(status.Unauthenticated, "peer is not registered")
|
||||||
|
}
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// we found the peer, and we follow a normal login flow
|
||||||
unlock := am.Store.AcquireAccountLock(account.Id)
|
unlock := am.Store.AcquireAccountLock(account.Id)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
// ensure that we consider modification happened meanwhile (because we were outside the account lock when we fetched the account)
|
// fetch the account from the store once more after acquiring lock to avoid concurrent updates inconsistencies
|
||||||
account, err = am.Store.GetAccount(account.Id)
|
account, err = am.Store.GetAccount(account.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
peer := account.GetPeer(peerID)
|
peer, err := account.FindPeerByPubKey(sync.WireGuardPubKey)
|
||||||
if peer == nil {
|
if err != nil {
|
||||||
return status.Errorf(status.NotFound, "peer with ID %s not found", peerID)
|
return nil, nil, status.Errorf(status.Unauthenticated, "peer is not registered")
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.LastLogin = time.Now()
|
if peerLoginExpired(peer, account) {
|
||||||
newStatus := peer.Status.Copy()
|
return nil, nil, status.Errorf(status.PermissionDenied, "peer login has expired, please log in once more")
|
||||||
newStatus.LoginExpired = false
|
}
|
||||||
peer.Status = newStatus
|
return peer, account.GetPeerNetworkMap(peer.ID, am.dnsDomain), nil
|
||||||
|
}
|
||||||
|
|
||||||
account.UpdatePeer(peer)
|
// LoginPeer logs in or registers a peer.
|
||||||
|
// If peer doesn't exist the function checks whether a setup key or a user is present and registers a new peer if so.
|
||||||
|
func (am *DefaultAccountManager) LoginPeer(login PeerLogin) (*Peer, *NetworkMap, error) {
|
||||||
|
account, err := am.Store.GetAccountByPeerPubKey(login.WireGuardPubKey)
|
||||||
|
if err != nil {
|
||||||
|
if errStatus, ok := status.FromError(err); ok && errStatus.Type() == status.NotFound {
|
||||||
|
// we couldn't find this peer by its public key which can mean that peer hasn't been registered yet.
|
||||||
|
// Try registering it.
|
||||||
|
return am.AddPeer(login.SetupKey, login.UserID, &Peer{
|
||||||
|
Key: login.WireGuardPubKey,
|
||||||
|
Meta: login.Meta,
|
||||||
|
SSHKey: login.SSHKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
log.Errorf("failed while logging in peer %s: %v", login.WireGuardPubKey, err)
|
||||||
|
return nil, nil, status.Errorf(status.Internal, "failed while logging in peer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// we found the peer, and we follow a normal login flow
|
||||||
|
unlock := am.Store.AcquireAccountLock(account.Id)
|
||||||
|
defer unlock()
|
||||||
|
|
||||||
|
// fetch the account from the store once more after acquiring lock to avoid concurrent updates inconsistencies
|
||||||
|
account, err = am.Store.GetAccount(account.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
peer, err := account.FindPeerByPubKey(login.WireGuardPubKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, status.Errorf(status.Unauthenticated, "peer is not registered")
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRemotePeers := false
|
||||||
|
if peerLoginExpired(peer, account) {
|
||||||
|
err = checkAuth(login.UserID, peer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// If peer was expired before and if it reached this point, it is re-authenticated.
|
||||||
|
// UserID is present, meaning that JWT validation passed successfully in the API layer.
|
||||||
|
updatePeerLastLogin(peer, account)
|
||||||
|
updateRemotePeers = true
|
||||||
|
}
|
||||||
|
|
||||||
|
peer = updatePeerMeta(peer, login.Meta, account)
|
||||||
|
|
||||||
|
peer, err = am.checkAndUpdatePeerSSHKey(peer, account, login.SSHKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
err = am.Store.SaveAccount(account)
|
err = am.Store.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if updateRemotePeers {
|
||||||
|
err = am.updateAccountPeers(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peer, account.GetPeerNetworkMap(peer.ID, am.dnsDomain), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAuth(loginUserID string, peer *Peer) error {
|
||||||
|
if loginUserID == "" {
|
||||||
|
// absence of a user ID indicates that JWT wasn't provided.
|
||||||
|
return status.Errorf(status.PermissionDenied, "peer login has expired, please log in once more")
|
||||||
|
}
|
||||||
|
if peer.UserID != loginUserID {
|
||||||
|
log.Warnf("user mismatch when loggin in peer %s: peer user %s, login user %s ", peer.ID, peer.UserID, loginUserID)
|
||||||
|
return status.Errorf(status.Unauthenticated, "can't login")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func peerLoginExpired(peer *Peer, account *Account) bool {
|
||||||
|
expired, expiresIn := peer.LoginExpired(account.Settings.PeerLoginExpiration)
|
||||||
|
expired = account.Settings.PeerLoginExpirationEnabled && expired
|
||||||
|
if expired || peer.Status.LoginExpired {
|
||||||
|
log.Debugf("peer's %s login expired %v ago", peer.ID, expiresIn)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePeerLastLogin(peer *Peer, account *Account) {
|
||||||
|
peer.UpdateLastLogin()
|
||||||
|
account.UpdatePeer(peer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateLastLogin and set login expired false
|
||||||
|
func (p *Peer) UpdateLastLogin() *Peer {
|
||||||
|
p.LastLogin = time.Now()
|
||||||
|
newStatus := p.Status.Copy()
|
||||||
|
newStatus.LoginExpired = false
|
||||||
|
p.Status = newStatus
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *DefaultAccountManager) checkAndUpdatePeerSSHKey(peer *Peer, account *Account, newSSHKey string) (*Peer, error) {
|
||||||
|
if len(newSSHKey) == 0 {
|
||||||
|
log.Debugf("no new SSH key provided for peer %s, skipping update", peer.ID)
|
||||||
|
return peer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if peer.SSHKey == newSSHKey {
|
||||||
|
log.Debugf("same SSH key provided for peer %s, skipping update", peer.ID)
|
||||||
|
return peer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
peer.SSHKey = newSSHKey
|
||||||
|
account.UpdatePeer(peer)
|
||||||
|
|
||||||
|
err := am.Store.SaveAccount(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// trigger network map update
|
||||||
|
err = am.updateAccountPeers(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return peer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePeerSSHKey updates peer's public SSH key
|
// UpdatePeerSSHKey updates peer's public SSH key
|
||||||
func (am *DefaultAccountManager) UpdatePeerSSHKey(peerID string, sshKey string) error {
|
func (am *DefaultAccountManager) UpdatePeerSSHKey(peerID string, sshKey string) error {
|
||||||
|
|
||||||
if sshKey == "" {
|
if sshKey == "" {
|
||||||
log.Debugf("empty SSH key provided for peer %s, skipping update", peerID)
|
log.Debugf("empty SSH key provided for peer %s, skipping update", peerID)
|
||||||
return nil
|
return nil
|
||||||
@ -681,7 +784,6 @@ func (am *DefaultAccountManager) UpdatePeerSSHKey(peerID string, sshKey string)
|
|||||||
|
|
||||||
// GetPeer for a given accountID, peerID and userID error if not found.
|
// GetPeer for a given accountID, peerID and userID error if not found.
|
||||||
func (am *DefaultAccountManager) GetPeer(accountID, peerID, userID string) (*Peer, error) {
|
func (am *DefaultAccountManager) GetPeer(accountID, peerID, userID string) (*Peer, error) {
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
defer unlock()
|
defer unlock()
|
||||||
|
|
||||||
@ -713,7 +815,7 @@ func (am *DefaultAccountManager) GetPeer(accountID, peerID, userID string) (*Pee
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range userPeers {
|
for _, p := range userPeers {
|
||||||
aclPeers := account.getPeersByACL(p.ID)
|
aclPeers, _ := account.getPeersByPolicy(p.ID)
|
||||||
for _, aclPeer := range aclPeers {
|
for _, aclPeer := range aclPeers {
|
||||||
if aclPeer.ID == peerID {
|
if aclPeer.ID == peerID {
|
||||||
return peer, nil
|
return peer, nil
|
||||||
@ -724,95 +826,10 @@ func (am *DefaultAccountManager) GetPeer(accountID, peerID, userID string) (*Pee
|
|||||||
return nil, status.Errorf(status.Internal, "user %s has no access to peer %s under account %s", userID, peerID, accountID)
|
return nil, status.Errorf(status.Internal, "user %s has no access to peer %s under account %s", userID, peerID, accountID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdatePeerMeta updates peer's system metadata
|
func updatePeerMeta(peer *Peer, meta PeerSystemMeta, account *Account) *Peer {
|
||||||
func (am *DefaultAccountManager) UpdatePeerMeta(peerID string, meta PeerSystemMeta) error {
|
peer.UpdateMeta(meta)
|
||||||
|
|
||||||
account, err := am.Store.GetAccountByPeerID(peerID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
unlock := am.Store.AcquireAccountLock(account.Id)
|
|
||||||
defer unlock()
|
|
||||||
|
|
||||||
peer := account.GetPeer(peerID)
|
|
||||||
if peer == nil {
|
|
||||||
return status.Errorf(status.NotFound, "peer with ID %s not found", peerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid overwriting UIVersion if the update was triggered sole by the CLI client
|
|
||||||
if meta.UIVersion == "" {
|
|
||||||
meta.UIVersion = peer.Meta.UIVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
peer.Meta = meta
|
|
||||||
account.UpdatePeer(peer)
|
account.UpdatePeer(peer)
|
||||||
|
return peer
|
||||||
err = am.Store.SaveAccount(account)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPeersByACL returns all peers that given peer has access to.
|
|
||||||
func (a *Account) getPeersByACL(peerID string) []*Peer {
|
|
||||||
var peers []*Peer
|
|
||||||
srcRules, dstRules := a.GetPeerRules(peerID)
|
|
||||||
|
|
||||||
groups := map[string]*Group{}
|
|
||||||
for _, r := range srcRules {
|
|
||||||
if r.Disabled {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if r.Flow == TrafficFlowBidirect {
|
|
||||||
for _, gid := range r.Destination {
|
|
||||||
if group, ok := a.Groups[gid]; ok {
|
|
||||||
groups[gid] = group
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, r := range dstRules {
|
|
||||||
if r.Disabled {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if r.Flow == TrafficFlowBidirect {
|
|
||||||
for _, gid := range r.Source {
|
|
||||||
if group, ok := a.Groups[gid]; ok {
|
|
||||||
groups[gid] = group
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
peersSet := make(map[string]struct{})
|
|
||||||
for _, g := range groups {
|
|
||||||
for _, pid := range g.Peers {
|
|
||||||
peer, ok := a.Peers[pid]
|
|
||||||
if !ok {
|
|
||||||
log.Warnf(
|
|
||||||
"peer %s found in group %s but doesn't belong to account %s",
|
|
||||||
pid,
|
|
||||||
g.ID,
|
|
||||||
a.Id,
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
expired, _ := peer.LoginExpired(a.Settings.PeerLoginExpiration)
|
|
||||||
if expired {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// exclude original peer
|
|
||||||
if _, ok := peersSet[peer.ID]; peer.ID != peerID && !ok {
|
|
||||||
peersSet[peer.ID] = struct{}{}
|
|
||||||
peers = append(peers, peer.Copy())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return peers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateAccountPeers updates all peers that belong to an account.
|
// updateAccountPeers updates all peers that belong to an account.
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPeer_LoginExpired(t *testing.T) {
|
func TestPeer_LoginExpired(t *testing.T) {
|
||||||
|
|
||||||
tt := []struct {
|
tt := []struct {
|
||||||
name string
|
name string
|
||||||
expirationEnabled bool
|
expirationEnabled bool
|
||||||
@ -55,6 +55,7 @@ func TestPeer_LoginExpired(t *testing.T) {
|
|||||||
peer := &Peer{
|
peer := &Peer{
|
||||||
LoginExpirationEnabled: c.expirationEnabled,
|
LoginExpirationEnabled: c.expirationEnabled,
|
||||||
LastLogin: c.lastLogin,
|
LastLogin: c.lastLogin,
|
||||||
|
UserID: userID,
|
||||||
}
|
}
|
||||||
|
|
||||||
expired, _ := peer.LoginExpired(c.accountSettings.PeerLoginExpiration)
|
expired, _ := peer.LoginExpired(c.accountSettings.PeerLoginExpiration)
|
||||||
@ -90,12 +91,10 @@ func TestAccountManager_GetNetworkMap(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
peer1, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
peer1, _, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
||||||
Key: peerKey1.PublicKey().String(),
|
Key: peerKey1.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer-1"},
|
||||||
Name: "test-peer-2",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting peer to be added, got failure %v", err)
|
t.Errorf("expecting peer to be added, got failure %v", err)
|
||||||
return
|
return
|
||||||
@ -106,10 +105,9 @@ func TestAccountManager_GetNetworkMap(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = manager.AddPeer(setupKey.Key, "", &Peer{
|
_, _, err = manager.AddPeer(setupKey.Key, "", &Peer{
|
||||||
Key: peerKey2.PublicKey().String(),
|
Key: peerKey2.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer-2"},
|
||||||
Name: "test-peer-2",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -125,6 +123,7 @@ func TestAccountManager_GetNetworkMap(t *testing.T) {
|
|||||||
|
|
||||||
if len(networkMap.Peers) != 1 {
|
if len(networkMap.Peers) != 1 {
|
||||||
t.Errorf("expecting Account NetworkMap to have 1 peers, got %v", len(networkMap.Peers))
|
t.Errorf("expecting Account NetworkMap to have 1 peers, got %v", len(networkMap.Peers))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if networkMap.Peers[0].Key != peerKey2.PublicKey().String() {
|
if networkMap.Peers[0].Key != peerKey2.PublicKey().String() {
|
||||||
@ -136,7 +135,7 @@ func TestAccountManager_GetNetworkMap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccountManager_GetNetworkMapWithRule(t *testing.T) {
|
func TestAccountManager_GetNetworkMapWithPolicy(t *testing.T) {
|
||||||
manager, err := createManager(t)
|
manager, err := createManager(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -163,12 +162,10 @@ func TestAccountManager_GetNetworkMapWithRule(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
peer1, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
peer1, _, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
||||||
Key: peerKey1.PublicKey().String(),
|
Key: peerKey1.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer-1"},
|
||||||
Name: "test-peer-2",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting peer to be added, got failure %v", err)
|
t.Errorf("expecting peer to be added, got failure %v", err)
|
||||||
return
|
return
|
||||||
@ -179,24 +176,22 @@ func TestAccountManager_GetNetworkMapWithRule(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
peer2, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
peer2, _, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
||||||
Key: peerKey2.PublicKey().String(),
|
Key: peerKey2.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer-2"},
|
||||||
Name: "test-peer-2",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting peer to be added, got failure %v", err)
|
t.Errorf("expecting peer to be added, got failure %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rules, err := manager.ListRules(account.Id, userID)
|
policies, err := manager.ListPolicies(account.Id, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting to get a list of rules, got failure %v", err)
|
t.Errorf("expecting to get a list of rules, got failure %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = manager.DeleteRule(account.Id, rules[0].ID, userID)
|
err = manager.DeletePolicy(account.Id, policies[0].ID, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting to delete 1 group, got failure %v", err)
|
t.Errorf("expecting to delete 1 group, got failure %v", err)
|
||||||
return
|
return
|
||||||
@ -204,14 +199,14 @@ func TestAccountManager_GetNetworkMapWithRule(t *testing.T) {
|
|||||||
var (
|
var (
|
||||||
group1 Group
|
group1 Group
|
||||||
group2 Group
|
group2 Group
|
||||||
rule Rule
|
policy Policy
|
||||||
)
|
)
|
||||||
|
|
||||||
group1.ID = xid.New().String()
|
group1.ID = xid.New().String()
|
||||||
group2.ID = xid.New().String()
|
group2.ID = xid.New().String()
|
||||||
group1.Name = "src"
|
group1.Name = "src"
|
||||||
group2.Name = "dst"
|
group2.Name = "dst"
|
||||||
rule.ID = xid.New().String()
|
policy.ID = xid.New().String()
|
||||||
group1.Peers = append(group1.Peers, peer1.ID)
|
group1.Peers = append(group1.Peers, peer1.ID)
|
||||||
group2.Peers = append(group2.Peers, peer2.ID)
|
group2.Peers = append(group2.Peers, peer2.ID)
|
||||||
|
|
||||||
@ -226,11 +221,21 @@ func TestAccountManager_GetNetworkMapWithRule(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rule.Name = "test"
|
policy.Name = "test"
|
||||||
rule.Source = append(rule.Source, group1.ID)
|
policy.Enabled = true
|
||||||
rule.Destination = append(rule.Destination, group2.ID)
|
policy.Rules = []*PolicyRule{
|
||||||
rule.Flow = TrafficFlowBidirect
|
{
|
||||||
err = manager.SaveRule(account.Id, userID, &rule)
|
Enabled: true,
|
||||||
|
Sources: []string{group1.ID},
|
||||||
|
Destinations: []string{group2.ID},
|
||||||
|
Action: PolicyTrafficActionAccept,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if err := policy.UpdateQueryFromRules(); err != nil {
|
||||||
|
t.Errorf("expecting policy to be updated, got failure %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = manager.SavePolicy(account.Id, userID, &policy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting rule to be added, got failure %v", err)
|
t.Errorf("expecting rule to be added, got failure %v", err)
|
||||||
return
|
return
|
||||||
@ -277,8 +282,8 @@ func TestAccountManager_GetNetworkMapWithRule(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
rule.Disabled = true
|
policy.Enabled = false
|
||||||
err = manager.SaveRule(account.Id, userID, &rule)
|
err = manager.SavePolicy(account.Id, userID, &policy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting rule to be added, got failure %v", err)
|
t.Errorf("expecting rule to be added, got failure %v", err)
|
||||||
return
|
return
|
||||||
@ -332,12 +337,10 @@ func TestAccountManager_GetPeerNetwork(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
peer1, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
peer1, _, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
||||||
Key: peerKey1.PublicKey().String(),
|
Key: peerKey1.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer-1"},
|
||||||
Name: "test-peer-2",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting peer to be added, got failure %v", err)
|
t.Errorf("expecting peer to be added, got failure %v", err)
|
||||||
return
|
return
|
||||||
@ -348,10 +351,9 @@ func TestAccountManager_GetPeerNetwork(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = manager.AddPeer(setupKey.Key, "", &Peer{
|
_, _, err = manager.AddPeer(setupKey.Key, "", &Peer{
|
||||||
Key: peerKey2.PublicKey().String(),
|
Key: peerKey2.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer-2"},
|
||||||
Name: "test-peer-2",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -368,7 +370,6 @@ func TestAccountManager_GetPeerNetwork(t *testing.T) {
|
|||||||
if account.Network.Id != network.Id {
|
if account.Network.Id != network.Id {
|
||||||
t.Errorf("expecting Account Networks ID to be equal, got %s expected %s", network.Id, account.Network.Id)
|
t.Errorf("expecting Account Networks ID to be equal, got %s expected %s", network.Id, account.Network.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultAccountManager_GetPeer(t *testing.T) {
|
func TestDefaultAccountManager_GetPeer(t *testing.T) {
|
||||||
@ -401,12 +402,10 @@ func TestDefaultAccountManager_GetPeer(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
peer1, err := manager.AddPeer("", someUser, &Peer{
|
peer1, _, err := manager.AddPeer("", someUser, &Peer{
|
||||||
Key: peerKey1.PublicKey().String(),
|
Key: peerKey1.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer-2"},
|
||||||
Name: "test-peer-2",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("expecting peer to be added, got failure %v", err)
|
t.Errorf("expecting peer to be added, got failure %v", err)
|
||||||
return
|
return
|
||||||
@ -419,12 +418,10 @@ func TestDefaultAccountManager_GetPeer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the second peer added with a setup key
|
// the second peer added with a setup key
|
||||||
peer2, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
peer2, _, err := manager.AddPeer(setupKey.Key, "", &Peer{
|
||||||
Key: peerKey2.PublicKey().String(),
|
Key: peerKey2.PublicKey().String(),
|
||||||
Meta: PeerSystemMeta{},
|
Meta: PeerSystemMeta{Hostname: "test-peer-2"},
|
||||||
Name: "test-peer-2",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
@ -446,9 +443,9 @@ func TestDefaultAccountManager_GetPeer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assert.NotNil(t, peer)
|
assert.NotNil(t, peer)
|
||||||
|
|
||||||
// delete the all-to-all rule so that user's peer1 has no access to peer2
|
// delete the all-to-all policy so that user's peer1 has no access to peer2
|
||||||
for _, rule := range account.Rules {
|
for _, policy := range account.Policies {
|
||||||
err = manager.DeleteRule(accountID, rule.ID, adminUser)
|
err = manager.DeletePolicy(accountID, policy.ID, adminUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
@ -473,11 +470,9 @@ func TestDefaultAccountManager_GetPeer(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
assert.NotNil(t, peer)
|
assert.NotNil(t, peer)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSetupKey(account *Account, keyType SetupKeyType) *SetupKey {
|
func getSetupKey(account *Account, keyType SetupKeyType) *SetupKey {
|
||||||
|
|
||||||
var setupKey *SetupKey
|
var setupKey *SetupKey
|
||||||
for _, key := range account.SetupKeys {
|
for _, key := range account.SetupKeys {
|
||||||
if key.Type == keyType {
|
if key.Type == keyType {
|
||||||
|
451
management/server/policy.go
Normal file
451
management/server/policy.go
Normal file
@ -0,0 +1,451 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
|
"github.com/netbirdio/netbird/management/server/status"
|
||||||
|
|
||||||
|
"github.com/open-policy-agent/opa/rego"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PolicyUpdateOperationType operation type
|
||||||
|
type PolicyUpdateOperationType int
|
||||||
|
|
||||||
|
// PolicyTrafficActionType action type for the firewall
|
||||||
|
type PolicyTrafficActionType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// PolicyTrafficActionAccept indicates that the traffic is accepted
|
||||||
|
PolicyTrafficActionAccept = PolicyTrafficActionType("accept")
|
||||||
|
// PolicyTrafficActionDrop indicates that the traffic is dropped
|
||||||
|
PolicyTrafficActionDrop = PolicyTrafficActionType("drop")
|
||||||
|
)
|
||||||
|
|
||||||
|
// PolicyUpdateOperation operation object with type and values to be applied
|
||||||
|
type PolicyUpdateOperation struct {
|
||||||
|
Type PolicyUpdateOperationType
|
||||||
|
Values []string
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed rego/default_policy_module.rego
|
||||||
|
var defaultPolicyModule string
|
||||||
|
|
||||||
|
//go:embed rego/default_policy.rego
|
||||||
|
var defaultPolicyText string
|
||||||
|
|
||||||
|
// defaultPolicyTemplate is a template for the default policy
|
||||||
|
var defaultPolicyTemplate = template.Must(template.New("policy").Parse(defaultPolicyText))
|
||||||
|
|
||||||
|
// PolicyRule is the metadata of the policy
|
||||||
|
type PolicyRule struct {
|
||||||
|
// ID of the policy rule
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Name of the rule visible in the UI
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Description of the rule visible in the UI
|
||||||
|
Description string
|
||||||
|
|
||||||
|
// Enabled status of rule in the system
|
||||||
|
Enabled bool
|
||||||
|
|
||||||
|
// Action policy accept or drops packets
|
||||||
|
Action PolicyTrafficActionType
|
||||||
|
|
||||||
|
// Destinations policy destination groups
|
||||||
|
Destinations []string
|
||||||
|
|
||||||
|
// Sources policy source groups
|
||||||
|
Sources []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of a policy rule
|
||||||
|
func (pm *PolicyRule) Copy() *PolicyRule {
|
||||||
|
return &PolicyRule{
|
||||||
|
ID: pm.ID,
|
||||||
|
Name: pm.Name,
|
||||||
|
Description: pm.Description,
|
||||||
|
Enabled: pm.Enabled,
|
||||||
|
Action: pm.Action,
|
||||||
|
Destinations: pm.Destinations[:],
|
||||||
|
Sources: pm.Sources[:],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToRule converts the PolicyRule to a legacy representation of the Rule (for backwards compatibility)
|
||||||
|
func (pm *PolicyRule) ToRule() *Rule {
|
||||||
|
return &Rule{
|
||||||
|
ID: pm.ID,
|
||||||
|
Name: pm.Name,
|
||||||
|
Description: pm.Description,
|
||||||
|
Disabled: !pm.Enabled,
|
||||||
|
Flow: TrafficFlowBidirect,
|
||||||
|
Destination: pm.Destinations,
|
||||||
|
Source: pm.Sources,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Policy of the Rego query
|
||||||
|
type Policy struct {
|
||||||
|
// ID of the policy
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// Name of the Policy
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Description of the policy visible in the UI
|
||||||
|
Description string
|
||||||
|
|
||||||
|
// Enabled status of the policy
|
||||||
|
Enabled bool
|
||||||
|
|
||||||
|
// Query of Rego the policy
|
||||||
|
Query string
|
||||||
|
|
||||||
|
// Rules of the policy
|
||||||
|
Rules []*PolicyRule
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the policy.
|
||||||
|
func (p *Policy) Copy() *Policy {
|
||||||
|
c := &Policy{
|
||||||
|
ID: p.ID,
|
||||||
|
Name: p.Name,
|
||||||
|
Description: p.Description,
|
||||||
|
Enabled: p.Enabled,
|
||||||
|
Query: p.Query,
|
||||||
|
}
|
||||||
|
for _, r := range p.Rules {
|
||||||
|
c.Rules = append(c.Rules, r.Copy())
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventMeta returns activity event meta related to this policy
|
||||||
|
func (p *Policy) EventMeta() map[string]any {
|
||||||
|
return map[string]any{"name": p.Name}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateQueryFromRules marshals policy rules to Rego string and set it to Query
|
||||||
|
func (p *Policy) UpdateQueryFromRules() error {
|
||||||
|
type templateVars struct {
|
||||||
|
All []string
|
||||||
|
Source []string
|
||||||
|
Destination []string
|
||||||
|
}
|
||||||
|
queries := []string{}
|
||||||
|
for _, r := range p.Rules {
|
||||||
|
if !r.Enabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
buff := new(bytes.Buffer)
|
||||||
|
input := templateVars{
|
||||||
|
All: append(r.Destinations[:], r.Sources...),
|
||||||
|
Source: r.Sources,
|
||||||
|
Destination: r.Destinations,
|
||||||
|
}
|
||||||
|
if err := defaultPolicyTemplate.Execute(buff, input); err != nil {
|
||||||
|
return status.Errorf(status.BadRequest, "failed to update policy query: %v", err)
|
||||||
|
}
|
||||||
|
queries = append(queries, buff.String())
|
||||||
|
}
|
||||||
|
p.Query = strings.Join(queries, "\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirewallRule is a rule of the firewall.
|
||||||
|
type FirewallRule struct {
|
||||||
|
// PeerID of the peer
|
||||||
|
PeerID string
|
||||||
|
|
||||||
|
// PeerIP of the peer
|
||||||
|
PeerIP string
|
||||||
|
|
||||||
|
// Direction of the traffic
|
||||||
|
Direction string
|
||||||
|
|
||||||
|
// Action of the traffic
|
||||||
|
Action string
|
||||||
|
|
||||||
|
// Port of the traffic
|
||||||
|
Port string
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFromRegoResult parses the Rego result to a FirewallRule.
|
||||||
|
func (f *FirewallRule) parseFromRegoResult(value interface{}) error {
|
||||||
|
object, ok := value.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid Rego query eval result")
|
||||||
|
}
|
||||||
|
|
||||||
|
peerID, ok := object["ID"].(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid Rego query eval result peer ID type")
|
||||||
|
}
|
||||||
|
|
||||||
|
peerIP, ok := object["IP"].(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid Rego query eval result peer IP type")
|
||||||
|
}
|
||||||
|
|
||||||
|
direction, ok := object["Direction"].(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid Rego query eval result peer direction type")
|
||||||
|
}
|
||||||
|
|
||||||
|
action, ok := object["Action"].(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid Rego query eval result peer action type")
|
||||||
|
}
|
||||||
|
|
||||||
|
port, ok := object["Port"].(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid Rego query eval result peer port type")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.PeerID = peerID
|
||||||
|
f.PeerIP = peerIP
|
||||||
|
f.Direction = direction
|
||||||
|
f.Action = action
|
||||||
|
f.Port = port
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRegoQuery returns a initialized Rego object with default rule.
|
||||||
|
func (a *Account) getRegoQuery() (rego.PreparedEvalQuery, error) {
|
||||||
|
queries := []func(*rego.Rego){
|
||||||
|
rego.Query("data.netbird.all"),
|
||||||
|
rego.Module("netbird", defaultPolicyModule),
|
||||||
|
}
|
||||||
|
for i, p := range a.Policies {
|
||||||
|
if !p.Enabled {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
queries = append(queries, rego.Module(fmt.Sprintf("netbird-%d", i), p.Query))
|
||||||
|
}
|
||||||
|
return rego.New(queries...).PrepareForEval(context.TODO())
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPeersByPolicy returns all peers that given peer has access to.
|
||||||
|
func (a *Account) getPeersByPolicy(peerID string) ([]*Peer, []*FirewallRule) {
|
||||||
|
input := map[string]interface{}{
|
||||||
|
"peer_id": peerID,
|
||||||
|
"peers": a.Peers,
|
||||||
|
"groups": a.Groups,
|
||||||
|
}
|
||||||
|
|
||||||
|
query, err := a.getRegoQuery()
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("get Rego query")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
evalResult, err := query.Eval(
|
||||||
|
context.TODO(),
|
||||||
|
rego.EvalInput(input),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.WithError(err).Error("eval Rego query")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(evalResult) == 0 || len(evalResult[0].Expressions) == 0 {
|
||||||
|
log.Trace("empty Rego query eval result")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
expressions, ok := evalResult[0].Expressions[0].Value.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := make(map[string]struct{})
|
||||||
|
src := make(map[string]struct{})
|
||||||
|
peers := make([]*Peer, 0, len(expressions))
|
||||||
|
rules := make([]*FirewallRule, 0, len(expressions))
|
||||||
|
for _, v := range expressions {
|
||||||
|
rule := &FirewallRule{}
|
||||||
|
if err := rule.parseFromRegoResult(v); err != nil {
|
||||||
|
log.WithError(err).Error("parse Rego query eval result")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rules = append(rules, rule)
|
||||||
|
switch rule.Direction {
|
||||||
|
case "dst":
|
||||||
|
if _, ok := dst[rule.PeerID]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dst[rule.PeerID] = struct{}{}
|
||||||
|
case "src":
|
||||||
|
if _, ok := src[rule.PeerID]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
src[rule.PeerID] = struct{}{}
|
||||||
|
default:
|
||||||
|
log.WithField("direction", rule.Direction).Error("invalid direction")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
added := make(map[string]struct{})
|
||||||
|
if _, ok := src[peerID]; ok {
|
||||||
|
for id := range dst {
|
||||||
|
if _, ok := added[id]; !ok && id != peerID {
|
||||||
|
added[id] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ok := dst[peerID]; ok {
|
||||||
|
for id := range src {
|
||||||
|
if _, ok := added[id]; !ok && id != peerID {
|
||||||
|
added[id] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for id := range added {
|
||||||
|
peers = append(peers, a.Peers[id])
|
||||||
|
}
|
||||||
|
return peers, rules
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPolicy from the store
|
||||||
|
func (am *DefaultAccountManager) GetPolicy(accountID, policyID, userID string) (*Policy, error) {
|
||||||
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
|
defer unlock()
|
||||||
|
|
||||||
|
account, err := am.Store.GetAccount(accountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := account.FindUser(userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !user.IsAdmin() {
|
||||||
|
return nil, status.Errorf(status.PermissionDenied, "only admins are allowed to view policies")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, policy := range account.Policies {
|
||||||
|
if policy.ID == policyID {
|
||||||
|
return policy, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, status.Errorf(status.NotFound, "policy with ID %s not found", policyID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePolicy in the store
|
||||||
|
func (am *DefaultAccountManager) SavePolicy(accountID, userID string, policy *Policy) error {
|
||||||
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
|
defer unlock()
|
||||||
|
|
||||||
|
account, err := am.Store.GetAccount(accountID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
exists := am.savePolicy(account, policy)
|
||||||
|
|
||||||
|
account.Network.IncSerial()
|
||||||
|
if err = am.Store.SaveAccount(account); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
action := activity.PolicyAdded
|
||||||
|
if exists {
|
||||||
|
action = activity.PolicyUpdated
|
||||||
|
}
|
||||||
|
am.storeEvent(userID, policy.ID, accountID, action, policy.EventMeta())
|
||||||
|
|
||||||
|
return am.updateAccountPeers(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePolicy from the store
|
||||||
|
func (am *DefaultAccountManager) DeletePolicy(accountID, policyID, userID string) error {
|
||||||
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
|
defer unlock()
|
||||||
|
|
||||||
|
account, err := am.Store.GetAccount(accountID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
policy, err := am.deletePolicy(account, policyID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
account.Network.IncSerial()
|
||||||
|
if err = am.Store.SaveAccount(account); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
am.storeEvent(userID, policy.ID, accountID, activity.PolicyRemoved, policy.EventMeta())
|
||||||
|
|
||||||
|
return am.updateAccountPeers(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListPolicies from the store
|
||||||
|
func (am *DefaultAccountManager) ListPolicies(accountID, userID string) ([]*Policy, error) {
|
||||||
|
unlock := am.Store.AcquireAccountLock(accountID)
|
||||||
|
defer unlock()
|
||||||
|
|
||||||
|
account, err := am.Store.GetAccount(accountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := account.FindUser(userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !user.IsAdmin() {
|
||||||
|
return nil, status.Errorf(status.PermissionDenied, "Only Administrators can view policies")
|
||||||
|
}
|
||||||
|
|
||||||
|
return account.Policies[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *DefaultAccountManager) deletePolicy(account *Account, policyID string) (*Policy, error) {
|
||||||
|
policyIdx := -1
|
||||||
|
for i, policy := range account.Policies {
|
||||||
|
if policy.ID == policyID {
|
||||||
|
policyIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if policyIdx < 0 {
|
||||||
|
return nil, status.Errorf(status.NotFound, "rule with ID %s doesn't exist", policyID)
|
||||||
|
}
|
||||||
|
|
||||||
|
policy := account.Policies[policyIdx]
|
||||||
|
account.Policies = append(account.Policies[:policyIdx], account.Policies[policyIdx+1:]...)
|
||||||
|
return policy, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am *DefaultAccountManager) savePolicy(account *Account, policy *Policy) (exists bool) {
|
||||||
|
for i, p := range account.Policies {
|
||||||
|
if p.ID == policy.ID {
|
||||||
|
account.Policies[i] = policy
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
account.Policies = append(account.Policies, policy)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
67
management/server/policy_test.go
Normal file
67
management/server/policy_test.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAccount_getPeersByPolicy(t *testing.T) {
|
||||||
|
account := &Account{
|
||||||
|
Peers: map[string]*Peer{
|
||||||
|
"peer1": {
|
||||||
|
ID: "peer1",
|
||||||
|
IP: net.IPv4(10, 20, 0, 1),
|
||||||
|
},
|
||||||
|
"peer2": {
|
||||||
|
ID: "peer2",
|
||||||
|
IP: net.IPv4(10, 20, 0, 2),
|
||||||
|
},
|
||||||
|
"peer3": {
|
||||||
|
ID: "peer3",
|
||||||
|
IP: net.IPv4(10, 20, 0, 3),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Groups: map[string]*Group{
|
||||||
|
"gid1": {
|
||||||
|
ID: "gid1",
|
||||||
|
Name: "all",
|
||||||
|
Peers: []string{"peer1", "peer2", "peer3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Rules: map[string]*Rule{
|
||||||
|
"default": {
|
||||||
|
ID: "default",
|
||||||
|
Name: "default",
|
||||||
|
Description: "default",
|
||||||
|
Disabled: false,
|
||||||
|
Source: []string{"gid1"},
|
||||||
|
Destination: []string{"gid1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rule, err := RuleToPolicy(account.Rules["default"])
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
account.Policies = append(account.Policies, rule)
|
||||||
|
|
||||||
|
peers, firewallRules := account.getPeersByPolicy("peer1")
|
||||||
|
assert.Len(t, peers, 2)
|
||||||
|
assert.Contains(t, peers, account.Peers["peer2"])
|
||||||
|
assert.Contains(t, peers, account.Peers["peer3"])
|
||||||
|
|
||||||
|
epectedFirewallRules := []*FirewallRule{
|
||||||
|
{PeerID: "peer1", PeerIP: "10.20.0.1", Direction: "dst", Action: "accept", Port: ""},
|
||||||
|
{PeerID: "peer2", PeerIP: "10.20.0.2", Direction: "dst", Action: "accept", Port: ""},
|
||||||
|
{PeerID: "peer3", PeerIP: "10.20.0.3", Direction: "dst", Action: "accept", Port: ""},
|
||||||
|
{PeerID: "peer1", PeerIP: "10.20.0.1", Direction: "src", Action: "accept", Port: ""},
|
||||||
|
{PeerID: "peer2", PeerIP: "10.20.0.2", Direction: "src", Action: "accept", Port: ""},
|
||||||
|
{PeerID: "peer3", PeerIP: "10.20.0.3", Direction: "src", Action: "accept", Port: ""},
|
||||||
|
}
|
||||||
|
assert.Len(t, firewallRules, len(epectedFirewallRules))
|
||||||
|
for i := range firewallRules {
|
||||||
|
assert.Equal(t, firewallRules[i], epectedFirewallRules[i])
|
||||||
|
}
|
||||||
|
}
|
9
management/server/rego/default_policy.rego
Normal file
9
management/server/rego/default_policy.rego
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package netbird
|
||||||
|
|
||||||
|
all[rule] {
|
||||||
|
is_peer_in_any_group([{{range $i, $e := .All}}{{if $i}},{{end}}"{{$e}}"{{end}}])
|
||||||
|
rule := array.concat(
|
||||||
|
rules_from_groups([{{range $i, $e := .Destination}}{{if $i}},{{end}}"{{$e}}"{{end}}], "dst", "accept", ""),
|
||||||
|
rules_from_groups([{{range $i, $e := .Source}}{{if $i}},{{end}}"{{$e}}"{{end}}], "src", "accept", ""),
|
||||||
|
)[_]
|
||||||
|
}
|
40
management/server/rego/default_policy_module.rego
Normal file
40
management/server/rego/default_policy_module.rego
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package netbird
|
||||||
|
|
||||||
|
import future.keywords.if
|
||||||
|
import future.keywords.in
|
||||||
|
import future.keywords.contains
|
||||||
|
|
||||||
|
# get_rule builds a netbird rule object from given parameters
|
||||||
|
get_rule(peer_id, direction, action, port) := rule if {
|
||||||
|
peer := input.peers[_]
|
||||||
|
peer.ID == peer_id
|
||||||
|
rule := {
|
||||||
|
"ID": peer.ID,
|
||||||
|
"IP": peer.IP,
|
||||||
|
"Direction": direction,
|
||||||
|
"Action": action,
|
||||||
|
"Port": port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# peers_from_group returns a list of peer ids for a given group id
|
||||||
|
peers_from_group(group_id) := peers if {
|
||||||
|
group := input.groups[_]
|
||||||
|
group.ID == group_id
|
||||||
|
peers := [peer | peer := group.Peers[_]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# netbird_rules_from_groups returns a list of netbird rules for a given list of group names
|
||||||
|
rules_from_groups(groups, direction, action, port) := rules if {
|
||||||
|
group_id := groups[_]
|
||||||
|
rules := [get_rule(peer, direction, action, port) | peer := peers_from_group(group_id)[_]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# is_peer_in_any_group checks that input peer present at least in one group
|
||||||
|
is_peer_in_any_group(groups) := count([group_id]) > 0 if {
|
||||||
|
group_id := groups[_]
|
||||||
|
group := input.groups[_]
|
||||||
|
group.ID == group_id
|
||||||
|
peer := group.Peers[_]
|
||||||
|
peer == input.peer_id
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
"github.com/netbirdio/netbird/management/server/activity"
|
||||||
"github.com/netbirdio/netbird/route"
|
"github.com/netbirdio/netbird/route"
|
||||||
"github.com/rs/xid"
|
"github.com/rs/xid"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"net/netip"
|
|
||||||
"testing"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -21,7 +22,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateRoute(t *testing.T) {
|
func TestCreateRoute(t *testing.T) {
|
||||||
|
|
||||||
type input struct {
|
type input struct {
|
||||||
network string
|
network string
|
||||||
netID string
|
netID string
|
||||||
@ -265,13 +265,11 @@ func TestCreateRoute(t *testing.T) {
|
|||||||
if !testCase.expectedRoute.IsEqual(outRoute) {
|
if !testCase.expectedRoute.IsEqual(outRoute) {
|
||||||
t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", outRoute, testCase.expectedRoute)
|
t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", outRoute, testCase.expectedRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSaveRoute(t *testing.T) {
|
func TestSaveRoute(t *testing.T) {
|
||||||
|
|
||||||
validPeer := peer2ID
|
validPeer := peer2ID
|
||||||
invalidPeer := "nonExisting"
|
invalidPeer := "nonExisting"
|
||||||
validPrefix := netip.MustParsePrefix("192.168.0.0/24")
|
validPrefix := netip.MustParsePrefix("192.168.0.0/24")
|
||||||
@ -521,7 +519,6 @@ func TestSaveRoute(t *testing.T) {
|
|||||||
if !testCase.expectedRoute.IsEqual(savedRoute) {
|
if !testCase.expectedRoute.IsEqual(savedRoute) {
|
||||||
t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", savedRoute, testCase.expectedRoute)
|
t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", savedRoute, testCase.expectedRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -781,13 +778,11 @@ func TestUpdateRoute(t *testing.T) {
|
|||||||
if !testCase.expectedRoute.IsEqual(updatedRoute) {
|
if !testCase.expectedRoute.IsEqual(updatedRoute) {
|
||||||
t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", updatedRoute, testCase.expectedRoute)
|
t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", updatedRoute, testCase.expectedRoute)
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteRoute(t *testing.T) {
|
func TestDeleteRoute(t *testing.T) {
|
||||||
|
|
||||||
testingRoute := &route.Route{
|
testingRoute := &route.Route{
|
||||||
ID: "testingRoute",
|
ID: "testingRoute",
|
||||||
Network: netip.MustParsePrefix("192.168.0.0/16"),
|
Network: netip.MustParsePrefix("192.168.0.0/16"),
|
||||||
@ -906,20 +901,22 @@ func TestGetNetworkMap_RouteSync(t *testing.T) {
|
|||||||
err = am.SaveGroup(account.Id, userID, newGroup)
|
err = am.SaveGroup(account.Id, userID, newGroup)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rules, err := am.ListRules(account.Id, "testingUser")
|
rules, err := am.ListPolicies(account.Id, "testingUser")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
defaultRule := rules[0]
|
defaultRule := rules[0]
|
||||||
newRule := defaultRule.Copy()
|
newPolicy := defaultRule.Copy()
|
||||||
newRule.ID = xid.New().String()
|
newPolicy.ID = xid.New().String()
|
||||||
newRule.Name = "peer1 only"
|
newPolicy.Name = "peer1 only"
|
||||||
newRule.Source = []string{newGroup.ID}
|
newPolicy.Rules[0].Sources = []string{newGroup.ID}
|
||||||
newRule.Destination = []string{newGroup.ID}
|
newPolicy.Rules[0].Destinations = []string{newGroup.ID}
|
||||||
|
err = newPolicy.UpdateQueryFromRules()
|
||||||
err = am.SaveRule(account.Id, userID, newRule)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
err = am.DeleteRule(account.Id, defaultRule.ID, userID)
|
err = am.SavePolicy(account.Id, userID, newPolicy)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = am.DeletePolicy(account.Id, defaultRule.ID, userID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
peer1GroupRoutes, err := am.GetNetworkMap(peer1ID)
|
peer1GroupRoutes, err := am.GetNetworkMap(peer1ID)
|
||||||
@ -936,7 +933,6 @@ func TestGetNetworkMap_RouteSync(t *testing.T) {
|
|||||||
peer1DeletedRoute, err := am.GetNetworkMap(peer1ID)
|
peer1DeletedRoute, err := am.GetNetworkMap(peer1ID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, peer1DeletedRoute.Routes, 0, "we should receive one route for peer1")
|
require.Len(t, peer1DeletedRoute.Routes, 0, "we should receive one route for peer1")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
|
func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
|
||||||
@ -959,7 +955,6 @@ func createRouterStore(t *testing.T) (Store, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initTestRouteAccount(t *testing.T, am *DefaultAccountManager) (*Account, error) {
|
func initTestRouteAccount(t *testing.T, am *DefaultAccountManager) (*Account, error) {
|
||||||
|
|
||||||
accountID := "testingAcc"
|
accountID := "testingAcc"
|
||||||
domain := "example.com"
|
domain := "example.com"
|
||||||
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"github.com/netbirdio/netbird/management/server/activity"
|
|
||||||
"github.com/netbirdio/netbird/management/server/status"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TrafficFlowType defines allowed direction of the traffic in the rule
|
// TrafficFlowType defines allowed direction of the traffic in the rule
|
||||||
type TrafficFlowType int
|
type TrafficFlowType int
|
||||||
@ -18,6 +14,10 @@ const (
|
|||||||
DefaultRuleName = "Default"
|
DefaultRuleName = "Default"
|
||||||
// DefaultRuleDescription is a description for the Default rule that is created for every account
|
// DefaultRuleDescription is a description for the Default rule that is created for every account
|
||||||
DefaultRuleDescription = "This is a default rule that allows connections between all the resources"
|
DefaultRuleDescription = "This is a default rule that allows connections between all the resources"
|
||||||
|
// DefaultPolicyName is a name for the Default policy that is created for every account
|
||||||
|
DefaultPolicyName = "Default"
|
||||||
|
// DefaultPolicyDescription is a description for the Default policy that is created for every account
|
||||||
|
DefaultPolicyDescription = "This is a default policy that allows connections between all the resources"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Rule of ACL for groups
|
// Rule of ACL for groups
|
||||||
@ -44,38 +44,6 @@ type Rule struct {
|
|||||||
Flow TrafficFlowType
|
Flow TrafficFlowType
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
// UpdateRuleName indicates a rule name update operation
|
|
||||||
UpdateRuleName RuleUpdateOperationType = iota
|
|
||||||
// UpdateRuleDescription indicates a rule description update operation
|
|
||||||
UpdateRuleDescription
|
|
||||||
// UpdateRuleStatus indicates a rule status update operation
|
|
||||||
UpdateRuleStatus
|
|
||||||
// UpdateRuleFlow indicates a rule flow update operation
|
|
||||||
UpdateRuleFlow
|
|
||||||
// InsertGroupsToSource indicates an insert groups to source rule operation
|
|
||||||
InsertGroupsToSource
|
|
||||||
// RemoveGroupsFromSource indicates an remove groups from source rule operation
|
|
||||||
RemoveGroupsFromSource
|
|
||||||
// UpdateSourceGroups indicates a replacement of source group list of a rule operation
|
|
||||||
UpdateSourceGroups
|
|
||||||
// InsertGroupsToDestination indicates an insert groups to destination rule operation
|
|
||||||
InsertGroupsToDestination
|
|
||||||
// RemoveGroupsFromDestination indicates an remove groups from destination rule operation
|
|
||||||
RemoveGroupsFromDestination
|
|
||||||
// UpdateDestinationGroups indicates a replacement of destination group list of a rule operation
|
|
||||||
UpdateDestinationGroups
|
|
||||||
)
|
|
||||||
|
|
||||||
// RuleUpdateOperationType operation type
|
|
||||||
type RuleUpdateOperationType int
|
|
||||||
|
|
||||||
// RuleUpdateOperation operation object with type and values to be applied
|
|
||||||
type RuleUpdateOperation struct {
|
|
||||||
Type RuleUpdateOperationType
|
|
||||||
Values []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Rule) Copy() *Rule {
|
func (r *Rule) Copy() *Rule {
|
||||||
return &Rule{
|
return &Rule{
|
||||||
ID: r.ID,
|
ID: r.ID,
|
||||||
@ -93,185 +61,36 @@ func (r *Rule) EventMeta() map[string]any {
|
|||||||
return map[string]any{"name": r.Name}
|
return map[string]any{"name": r.Name}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRule of ACL from the store
|
// ToPolicyRule converts a Rule to a PolicyRule object
|
||||||
func (am *DefaultAccountManager) GetRule(accountID, ruleID, userID string) (*Rule, error) {
|
func (r *Rule) ToPolicyRule() *PolicyRule {
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
if r == nil {
|
||||||
defer unlock()
|
return nil
|
||||||
|
|
||||||
account, err := am.Store.GetAccount(accountID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
return &PolicyRule{
|
||||||
user, err := account.FindUser(userID)
|
ID: r.ID,
|
||||||
if err != nil {
|
Name: r.Name,
|
||||||
return nil, err
|
Enabled: !r.Disabled,
|
||||||
|
Description: r.Description,
|
||||||
|
Action: PolicyTrafficActionAccept,
|
||||||
|
Destinations: r.Destination,
|
||||||
|
Sources: r.Source,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
|
||||||
return nil, status.Errorf(status.PermissionDenied, "only admins are allowed to view rules")
|
|
||||||
}
|
|
||||||
|
|
||||||
rule, ok := account.Rules[ruleID]
|
|
||||||
if ok {
|
|
||||||
return rule, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, status.Errorf(status.NotFound, "rule with ID %s not found", ruleID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveRule of ACL in the store
|
// RuleToPolicy converts a Rule to a Policy query object
|
||||||
func (am *DefaultAccountManager) SaveRule(accountID, userID string, rule *Rule) error {
|
func RuleToPolicy(rule *Rule) (*Policy, error) {
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
|
||||||
defer unlock()
|
|
||||||
|
|
||||||
account, err := am.Store.GetAccount(accountID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, exists := account.Rules[rule.ID]
|
|
||||||
|
|
||||||
account.Rules[rule.ID] = rule
|
|
||||||
|
|
||||||
account.Network.IncSerial()
|
|
||||||
if err = am.Store.SaveAccount(account); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
action := activity.RuleAdded
|
|
||||||
if exists {
|
|
||||||
action = activity.RuleUpdated
|
|
||||||
}
|
|
||||||
am.storeEvent(userID, rule.ID, accountID, action, rule.EventMeta())
|
|
||||||
|
|
||||||
return am.updateAccountPeers(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateRule updates a rule using a list of operations
|
|
||||||
func (am *DefaultAccountManager) UpdateRule(accountID string, ruleID string,
|
|
||||||
operations []RuleUpdateOperation) (*Rule, error) {
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
|
||||||
defer unlock()
|
|
||||||
|
|
||||||
account, err := am.Store.GetAccount(accountID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleToUpdate, ok := account.Rules[ruleID]
|
|
||||||
if !ok {
|
|
||||||
return nil, status.Errorf(status.NotFound, "rule %s no longer exists", ruleID)
|
|
||||||
}
|
|
||||||
|
|
||||||
rule := ruleToUpdate.Copy()
|
|
||||||
|
|
||||||
for _, operation := range operations {
|
|
||||||
switch operation.Type {
|
|
||||||
case UpdateRuleName:
|
|
||||||
rule.Name = operation.Values[0]
|
|
||||||
case UpdateRuleDescription:
|
|
||||||
rule.Description = operation.Values[0]
|
|
||||||
case UpdateRuleFlow:
|
|
||||||
if operation.Values[0] != TrafficFlowBidirectString {
|
|
||||||
return nil, status.Errorf(status.InvalidArgument, "failed to parse flow")
|
|
||||||
}
|
|
||||||
rule.Flow = TrafficFlowBidirect
|
|
||||||
case UpdateRuleStatus:
|
|
||||||
if strings.ToLower(operation.Values[0]) == "true" {
|
|
||||||
rule.Disabled = true
|
|
||||||
} else if strings.ToLower(operation.Values[0]) == "false" {
|
|
||||||
rule.Disabled = false
|
|
||||||
} else {
|
|
||||||
return nil, status.Errorf(status.InvalidArgument, "failed to parse status")
|
|
||||||
}
|
|
||||||
case UpdateSourceGroups:
|
|
||||||
rule.Source = operation.Values
|
|
||||||
case InsertGroupsToSource:
|
|
||||||
sourceList := rule.Source
|
|
||||||
resultList := removeFromList(sourceList, operation.Values)
|
|
||||||
rule.Source = append(resultList, operation.Values...)
|
|
||||||
case RemoveGroupsFromSource:
|
|
||||||
sourceList := rule.Source
|
|
||||||
resultList := removeFromList(sourceList, operation.Values)
|
|
||||||
rule.Source = resultList
|
|
||||||
case UpdateDestinationGroups:
|
|
||||||
rule.Destination = operation.Values
|
|
||||||
case InsertGroupsToDestination:
|
|
||||||
sourceList := rule.Destination
|
|
||||||
resultList := removeFromList(sourceList, operation.Values)
|
|
||||||
rule.Destination = append(resultList, operation.Values...)
|
|
||||||
case RemoveGroupsFromDestination:
|
|
||||||
sourceList := rule.Destination
|
|
||||||
resultList := removeFromList(sourceList, operation.Values)
|
|
||||||
rule.Destination = resultList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
account.Rules[ruleID] = rule
|
|
||||||
|
|
||||||
account.Network.IncSerial()
|
|
||||||
if err = am.Store.SaveAccount(account); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = am.updateAccountPeers(account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, status.Errorf(status.Internal, "failed to update account peers")
|
|
||||||
}
|
|
||||||
|
|
||||||
return rule, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteRule of ACL from the store
|
|
||||||
func (am *DefaultAccountManager) DeleteRule(accountID, ruleID, userID string) error {
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
|
||||||
defer unlock()
|
|
||||||
|
|
||||||
account, err := am.Store.GetAccount(accountID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rule := account.Rules[ruleID]
|
|
||||||
if rule == nil {
|
if rule == nil {
|
||||||
return status.Errorf(status.NotFound, "rule with ID %s doesn't exist", ruleID)
|
return nil, fmt.Errorf("rule is empty")
|
||||||
}
|
}
|
||||||
delete(account.Rules, ruleID)
|
policy := &Policy{
|
||||||
|
ID: rule.ID,
|
||||||
account.Network.IncSerial()
|
Name: rule.Name,
|
||||||
if err = am.Store.SaveAccount(account); err != nil {
|
Description: rule.Description,
|
||||||
return err
|
Enabled: !rule.Disabled,
|
||||||
|
Rules: []*PolicyRule{rule.ToPolicyRule()},
|
||||||
}
|
}
|
||||||
|
if err := policy.UpdateQueryFromRules(); err != nil {
|
||||||
am.storeEvent(userID, rule.ID, accountID, activity.RuleRemoved, rule.EventMeta())
|
|
||||||
|
|
||||||
return am.updateAccountPeers(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListRules of ACL from the store
|
|
||||||
func (am *DefaultAccountManager) ListRules(accountID, userID string) ([]*Rule, error) {
|
|
||||||
unlock := am.Store.AcquireAccountLock(accountID)
|
|
||||||
defer unlock()
|
|
||||||
|
|
||||||
account, err := am.Store.GetAccount(accountID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return policy, nil
|
||||||
user, err := account.FindUser(userID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !user.IsAdmin() {
|
|
||||||
return nil, status.Errorf(status.PermissionDenied, "Only Administrators can view Access Rules")
|
|
||||||
}
|
|
||||||
|
|
||||||
rules := make([]*Rule, 0, len(account.Rules))
|
|
||||||
for _, item := range account.Rules {
|
|
||||||
rules = append(rules, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
return rules, nil
|
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,9 @@ const (
|
|||||||
|
|
||||||
// BadRequest indicates that user is not authorized
|
// BadRequest indicates that user is not authorized
|
||||||
BadRequest Type = 9
|
BadRequest Type = 9
|
||||||
|
|
||||||
|
// Unauthenticated indicates that user is not authenticated due to absence of valid credentials
|
||||||
|
Unauthenticated Type = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type is a type of the Error
|
// Type is a type of the Error
|
||||||
|
116
management/server/testdata/store_policy_migrate.json
vendored
Normal file
116
management/server/testdata/store_policy_migrate.json
vendored
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
{
|
||||||
|
"Accounts": {
|
||||||
|
"bf1c8084-ba50-4ce7-9439-34653001fc3b": {
|
||||||
|
"Id": "bf1c8084-ba50-4ce7-9439-34653001fc3b",
|
||||||
|
"Domain": "test.com",
|
||||||
|
"DomainCategory": "private",
|
||||||
|
"IsDomainPrimaryAccount": true,
|
||||||
|
"SetupKeys": {
|
||||||
|
"A2C8E62B-38F5-4553-B31E-DD66C696CEBB": {
|
||||||
|
"Key": "A2C8E62B-38F5-4553-B31E-DD66C696CEBB",
|
||||||
|
"Name": "Default key",
|
||||||
|
"Type": "reusable",
|
||||||
|
"CreatedAt": "2021-08-19T20:46:20.005936822+02:00",
|
||||||
|
"ExpiresAt": "2321-09-18T20:46:20.005936822+02:00",
|
||||||
|
"Revoked": false,
|
||||||
|
"UsedTimes": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Network": {
|
||||||
|
"Id": "af1c8024-ha40-4ce2-9418-34653101fc3c",
|
||||||
|
"Net": {
|
||||||
|
"IP": "100.64.0.0",
|
||||||
|
"Mask": "//8AAA=="
|
||||||
|
},
|
||||||
|
"Dns": null
|
||||||
|
},
|
||||||
|
"Peers": {
|
||||||
|
"cfefqs706sqkneg59g4g": {
|
||||||
|
"ID": "cfefqs706sqkneg59g4g",
|
||||||
|
"Key": "MI5mHfJhbggPfD3FqEIsXm8X5bSWeUI2LhO9MpEEtWA=",
|
||||||
|
"SetupKey": "",
|
||||||
|
"IP": "100.103.179.238",
|
||||||
|
"Meta": {
|
||||||
|
"Hostname": "Ubuntu-2204-jammy-amd64-base",
|
||||||
|
"GoOS": "linux",
|
||||||
|
"Kernel": "Linux",
|
||||||
|
"Core": "22.04",
|
||||||
|
"Platform": "x86_64",
|
||||||
|
"OS": "Ubuntu",
|
||||||
|
"WtVersion": "development",
|
||||||
|
"UIVersion": ""
|
||||||
|
},
|
||||||
|
"Name": "crocodile",
|
||||||
|
"DNSLabel": "crocodile",
|
||||||
|
"Status": {
|
||||||
|
"LastSeen": "2023-02-13T12:37:12.635454796Z",
|
||||||
|
"Connected": true
|
||||||
|
},
|
||||||
|
"UserID": "edafee4e-63fb-11ec-90d6-0242ac120003",
|
||||||
|
"SSHKey": "AAAAC3NzaC1lZDI1NTE5AAAAIJN1NM4bpB9K",
|
||||||
|
"SSHEnabled": false
|
||||||
|
},
|
||||||
|
"cfeg6sf06sqkneg59g50": {
|
||||||
|
"ID": "cfeg6sf06sqkneg59g50",
|
||||||
|
"Key": "zMAOKUeIYIuun4n0xPR1b3IdYZPmsyjYmB2jWCuloC4=",
|
||||||
|
"SetupKey": "",
|
||||||
|
"IP": "100.103.26.180",
|
||||||
|
"Meta": {
|
||||||
|
"Hostname": "borg",
|
||||||
|
"GoOS": "linux",
|
||||||
|
"Kernel": "Linux",
|
||||||
|
"Core": "22.04",
|
||||||
|
"Platform": "x86_64",
|
||||||
|
"OS": "Ubuntu",
|
||||||
|
"WtVersion": "development",
|
||||||
|
"UIVersion": ""
|
||||||
|
},
|
||||||
|
"Name": "dingo",
|
||||||
|
"DNSLabel": "dingo",
|
||||||
|
"Status": {
|
||||||
|
"LastSeen": "2023-02-21T09:37:42.565899199Z",
|
||||||
|
"Connected": false
|
||||||
|
},
|
||||||
|
"UserID": "f4f6d672-63fb-11ec-90d6-0242ac120003",
|
||||||
|
"SSHKey": "AAAAC3NzaC1lZDI1NTE5AAAAILHW",
|
||||||
|
"SSHEnabled": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Groups": {
|
||||||
|
"cfefqs706sqkneg59g3g": {
|
||||||
|
"ID": "cfefqs706sqkneg59g3g",
|
||||||
|
"Name": "All",
|
||||||
|
"Peers": [
|
||||||
|
"cfefqs706sqkneg59g4g",
|
||||||
|
"cfeg6sf06sqkneg59g50"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Rules": {
|
||||||
|
"cfefqs706sqkneg59g40": {
|
||||||
|
"ID": "cfefqs706sqkneg59g40",
|
||||||
|
"Name": "Default",
|
||||||
|
"Description": "This is a default rule that allows connections between all the resources",
|
||||||
|
"Disabled": false,
|
||||||
|
"Source": [
|
||||||
|
"cfefqs706sqkneg59g3g"
|
||||||
|
],
|
||||||
|
"Destination": [
|
||||||
|
"cfefqs706sqkneg59g3g"
|
||||||
|
],
|
||||||
|
"Flow": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Users": {
|
||||||
|
"edafee4e-63fb-11ec-90d6-0242ac120003": {
|
||||||
|
"Id": "edafee4e-63fb-11ec-90d6-0242ac120003",
|
||||||
|
"Role": "admin"
|
||||||
|
},
|
||||||
|
"f4f6d672-63fb-11ec-90d6-0242ac120003": {
|
||||||
|
"Id": "f4f6d672-63fb-11ec-90d6-0242ac120003",
|
||||||
|
"Role": "user"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
130
management/server/testdata/store_with_expired_peers.json
vendored
Normal file
130
management/server/testdata/store_with_expired_peers.json
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
{
|
||||||
|
"Accounts": {
|
||||||
|
"bf1c8084-ba50-4ce7-9439-34653001fc3b": {
|
||||||
|
"Id": "bf1c8084-ba50-4ce7-9439-34653001fc3b",
|
||||||
|
"Domain": "test.com",
|
||||||
|
"DomainCategory": "private",
|
||||||
|
"IsDomainPrimaryAccount": true,
|
||||||
|
"Settings": {
|
||||||
|
"PeerLoginExpirationEnabled": true,
|
||||||
|
"PeerLoginExpiration": 3600000000000
|
||||||
|
},
|
||||||
|
"SetupKeys": {
|
||||||
|
"A2C8E62B-38F5-4553-B31E-DD66C696CEBB": {
|
||||||
|
"Key": "A2C8E62B-38F5-4553-B31E-DD66C696CEBB",
|
||||||
|
"Name": "Default key",
|
||||||
|
"Type": "reusable",
|
||||||
|
"CreatedAt": "2021-08-19T20:46:20.005936822+02:00",
|
||||||
|
"ExpiresAt": "2321-09-18T20:46:20.005936822+02:00",
|
||||||
|
"Revoked": false,
|
||||||
|
"UsedTimes": 0
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Network": {
|
||||||
|
"Id": "af1c8024-ha40-4ce2-9418-34653101fc3c",
|
||||||
|
"Net": {
|
||||||
|
"IP": "100.64.0.0",
|
||||||
|
"Mask": "//8AAA=="
|
||||||
|
},
|
||||||
|
"Dns": null
|
||||||
|
},
|
||||||
|
"Peers": {
|
||||||
|
"cfvprsrlo1hqoo49ohog": {
|
||||||
|
"ID": "cfvprsrlo1hqoo49ohog",
|
||||||
|
"Key": "5rvhvriKJZ3S9oxYToVj5TzDM9u9y8cxg7htIMWlYAg=",
|
||||||
|
"SetupKey": "72546A29-6BC8-4311-BCFC-9CDBF33F1A48",
|
||||||
|
"IP": "100.64.114.31",
|
||||||
|
"Meta": {
|
||||||
|
"Hostname": "f2a34f6a4731",
|
||||||
|
"GoOS": "linux",
|
||||||
|
"Kernel": "Linux",
|
||||||
|
"Core": "11",
|
||||||
|
"Platform": "unknown",
|
||||||
|
"OS": "Debian GNU/Linux",
|
||||||
|
"WtVersion": "0.12.0",
|
||||||
|
"UIVersion": ""
|
||||||
|
},
|
||||||
|
"Name": "f2a34f6a4731",
|
||||||
|
"DNSLabel": "f2a34f6a4731",
|
||||||
|
"Status": {
|
||||||
|
"LastSeen": "2023-03-02T09:21:02.189035775+01:00",
|
||||||
|
"Connected": false,
|
||||||
|
"LoginExpired": false
|
||||||
|
},
|
||||||
|
"UserID": "",
|
||||||
|
"SSHKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILzUUSYG/LGnV8zarb2SGN+tib/PZ+M7cL4WtTzUrTpk",
|
||||||
|
"SSHEnabled": false,
|
||||||
|
"LoginExpirationEnabled": true,
|
||||||
|
"LastLogin": "2023-03-01T19:48:19.817799698+01:00"
|
||||||
|
},
|
||||||
|
"cg05lnblo1hkg2j514p0": {
|
||||||
|
"ID": "cg05lnblo1hkg2j514p0",
|
||||||
|
"Key": "RlSy2vzoG2HyMBTUImXOiVhCBiiBa5qD5xzMxkiFDW4=",
|
||||||
|
"SetupKey": "",
|
||||||
|
"IP": "100.64.39.54",
|
||||||
|
"Meta": {
|
||||||
|
"Hostname": "expiredhost",
|
||||||
|
"GoOS": "linux",
|
||||||
|
"Kernel": "Linux",
|
||||||
|
"Core": "22.04",
|
||||||
|
"Platform": "x86_64",
|
||||||
|
"OS": "Ubuntu",
|
||||||
|
"WtVersion": "development",
|
||||||
|
"UIVersion": ""
|
||||||
|
},
|
||||||
|
"Name": "expiredhost",
|
||||||
|
"DNSLabel": "expiredhost",
|
||||||
|
"Status": {
|
||||||
|
"LastSeen": "2023-03-02T09:19:57.276717255+01:00",
|
||||||
|
"Connected": false,
|
||||||
|
"LoginExpired": true
|
||||||
|
},
|
||||||
|
"UserID": "edafee4e-63fb-11ec-90d6-0242ac120003",
|
||||||
|
"SSHKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMbK5ZXJsGOOWoBT4OmkPtgdPZe2Q7bDuS/zjn2CZxhK",
|
||||||
|
"SSHEnabled": false,
|
||||||
|
"LoginExpirationEnabled": true,
|
||||||
|
"LastLogin": "2023-03-02T09:14:21.791679181+01:00"
|
||||||
|
},
|
||||||
|
"cg3161rlo1hs9cq94gdg": {
|
||||||
|
"ID": "cg3161rlo1hs9cq94gdg",
|
||||||
|
"Key": "mVABSKj28gv+JRsf7e0NEGKgSOGTfU/nPB2cpuG56HU=",
|
||||||
|
"SetupKey": "",
|
||||||
|
"IP": "100.64.117.96",
|
||||||
|
"Meta": {
|
||||||
|
"Hostname": "testhost",
|
||||||
|
"GoOS": "linux",
|
||||||
|
"Kernel": "Linux",
|
||||||
|
"Core": "22.04",
|
||||||
|
"Platform": "x86_64",
|
||||||
|
"OS": "Ubuntu",
|
||||||
|
"WtVersion": "development",
|
||||||
|
"UIVersion": ""
|
||||||
|
},
|
||||||
|
"Name": "testhost",
|
||||||
|
"DNSLabel": "testhost",
|
||||||
|
"Status": {
|
||||||
|
"LastSeen": "2023-03-06T18:21:27.252010027+01:00",
|
||||||
|
"Connected": false,
|
||||||
|
"LoginExpired": false
|
||||||
|
},
|
||||||
|
"UserID": "edafee4e-63fb-11ec-90d6-0242ac120003",
|
||||||
|
"SSHKey": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINWvvUkFFcrj48CWTkNUb/do/n52i1L5dH4DhGu+4ZuM",
|
||||||
|
"SSHEnabled": false,
|
||||||
|
"LoginExpirationEnabled": false,
|
||||||
|
"LastLogin": "2023-03-07T09:02:47.442857106+01:00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Users": {
|
||||||
|
"edafee4e-63fb-11ec-90d6-0242ac120003": {
|
||||||
|
"Id": "edafee4e-63fb-11ec-90d6-0242ac120003",
|
||||||
|
"Role": "admin"
|
||||||
|
},
|
||||||
|
"f4f6d672-63fb-11ec-90d6-0242ac120003": {
|
||||||
|
"Id": "f4f6d672-63fb-11ec-90d6-0242ac120003",
|
||||||
|
"Role": "user"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,11 +2,13 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/netbirdio/netbird/client/system"
|
|
||||||
"github.com/netbirdio/netbird/signal/proto"
|
|
||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/signal/proto"
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
|
|
||||||
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A set of tools to exchange connection details (Wireguard endpoints) with the remote peer.
|
// A set of tools to exchange connection details (Wireguard endpoints) with the remote peer.
|
||||||
@ -50,7 +52,7 @@ func MarshalCredential(myKey wgtypes.Key, myPort int, remoteKey wgtypes.Key, cre
|
|||||||
Type: t,
|
Type: t,
|
||||||
Payload: fmt.Sprintf("%s:%s", credential.UFrag, credential.Pwd),
|
Payload: fmt.Sprintf("%s:%s", credential.UFrag, credential.Pwd),
|
||||||
WgListenPort: uint32(myPort),
|
WgListenPort: uint32(myPort),
|
||||||
NetBirdVersion: system.NetbirdVersion(),
|
NetBirdVersion: version.NetbirdVersion(),
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/netbirdio/netbird/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -20,9 +22,10 @@ var (
|
|||||||
logFile string
|
logFile string
|
||||||
|
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "netbird-signal",
|
Use: "netbird-signal",
|
||||||
Short: "",
|
Short: "",
|
||||||
Long: "",
|
Long: "",
|
||||||
|
Version: version.NetbirdVersion(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execution control channel for stopCh signal
|
// Execution control channel for stopCh signal
|
||||||
|
9
version/version.go
Normal file
9
version/version.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package version
|
||||||
|
|
||||||
|
// will be replaced with the release version when using goreleaser
|
||||||
|
var version = "development"
|
||||||
|
|
||||||
|
// NetbirdVersion returns the Netbird version
|
||||||
|
func NetbirdVersion() string {
|
||||||
|
return version
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user