Add routing support to management service (#424)

Management will receive and store routes that are associated with a peer ID.
The routes are distributed to peers according to their ACLs.
This commit is contained in:
Maycon Santos 2022-08-18 18:22:15 +02:00 committed by GitHub
parent c39cd2f7b0
commit 4b34a6d6df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1689 additions and 113 deletions

View File

@ -6,12 +6,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: 1.18.x
- name: Install dependencies
run: sudo apt update && sudo apt install -y -q libgtk-3-dev libappindicator3-dev libayatana-appindicator3-dev libgl1-mesa-dev xorg-dev
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
args: --timeout=6m
# SA1019: "io/ioutil" has been deprecated since Go 1.16
args: --timeout=6m -e SA1019

View File

@ -9,7 +9,6 @@ import (
"flag"
"fmt"
"github.com/netbirdio/netbird/client/system"
"io/ioutil"
"os"
"os/exec"
"path"
@ -501,7 +500,7 @@ func (s *serviceClient) getSrvConfig() {
// checkPIDFile exists and return error, or write new.
func checkPIDFile() error {
pidFile := path.Join(os.TempDir(), "wiretrustee-ui.pid")
if piddata, err := ioutil.ReadFile(pidFile); err == nil {
if piddata, err := os.ReadFile(pidFile); err == nil {
if pid, err := strconv.Atoi(string(piddata)); err == nil {
if process, err := os.FindProcess(pid); err == nil {
if err := process.Signal(syscall.Signal(0)); err == nil {
@ -511,5 +510,5 @@ func checkPIDFile() error {
}
}
return ioutil.WriteFile(pidFile, []byte(fmt.Sprintf("%d", os.Getpid())), 0o664)
return os.WriteFile(pidFile, []byte(fmt.Sprintf("%d", os.Getpid())), 0o664)
}

View File

@ -11,7 +11,6 @@ import (
"golang.org/x/net/http2/h2c"
"io"
"io/fs"
"io/ioutil"
"net"
"net/http"
"os"
@ -394,7 +393,7 @@ func copySymLink(source, dest string) error {
func cpDir(src string, dst string) error {
var err error
var fds []os.FileInfo
var fds []os.DirEntry
var srcinfo os.FileInfo
if srcinfo, err = os.Stat(src); err != nil {
@ -405,7 +404,7 @@ func cpDir(src string, dst string) error {
return err
}
if fds, err = ioutil.ReadDir(src); err != nil {
if fds, err = os.ReadDir(src); err != nil {
return err
}
for _, fd := range fds {

View File

@ -1,15 +1,15 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.12.4
// protoc v3.21.2
// source: management.proto
package proto
import (
timestamp "github.com/golang/protobuf/ptypes/timestamp"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
)
@ -611,7 +611,7 @@ type ServerKeyResponse struct {
// Server's Wireguard public key
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
ExpiresAt *timestamp.Timestamp `protobuf:"bytes,2,opt,name=expiresAt,proto3" json:"expiresAt,omitempty"`
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=expiresAt,proto3" json:"expiresAt,omitempty"`
// Version of the Wiretrustee Management Service protocol
Version int32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"`
}
@ -655,7 +655,7 @@ func (x *ServerKeyResponse) GetKey() string {
return ""
}
func (x *ServerKeyResponse) GetExpiresAt() *timestamp.Timestamp {
func (x *ServerKeyResponse) GetExpiresAt() *timestamppb.Timestamp {
if x != nil {
return x.ExpiresAt
}
@ -980,6 +980,8 @@ type NetworkMap struct {
RemotePeers []*RemotePeerConfig `protobuf:"bytes,3,rep,name=remotePeers,proto3" json:"remotePeers,omitempty"`
// Indicates whether remotePeers array is empty or not to bypass protobuf null and empty array equality.
RemotePeersIsEmpty bool `protobuf:"varint,4,opt,name=remotePeersIsEmpty,proto3" json:"remotePeersIsEmpty,omitempty"`
// List of routes to be applied
Routes []*Route `protobuf:"bytes,5,rep,name=Routes,proto3" json:"Routes,omitempty"`
}
func (x *NetworkMap) Reset() {
@ -1042,6 +1044,13 @@ func (x *NetworkMap) GetRemotePeersIsEmpty() bool {
return false
}
func (x *NetworkMap) GetRoutes() []*Route {
if x != nil {
return x.Routes
}
return nil
}
// RemotePeerConfig represents a configuration of a remote peer.
// The properties are used to configure Wireguard Peers sections
type RemotePeerConfig struct {
@ -1343,6 +1352,94 @@ func (x *ProviderConfig) GetAudience() string {
return ""
}
// Route represents a route.Route object
type Route struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Prefix string `protobuf:"bytes,2,opt,name=Prefix,proto3" json:"Prefix,omitempty"`
PrefixType int64 `protobuf:"varint,3,opt,name=PrefixType,proto3" json:"PrefixType,omitempty"`
Peer string `protobuf:"bytes,4,opt,name=Peer,proto3" json:"Peer,omitempty"`
Metric int64 `protobuf:"varint,5,opt,name=Metric,proto3" json:"Metric,omitempty"`
Masquerade bool `protobuf:"varint,6,opt,name=Masquerade,proto3" json:"Masquerade,omitempty"`
}
func (x *Route) Reset() {
*x = Route{}
if protoimpl.UnsafeEnabled {
mi := &file_management_proto_msgTypes[19]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Route) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Route) ProtoMessage() {}
func (x *Route) ProtoReflect() protoreflect.Message {
mi := &file_management_proto_msgTypes[19]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Route.ProtoReflect.Descriptor instead.
func (*Route) Descriptor() ([]byte, []int) {
return file_management_proto_rawDescGZIP(), []int{19}
}
func (x *Route) GetID() string {
if x != nil {
return x.ID
}
return ""
}
func (x *Route) GetPrefix() string {
if x != nil {
return x.Prefix
}
return ""
}
func (x *Route) GetPrefixType() int64 {
if x != nil {
return x.PrefixType
}
return 0
}
func (x *Route) GetPeer() string {
if x != nil {
return x.Peer
}
return ""
}
func (x *Route) GetMetric() int64 {
if x != nil {
return x.Metric
}
return 0
}
func (x *Route) GetMasquerade() bool {
if x != nil {
return x.Masquerade
}
return false
}
var File_management_proto protoreflect.FileDescriptor
var file_management_proto_rawDesc = []byte{
@ -1459,7 +1556,7 @@ var file_management_proto_rawDesc = []byte{
0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53,
0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x22, 0xcc, 0x01, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d,
0x66, 0x69, 0x67, 0x22, 0xf7, 0x01, 0x0a, 0x0a, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d,
0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01,
0x28, 0x04, 0x52, 0x06, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x12, 0x36, 0x0a, 0x0a, 0x70, 0x65,
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
@ -1472,67 +1569,80 @@ var file_management_proto_rawDesc = []byte{
0x72, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72,
0x73, 0x49, 0x73, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12,
0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x73, 0x49, 0x73, 0x45, 0x6d, 0x70,
0x74, 0x79, 0x22, 0x83, 0x01, 0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65,
0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62,
0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62,
0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64,
0x49, 0x70, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x2e, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73,
0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62,
0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b,
0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62,
0x4b, 0x65, 0x79, 0x22, 0x20, 0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74,
0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,
0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f,
0x77, 0x12, 0x48, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
0x72, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22,
0x16, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48,
0x4f, 0x53, 0x54, 0x45, 0x44, 0x10, 0x00, 0x22, 0x84, 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, 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,
0x74, 0x79, 0x12, 0x29, 0x0a, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e,
0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x83, 0x01,
0x0a, 0x10, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x50, 0x65, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x67, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1e,
0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03,
0x28, 0x09, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x49, 0x70, 0x73, 0x12, 0x33,
0x0a, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53,
0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x09, 0x73, 0x73, 0x68, 0x43, 0x6f, 0x6e,
0x66, 0x69, 0x67, 0x22, 0x49, 0x0a, 0x09, 0x53, 0x53, 0x48, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x73, 0x68, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
0x12, 0x1c, 0x0a, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x73, 0x68, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0x20,
0x0a, 0x1e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x22, 0xbf, 0x01, 0x0a, 0x17, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f,
0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x48, 0x0a, 0x08,
0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x65, 0x76, 0x69,
0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46,
0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x50, 0x72,
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64,
0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x76,
0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x50, 0x72, 0x6f, 0x76,
0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x16, 0x0a, 0x08, 0x70, 0x72,
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x4f, 0x53, 0x54, 0x45, 0x44,
0x10, 0x00, 0x22, 0x84, 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, 0x22, 0x9b, 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, 0x16, 0x0a, 0x06, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1e, 0x0a, 0x0a, 0x50,
0x72, 0x65, 0x66, 0x69, 0x78, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
0x0a, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 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, 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, 0x65, 0x22,
0x00, 0x30, 0x01, 0x12, 0x42, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
0x4b, 0x65, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74,
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1d, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d,
0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x09, 0x69, 0x73, 0x48, 0x65, 0x61,
0x6c, 0x74, 0x68, 0x79, 0x12, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e,
0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x11, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65,
0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x1a,
0x47, 0x65, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x1c, 0x2e, 0x6d, 0x61, 0x6e,
0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65,
0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x1c, 0x2e, 0x6d, 0x61, 0x6e, 0x61, 0x67,
0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x06, 0x70, 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 (
@ -1548,7 +1658,7 @@ func file_management_proto_rawDescGZIP() []byte {
}
var file_management_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
var file_management_proto_msgTypes = make([]protoimpl.MessageInfo, 20)
var file_management_proto_goTypes = []interface{}{
(HostConfig_Protocol)(0), // 0: management.HostConfig.Protocol
(DeviceAuthorizationFlowProvider)(0), // 1: management.DeviceAuthorizationFlow.provider
@ -1571,7 +1681,8 @@ var file_management_proto_goTypes = []interface{}{
(*DeviceAuthorizationFlowRequest)(nil), // 18: management.DeviceAuthorizationFlowRequest
(*DeviceAuthorizationFlow)(nil), // 19: management.DeviceAuthorizationFlow
(*ProviderConfig)(nil), // 20: management.ProviderConfig
(*timestamp.Timestamp)(nil), // 21: google.protobuf.Timestamp
(*Route)(nil), // 21: management.Route
(*timestamppb.Timestamp)(nil), // 22: google.protobuf.Timestamp
}
var file_management_proto_depIdxs = []int32{
11, // 0: management.SyncResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig
@ -1582,7 +1693,7 @@ var file_management_proto_depIdxs = []int32{
6, // 5: management.LoginRequest.peerKeys:type_name -> management.PeerKeys
11, // 6: management.LoginResponse.wiretrusteeConfig:type_name -> management.WiretrusteeConfig
14, // 7: management.LoginResponse.peerConfig:type_name -> management.PeerConfig
21, // 8: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp
22, // 8: management.ServerKeyResponse.expiresAt:type_name -> google.protobuf.Timestamp
12, // 9: management.WiretrusteeConfig.stuns:type_name -> management.HostConfig
13, // 10: management.WiretrusteeConfig.turns:type_name -> management.ProtectedHostConfig
12, // 11: management.WiretrusteeConfig.signal:type_name -> management.HostConfig
@ -1591,24 +1702,25 @@ var file_management_proto_depIdxs = []int32{
17, // 14: management.PeerConfig.sshConfig:type_name -> management.SSHConfig
14, // 15: management.NetworkMap.peerConfig:type_name -> management.PeerConfig
16, // 16: management.NetworkMap.remotePeers:type_name -> management.RemotePeerConfig
17, // 17: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig
1, // 18: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider
20, // 19: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig
2, // 20: management.ManagementService.Login:input_type -> management.EncryptedMessage
2, // 21: management.ManagementService.Sync:input_type -> management.EncryptedMessage
10, // 22: management.ManagementService.GetServerKey:input_type -> management.Empty
10, // 23: management.ManagementService.isHealthy:input_type -> management.Empty
2, // 24: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage
2, // 25: management.ManagementService.Login:output_type -> management.EncryptedMessage
2, // 26: management.ManagementService.Sync:output_type -> management.EncryptedMessage
9, // 27: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse
10, // 28: management.ManagementService.isHealthy:output_type -> management.Empty
2, // 29: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage
25, // [25:30] is the sub-list for method output_type
20, // [20:25] is the sub-list for method input_type
20, // [20:20] is the sub-list for extension type_name
20, // [20:20] is the sub-list for extension extendee
0, // [0:20] is the sub-list for field type_name
21, // 17: management.NetworkMap.Routes:type_name -> management.Route
17, // 18: management.RemotePeerConfig.sshConfig:type_name -> management.SSHConfig
1, // 19: management.DeviceAuthorizationFlow.Provider:type_name -> management.DeviceAuthorizationFlow.provider
20, // 20: management.DeviceAuthorizationFlow.ProviderConfig:type_name -> management.ProviderConfig
2, // 21: management.ManagementService.Login:input_type -> management.EncryptedMessage
2, // 22: management.ManagementService.Sync:input_type -> management.EncryptedMessage
10, // 23: management.ManagementService.GetServerKey:input_type -> management.Empty
10, // 24: management.ManagementService.isHealthy:input_type -> management.Empty
2, // 25: management.ManagementService.GetDeviceAuthorizationFlow:input_type -> management.EncryptedMessage
2, // 26: management.ManagementService.Login:output_type -> management.EncryptedMessage
2, // 27: management.ManagementService.Sync:output_type -> management.EncryptedMessage
9, // 28: management.ManagementService.GetServerKey:output_type -> management.ServerKeyResponse
10, // 29: management.ManagementService.isHealthy:output_type -> management.Empty
2, // 30: management.ManagementService.GetDeviceAuthorizationFlow:output_type -> management.EncryptedMessage
26, // [26:31] is the sub-list for method output_type
21, // [21:26] is the sub-list for method input_type
21, // [21:21] is the sub-list for extension type_name
21, // [21:21] is the sub-list for extension extendee
0, // [0:21] is the sub-list for field type_name
}
func init() { file_management_proto_init() }
@ -1845,6 +1957,18 @@ func file_management_proto_init() {
return nil
}
}
file_management_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Route); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
@ -1852,7 +1976,7 @@ func file_management_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_management_proto_rawDesc,
NumEnums: 2,
NumMessages: 19,
NumMessages: 20,
NumExtensions: 0,
NumServices: 1,
},

View File

@ -176,6 +176,8 @@ message NetworkMap {
// Indicates whether remotePeers array is empty or not to bypass protobuf null and empty array equality.
bool remotePeersIsEmpty = 4;
// List of routes to be applied
repeated Route Routes = 5;
}
// RemotePeerConfig represents a configuration of a remote peer.
@ -229,3 +231,13 @@ message ProviderConfig {
// An Audience for validation
string Audience = 4;
}
// Route represents a route.Route object
message Route {
string ID = 1;
string Prefix = 2;
int64 PrefixType = 3;
string Peer = 4;
int64 Metric = 5;
bool Masquerade = 6;
}

View File

@ -7,6 +7,7 @@ import (
cacheStore "github.com/eko/gocache/v2/store"
"github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/route"
gocache "github.com/patrickmn/go-cache"
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
@ -48,6 +49,7 @@ type AccountManager interface {
RenamePeer(accountId string, peerKey string, newName string) (*Peer, error)
DeletePeer(accountId string, peerKey string) (*Peer, error)
GetPeerByIP(accountId string, peerIP string) (*Peer, error)
UpdatePeer(accountID string, peer *Peer) (*Peer, error)
GetNetworkMap(peerKey string) (*NetworkMap, error)
GetPeerNetwork(peerKey string) (*Network, error)
AddPeer(setupKey string, userId string, peer *Peer) (*Peer, error)
@ -67,7 +69,12 @@ type AccountManager interface {
UpdateRule(accountID string, ruleID string, operations []RuleUpdateOperation) (*Rule, error)
DeleteRule(accountId, ruleID string) error
ListRules(accountId string) ([]*Rule, error)
UpdatePeer(accountID string, peer *Peer) (*Peer, error)
GetRoute(accountID, routeID string) (*route.Route, error)
CreateRoute(accountID string, prefix, peer, description string, masquerade bool, metric int, enabled bool) (*route.Route, error)
SaveRoute(accountID string, route *route.Route) error
UpdateRoute(accountID string, routeID string, operations []RouteUpdateOperation) (*route.Route, error)
DeleteRoute(accountID, routeID string) error
ListRoutes(accountID string) ([]*route.Route, error)
}
type DefaultAccountManager struct {
@ -94,6 +101,7 @@ type Account struct {
Users map[string]*User
Groups map[string]*Group
Rules map[string]*Rule
Routes map[string]*route.Route
}
type UserInfo struct {
@ -686,6 +694,7 @@ func newAccountWithId(accountId, userId, domain string) *Account {
network := NewNetwork()
peers := make(map[string]*Peer)
users := make(map[string]*User)
routes := make(map[string]*route.Route)
users[userId] = NewAdminUser(userId)
log.Debugf("created new account %s with setup key %s", accountId, defaultKey.Key)
@ -697,6 +706,7 @@ func newAccountWithId(accountId, userId, domain string) *Account {
Users: users,
CreatedBy: userId,
Domain: domain,
Routes: routes,
}
addAllGroup(acc)

View File

@ -2,6 +2,8 @@ package server
import (
"fmt"
"github.com/netbirdio/netbird/route"
"net/netip"
"os"
"path/filepath"
"strings"
@ -25,6 +27,8 @@ type FileStore struct {
PrivateDomain2AccountId map[string]string `json:"-"`
PeerKeyId2SrcRulesId map[string]map[string]struct{} `json:"-"`
PeerKeyId2DstRulesId map[string]map[string]struct{} `json:"-"`
PeerKeyID2RouteIDs map[string]map[string]struct{} `json:"-"`
AccountPrefix2RouteIDs map[string]map[string][]string `json:"-"`
// mutex to synchronise Store read/write operations
mux sync.Mutex `json:"-"`
@ -51,7 +55,9 @@ func restore(file string) (*FileStore, error) {
UserId2AccountId: make(map[string]string),
PrivateDomain2AccountId: make(map[string]string),
PeerKeyId2SrcRulesId: make(map[string]map[string]struct{}),
PeerKeyID2RouteIDs: make(map[string]map[string]struct{}),
PeerKeyId2DstRulesId: make(map[string]map[string]struct{}),
AccountPrefix2RouteIDs: make(map[string]map[string][]string),
storeFile: file,
}
@ -74,8 +80,10 @@ func restore(file string) (*FileStore, error) {
store.PeerKeyId2AccountId = make(map[string]string)
store.UserId2AccountId = make(map[string]string)
store.PrivateDomain2AccountId = make(map[string]string)
store.PeerKeyId2SrcRulesId = map[string]map[string]struct{}{}
store.PeerKeyId2DstRulesId = map[string]map[string]struct{}{}
store.PeerKeyId2SrcRulesId = make(map[string]map[string]struct{})
store.PeerKeyId2DstRulesId = make(map[string]map[string]struct{})
store.PeerKeyID2RouteIDs = make(map[string]map[string]struct{})
store.AccountPrefix2RouteIDs = make(map[string]map[string][]string)
for accountId, account := range store.Accounts {
for setupKeyId := range account.SetupKeys {
@ -116,6 +124,24 @@ func restore(file string) (*FileStore, error) {
for _, user := range account.Users {
store.UserId2AccountId[user.Id] = accountId
}
for _, route := range account.Routes {
if route.Peer != "" {
if store.PeerKeyID2RouteIDs[route.Peer] == nil {
store.PeerKeyID2RouteIDs[route.Peer] = make(map[string]struct{})
}
store.PeerKeyID2RouteIDs[route.Peer][route.ID] = struct{}{}
}
if store.AccountPrefix2RouteIDs[account.Id] == nil {
store.AccountPrefix2RouteIDs[account.Id] = make(map[string][]string)
}
if _, ok := store.AccountPrefix2RouteIDs[account.Id][route.Prefix.String()]; !ok {
store.AccountPrefix2RouteIDs[account.Id][route.Prefix.String()] = make([]string, 0)
}
store.AccountPrefix2RouteIDs[account.Id][route.Prefix.String()] = append(
store.AccountPrefix2RouteIDs[account.Id][route.Prefix.String()],
route.ID,
)
}
if account.Domain != "" && account.DomainCategory == PrivateCategory &&
account.IsDomainPrimaryAccount {
store.PrivateDomain2AccountId[account.Domain] = accountId
@ -177,11 +203,12 @@ func (s *FileStore) DeletePeer(accountId string, peerKey string) (*Peer, error)
if peer == nil {
return nil, status.Errorf(codes.NotFound, "peer not found")
}
peerRoutes := s.PeerKeyID2RouteIDs[peerKey]
delete(account.Peers, peerKey)
delete(s.PeerKeyId2AccountId, peerKey)
delete(s.PeerKeyId2DstRulesId, peerKey)
delete(s.PeerKeyId2SrcRulesId, peerKey)
delete(s.PeerKeyID2RouteIDs, peerKey)
// cleanup groups
for _, g := range account.Groups {
@ -194,6 +221,11 @@ func (s *FileStore) DeletePeer(accountId string, peerKey string) (*Peer, error)
g.Peers = peers
}
for routeID := range peerRoutes {
account.Routes[routeID].Enabled = false
account.Routes[routeID].Peer = ""
}
err = s.persist(s.storeFile)
if err != nil {
return nil, err
@ -238,10 +270,14 @@ func (s *FileStore) SaveAccount(account *Account) error {
s.SetupKeyId2AccountId[strings.ToUpper(keyId)] = account.Id
}
// enforce peer to account index and delete peer to route indexes for rebuild
for _, peer := range account.Peers {
s.PeerKeyId2AccountId[peer.Key] = account.Id
delete(s.PeerKeyID2RouteIDs, peer.Key)
}
delete(s.AccountPrefix2RouteIDs, account.Id)
// remove all peers related to account from rules indexes
cleanIDs := make([]string, 0)
for key := range s.PeerKeyId2SrcRulesId {
@ -294,6 +330,25 @@ func (s *FileStore) SaveAccount(account *Account) error {
}
}
for _, route := range account.Routes {
if route.Peer != "" {
if s.PeerKeyID2RouteIDs[route.Peer] == nil {
s.PeerKeyID2RouteIDs[route.Peer] = make(map[string]struct{})
}
s.PeerKeyID2RouteIDs[route.Peer][route.ID] = struct{}{}
}
if s.AccountPrefix2RouteIDs[account.Id] == nil {
s.AccountPrefix2RouteIDs[account.Id] = make(map[string][]string)
}
if _, ok := s.AccountPrefix2RouteIDs[account.Id][route.Prefix.String()]; !ok {
s.AccountPrefix2RouteIDs[account.Id][route.Prefix.String()] = make([]string, 0)
}
s.AccountPrefix2RouteIDs[account.Id][route.Prefix.String()] = append(
s.AccountPrefix2RouteIDs[account.Id][route.Prefix.String()],
route.ID,
)
}
for _, user := range account.Users {
s.UserId2AccountId[user.Id] = account.Id
}
@ -305,6 +360,7 @@ func (s *FileStore) SaveAccount(account *Account) error {
return s.persist(s.storeFile)
}
// GetAccountByPrivateDomain returns account by private domain
func (s *FileStore) GetAccountByPrivateDomain(domain string) (*Account, error) {
accountId, accountIdFound := s.PrivateDomain2AccountId[strings.ToLower(domain)]
if !accountIdFound {
@ -322,6 +378,7 @@ func (s *FileStore) GetAccountByPrivateDomain(domain string) (*Account, error) {
return account, nil
}
// GetAccountBySetupKey returns account by setup key id
func (s *FileStore) GetAccountBySetupKey(setupKey string) (*Account, error) {
accountId, accountIdFound := s.SetupKeyId2AccountId[strings.ToUpper(setupKey)]
if !accountIdFound {
@ -336,6 +393,7 @@ func (s *FileStore) GetAccountBySetupKey(setupKey string) (*Account, error) {
return account, nil
}
// GetAccountPeers returns account peers
func (s *FileStore) GetAccountPeers(accountId string) ([]*Peer, error) {
s.mux.Lock()
defer s.mux.Unlock()
@ -353,6 +411,7 @@ func (s *FileStore) GetAccountPeers(accountId string) ([]*Peer, error) {
return peers, nil
}
// GetAllAccounts returns all accounts
func (s *FileStore) GetAllAccounts() (all []*Account) {
for _, a := range s.Accounts {
all = append(all, a)
@ -361,6 +420,7 @@ func (s *FileStore) GetAllAccounts() (all []*Account) {
return all
}
// GetAccount returns an account for id
func (s *FileStore) GetAccount(accountId string) (*Account, error) {
account, accountFound := s.Accounts[accountId]
if !accountFound {
@ -370,6 +430,7 @@ func (s *FileStore) GetAccount(accountId string) (*Account, error) {
return account, nil
}
// GetUserAccount returns a user account
func (s *FileStore) GetUserAccount(userId string) (*Account, error) {
s.mux.Lock()
defer s.mux.Unlock()
@ -382,10 +443,7 @@ func (s *FileStore) GetUserAccount(userId string) (*Account, error) {
return s.GetAccount(accountId)
}
func (s *FileStore) GetPeerAccount(peerKey string) (*Account, error) {
s.mux.Lock()
defer s.mux.Unlock()
func (s *FileStore) getPeerAccount(peerKey string) (*Account, error) {
accountId, accountIdFound := s.PeerKeyId2AccountId[peerKey]
if !accountIdFound {
return nil, status.Errorf(codes.NotFound, "Provided peer key doesn't exists %s", peerKey)
@ -394,6 +452,15 @@ func (s *FileStore) GetPeerAccount(peerKey string) (*Account, error) {
return s.GetAccount(accountId)
}
// GetPeerAccount returns user account if exists
func (s *FileStore) GetPeerAccount(peerKey string) (*Account, error) {
s.mux.Lock()
defer s.mux.Unlock()
return s.getPeerAccount(peerKey)
}
// GetPeerSrcRules return list of source rules for peer
func (s *FileStore) GetPeerSrcRules(accountId, peerKey string) ([]*Rule, error) {
s.mux.Lock()
defer s.mux.Unlock()
@ -419,6 +486,7 @@ func (s *FileStore) GetPeerSrcRules(accountId, peerKey string) ([]*Rule, error)
return rules, nil
}
// GetPeerDstRules return list of destination rules for peer
func (s *FileStore) GetPeerDstRules(accountId, peerKey string) ([]*Rule, error) {
s.mux.Lock()
defer s.mux.Unlock()
@ -443,3 +511,56 @@ func (s *FileStore) GetPeerDstRules(accountId, peerKey string) ([]*Rule, error)
return rules, nil
}
// GetPeerRoutes return list of routes for peer
func (s *FileStore) GetPeerRoutes(peerKey string) ([]*route.Route, error) {
s.mux.Lock()
defer s.mux.Unlock()
account, err := s.getPeerAccount(peerKey)
if err != nil {
return nil, err
}
var routes []*route.Route
routeIDs, ok := s.PeerKeyID2RouteIDs[peerKey]
if !ok {
return routes, nil
}
for id := range routeIDs {
route, found := account.Routes[id]
if found {
routes = append(routes, route)
}
}
return routes, nil
}
// GetRoutesByPrefix return list of routes by account and route prefix
func (s *FileStore) GetRoutesByPrefix(accountID string, prefix netip.Prefix) ([]*route.Route, error) {
s.mux.Lock()
defer s.mux.Unlock()
account, err := s.GetAccount(accountID)
if err != nil {
return nil, err
}
routeIDs, ok := s.AccountPrefix2RouteIDs[accountID][prefix.String()]
if !ok {
return nil, status.Errorf(codes.NotFound, "no routes for prefix: %v", prefix.String())
}
var routes []*route.Route
for _, id := range routeIDs {
route, found := account.Routes[id]
if found {
routes = append(routes, route)
}
}
return routes, nil
}

View File

@ -3,6 +3,7 @@ package server
import (
"context"
"fmt"
"github.com/netbirdio/netbird/route"
"strings"
"time"
@ -230,7 +231,7 @@ func (s *GRPCServer) registerPeer(peerKey wgtypes.Key, req *proto.LoginRequest)
peersToSend = append(peersToSend, p)
}
}
update := toSyncResponse(s.config, remotePeer, peersToSend, nil, networkMap.Network.CurrentSerial(), networkMap.Network)
update := toSyncResponse(s.config, remotePeer, peersToSend, networkMap.Routes, nil, networkMap.Network.CurrentSerial(), networkMap.Network)
err = s.peersUpdateManager.SendUpdate(remotePeer.Key, &UpdateMessage{Update: update})
if err != nil {
// todo rethink if we should keep this return
@ -407,13 +408,15 @@ func toRemotePeerConfig(peers []*Peer) []*proto.RemotePeerConfig {
return remotePeers
}
func toSyncResponse(config *Config, peer *Peer, peers []*Peer, turnCredentials *TURNCredentials, serial uint64, network *Network) *proto.SyncResponse {
func toSyncResponse(config *Config, peer *Peer, peers []*Peer, routes []*route.Route, turnCredentials *TURNCredentials, serial uint64, network *Network) *proto.SyncResponse {
wtConfig := toWiretrusteeConfig(config, turnCredentials)
pConfig := toPeerConfig(peer, network)
remotePeers := toRemotePeerConfig(peers)
routesUpdate := toProtocolRoutes(routes)
return &proto.SyncResponse{
WiretrusteeConfig: wtConfig,
PeerConfig: pConfig,
@ -424,6 +427,7 @@ func toSyncResponse(config *Config, peer *Peer, peers []*Peer, turnCredentials *
PeerConfig: pConfig,
RemotePeers: remotePeers,
RemotePeersIsEmpty: len(remotePeers) == 0,
Routes: routesUpdate,
},
}
}
@ -449,7 +453,7 @@ func (s *GRPCServer) sendInitialSync(peerKey wgtypes.Key, peer *Peer, srv proto.
} else {
turnCredentials = nil
}
plainResp := toSyncResponse(s.config, peer, networkMap.Peers, turnCredentials, networkMap.Network.CurrentSerial(), networkMap.Network)
plainResp := toSyncResponse(s.config, peer, networkMap.Peers, networkMap.Routes, turnCredentials, networkMap.Network.CurrentSerial(), networkMap.Network)
encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, plainResp)
if err != nil {

View File

@ -3,6 +3,7 @@ package mock_server
import (
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/jwtclaims"
"github.com/netbirdio/netbird/route"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"time"
@ -44,6 +45,12 @@ type MockAccountManager struct {
UpdatePeerMetaFunc func(peerKey string, meta server.PeerSystemMeta) error
UpdatePeerSSHKeyFunc func(peerKey string, sshKey string) error
UpdatePeerFunc func(accountID string, peer *server.Peer) (*server.Peer, error)
CreateRouteFunc func(accountID string, prefix, peer, description string, masquerade bool, metric int, enabled bool) (*route.Route, error)
GetRouteFunc func(accountID, routeID string) (*route.Route, error)
SaveRouteFunc func(accountID string, route *route.Route) error
UpdateRouteFunc func(accountID string, routeID string, operations []server.RouteUpdateOperation) (*route.Route, error)
DeleteRouteFunc func(accountID, routeID string) error
ListRoutesFunc func(accountID string) ([]*route.Route, error)
}
// GetUsersFromAccount mock implementation of GetUsersFromAccount from server.AccountManager interface
@ -360,3 +367,51 @@ func (am *MockAccountManager) UpdatePeer(accountID string, peer *server.Peer) (*
}
return nil, status.Errorf(codes.Unimplemented, "method UpdatePeerFunc is is not implemented")
}
// CreateRoute mock implementation of CreateRoute from server.AccountManager interface
func (am *MockAccountManager) CreateRoute(accountID string, prefix, peer, description string, masquerade bool, metric int, enabled bool) (*route.Route, error) {
if am.GetRouteFunc != nil {
return am.CreateRouteFunc(accountID, prefix, peer, description, masquerade, metric, enabled)
}
return nil, status.Errorf(codes.Unimplemented, "method CreateRoute is not implemented")
}
// GetRoute mock implementation of GetRoute from server.AccountManager interface
func (am *MockAccountManager) GetRoute(accountID, routeID string) (*route.Route, error) {
if am.GetRouteFunc != nil {
return am.GetRouteFunc(accountID, routeID)
}
return nil, status.Errorf(codes.Unimplemented, "method GetRoute is not implemented")
}
// SaveRoute mock implementation of SaveRoute from server.AccountManager interface
func (am *MockAccountManager) SaveRoute(accountID string, route *route.Route) error {
if am.SaveRouteFunc != nil {
return am.SaveRouteFunc(accountID, route)
}
return status.Errorf(codes.Unimplemented, "method SaveRoute is not implemented")
}
// UpdateRoute mock implementation of UpdateRoute from server.AccountManager interface
func (am *MockAccountManager) UpdateRoute(accountID string, ruleID string, operations []server.RouteUpdateOperation) (*route.Route, error) {
if am.UpdateRouteFunc != nil {
return am.UpdateRouteFunc(accountID, ruleID, operations)
}
return nil, status.Errorf(codes.Unimplemented, "method UpdateRoute not implemented")
}
// DeleteRoute mock implementation of DeleteRoute from server.AccountManager interface
func (am *MockAccountManager) DeleteRoute(accountID, routeID string) error {
if am.DeleteRouteFunc != nil {
return am.DeleteRouteFunc(accountID, routeID)
}
return status.Errorf(codes.Unimplemented, "method DeleteRoute is not implemented")
}
// ListRoutes mock implementation of ListRoutes from server.AccountManager interface
func (am *MockAccountManager) ListRoutes(accountID string) ([]*route.Route, error) {
if am.ListRoutesFunc != nil {
return am.ListRoutesFunc(accountID)
}
return nil, status.Errorf(codes.Unimplemented, "method ListRoutes is not implemented")
}

View File

@ -2,6 +2,7 @@ package server
import (
"github.com/c-robinson/iplib"
"github.com/netbirdio/netbird/route"
"github.com/rs/xid"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@ -24,6 +25,7 @@ const (
type NetworkMap struct {
Peers []*Peer
Network *Network
Routes []*route.Route
}
type Network struct {

View File

@ -251,9 +251,13 @@ func (am *DefaultAccountManager) GetNetworkMap(peerKey string) (*NetworkMap, err
return nil, status.Errorf(codes.Internal, "Invalid peer key %s", peerKey)
}
aclPeers := am.getPeersByACL(account, peerKey)
routesUpdate := am.getPeersRoutes(append(aclPeers, account.Peers[peerKey]))
return &NetworkMap{
Peers: am.getPeersByACL(account, peerKey),
Peers: aclPeers,
Network: account.Network.Copy(),
Routes: routesUpdate,
}, err
}
@ -508,20 +512,23 @@ func (am *DefaultAccountManager) updateAccountPeers(account *Account) error {
network := account.Network.Copy()
for _, p := range peers {
update := toRemotePeerConfig(am.getPeersByACL(account, p.Key))
err = am.peersUpdateManager.SendUpdate(p.Key,
for _, peer := range peers {
aclPeers := am.getPeersByACL(account, peer.Key)
peersUpdate := toRemotePeerConfig(aclPeers)
routesUpdate := toProtocolRoutes(am.getPeersRoutes(append(aclPeers, peer)))
err = am.peersUpdateManager.SendUpdate(peer.Key,
&UpdateMessage{
Update: &proto.SyncResponse{
// fill deprecated fields for backward compatibility
RemotePeers: update,
RemotePeersIsEmpty: len(update) == 0,
RemotePeers: peersUpdate,
RemotePeersIsEmpty: len(peersUpdate) == 0,
// new field
NetworkMap: &proto.NetworkMap{
Serial: account.Network.CurrentSerial(),
RemotePeers: update,
RemotePeersIsEmpty: len(update) == 0,
PeerConfig: toPeerConfig(p, network),
RemotePeers: peersUpdate,
RemotePeersIsEmpty: len(peersUpdate) == 0,
PeerConfig: toPeerConfig(peer, network),
Routes: routesUpdate,
},
},
})

362
management/server/route.go Normal file
View File

@ -0,0 +1,362 @@
package server
import (
"github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/route"
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"net/netip"
"strconv"
)
const (
// UpdateRouteDescription indicates a route description update operation
UpdateRouteDescription RouteUpdateOperationType = iota
// UpdateRoutePrefix indicates a route IP update operation
UpdateRoutePrefix
// UpdateRoutePeer indicates a route peer update operation
UpdateRoutePeer
// UpdateRouteMetric indicates a route metric update operation
UpdateRouteMetric
// UpdateRouteMasquerade indicates a route masquerade update operation
UpdateRouteMasquerade
// UpdateRouteEnabled indicates a route enabled update operation
UpdateRouteEnabled
)
// RouteUpdateOperationType operation type
type RouteUpdateOperationType int
func (t RouteUpdateOperationType) String() string {
switch t {
case UpdateRouteDescription:
return "UpdateRouteDescription"
case UpdateRoutePrefix:
return "UpdateRoutePrefix"
case UpdateRoutePeer:
return "UpdateRoutePeer"
case UpdateRouteMetric:
return "UpdateRouteMetric"
case UpdateRouteMasquerade:
return "UpdateRouteMasquerade"
case UpdateRouteEnabled:
return "UpdateRouteEnabled"
default:
return "InvalidOperation"
}
}
// RouteUpdateOperation operation object with type and values to be applied
type RouteUpdateOperation struct {
Type RouteUpdateOperationType
Values []string
}
// GetRoute gets a route object from account and route IDs
func (am *DefaultAccountManager) GetRoute(accountID, routeID string) (*route.Route, error) {
am.mux.Lock()
defer am.mux.Unlock()
account, err := am.Store.GetAccount(accountID)
if err != nil {
return nil, status.Errorf(codes.NotFound, "account not found")
}
wantedRoute, found := account.Routes[routeID]
if found {
return wantedRoute, nil
}
return nil, status.Errorf(codes.NotFound, "route with ID %s not found", routeID)
}
// checkPrefixPeerExists checks the combination of prefix and peer id, if it exists returns an error, otehrwise returns nil
func (am *DefaultAccountManager) checkPrefixPeerExists(accountID, peer string, prefix netip.Prefix) error {
routesWithPrefix, err := am.Store.GetRoutesByPrefix(accountID, prefix)
if err != nil {
if s, ok := status.FromError(err); ok && s.Code() == codes.NotFound {
return nil
}
return status.Errorf(codes.InvalidArgument, "failed to parse prefix %s", prefix.String())
}
for _, prefixRoute := range routesWithPrefix {
if prefixRoute.Peer == peer {
return status.Errorf(codes.AlreadyExists, "failed a route with prefix %s and peer already exist", prefix.String())
}
}
return nil
}
// CreateRoute creates and saves a new route
func (am *DefaultAccountManager) CreateRoute(accountID string, prefix, peer, description string, masquerade bool, metric int, enabled bool) (*route.Route, error) {
am.mux.Lock()
defer am.mux.Unlock()
account, err := am.Store.GetAccount(accountID)
if err != nil {
return nil, status.Errorf(codes.NotFound, "account not found")
}
var newRoute route.Route
prefixType, newPrefix, err := route.ParsePrefix(prefix)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to parse IP %s", prefix)
}
err = am.checkPrefixPeerExists(accountID, peer, newPrefix)
if err != nil {
return nil, err
}
if peer != "" {
_, peerExist := account.Peers[peer]
if !peerExist {
return nil, status.Errorf(codes.InvalidArgument, "failed to find Peer %s", peer)
}
}
if metric < route.MinMetric || metric > route.MaxMetric {
return nil, status.Errorf(codes.InvalidArgument, "metric should be between %d and %d", route.MinMetric, route.MaxMetric)
}
newRoute.Peer = peer
newRoute.ID = xid.New().String()
newRoute.Prefix = newPrefix
newRoute.PrefixType = prefixType
newRoute.Description = description
newRoute.Masquerade = masquerade
newRoute.Metric = metric
newRoute.Enabled = enabled
if account.Routes == nil {
account.Routes = make(map[string]*route.Route)
}
account.Routes[newRoute.ID] = &newRoute
account.Network.IncSerial()
if err = am.Store.SaveAccount(account); err != nil {
return nil, err
}
err = am.updateAccountPeers(account)
if err != nil {
log.Error(err)
return &newRoute, status.Errorf(codes.Unavailable, "failed to update peers after create route %s", newPrefix)
}
return &newRoute, nil
}
// SaveRoute saves route
func (am *DefaultAccountManager) SaveRoute(accountID string, routeToSave *route.Route) error {
am.mux.Lock()
defer am.mux.Unlock()
if routeToSave == nil {
return status.Errorf(codes.InvalidArgument, "route provided is nil")
}
if !routeToSave.Prefix.IsValid() {
return status.Errorf(codes.InvalidArgument, "invalid Prefix %s", routeToSave.Prefix.String())
}
if routeToSave.Metric < route.MinMetric || routeToSave.Metric > route.MaxMetric {
return status.Errorf(codes.InvalidArgument, "metric should be between %d and %d", route.MinMetric, route.MaxMetric)
}
account, err := am.Store.GetAccount(accountID)
if err != nil {
return status.Errorf(codes.NotFound, "account not found")
}
if routeToSave.Peer != "" {
_, peerExist := account.Peers[routeToSave.Peer]
if !peerExist {
return status.Errorf(codes.InvalidArgument, "failed to find Peer %s", routeToSave.Peer)
}
}
account.Routes[routeToSave.ID] = routeToSave
account.Network.IncSerial()
if err = am.Store.SaveAccount(account); err != nil {
return err
}
return am.updateAccountPeers(account)
}
// UpdateRoute updates existing route with set of operations
func (am *DefaultAccountManager) UpdateRoute(accountID, routeID string, operations []RouteUpdateOperation) (*route.Route, error) {
am.mux.Lock()
defer am.mux.Unlock()
account, err := am.Store.GetAccount(accountID)
if err != nil {
return nil, status.Errorf(codes.NotFound, "account not found")
}
routeToUpdate, ok := account.Routes[routeID]
if !ok {
return nil, status.Errorf(codes.NotFound, "route %s no longer exists", routeID)
}
newRoute := routeToUpdate.Copy()
for _, operation := range operations {
if len(operation.Values) != 1 {
return nil, status.Errorf(codes.InvalidArgument, "operation %s contains invalid number of values, it should be 1", operation.Type.String())
}
switch operation.Type {
case UpdateRouteDescription:
newRoute.Description = operation.Values[0]
case UpdateRoutePrefix:
prefixType, prefix, err := route.ParsePrefix(operation.Values[0])
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to parse IP %s", operation.Values[0])
}
err = am.checkPrefixPeerExists(accountID, routeToUpdate.Peer, prefix)
if err != nil {
return nil, err
}
newRoute.Prefix = prefix
newRoute.PrefixType = prefixType
case UpdateRoutePeer:
if operation.Values[0] != "" {
_, peerExist := account.Peers[operation.Values[0]]
if !peerExist {
return nil, status.Errorf(codes.InvalidArgument, "failed to find Peer %s", operation.Values[0])
}
}
err = am.checkPrefixPeerExists(accountID, operation.Values[0], routeToUpdate.Prefix)
if err != nil {
return nil, err
}
newRoute.Peer = operation.Values[0]
case UpdateRouteMetric:
metric, err := strconv.Atoi(operation.Values[0])
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to parse metric %s, not int", operation.Values[0])
}
if metric < route.MinMetric || metric > route.MaxMetric {
return nil, status.Errorf(codes.InvalidArgument, "failed to parse metric %s, value should be %d > N < %d",
operation.Values[0],
route.MinMetric,
route.MaxMetric,
)
}
newRoute.Metric = metric
case UpdateRouteMasquerade:
masquerade, err := strconv.ParseBool(operation.Values[0])
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to parse masquerade %s, not boolean", operation.Values[0])
}
newRoute.Masquerade = masquerade
case UpdateRouteEnabled:
enabled, err := strconv.ParseBool(operation.Values[0])
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "failed to parse enabled %s, not boolean", operation.Values[0])
}
newRoute.Enabled = enabled
}
}
account.Routes[routeID] = newRoute
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(codes.Internal, "failed to update account peers")
}
return newRoute, nil
}
// DeleteRoute deletes route with routeID
func (am *DefaultAccountManager) DeleteRoute(accountID, routeID string) error {
am.mux.Lock()
defer am.mux.Unlock()
account, err := am.Store.GetAccount(accountID)
if err != nil {
return status.Errorf(codes.NotFound, "account not found")
}
delete(account.Routes, routeID)
account.Network.IncSerial()
if err = am.Store.SaveAccount(account); err != nil {
return err
}
return am.updateAccountPeers(account)
}
// ListRoutes returns a list of routes from account
func (am *DefaultAccountManager) ListRoutes(accountID string) ([]*route.Route, error) {
am.mux.Lock()
defer am.mux.Unlock()
account, err := am.Store.GetAccount(accountID)
if err != nil {
return nil, status.Errorf(codes.NotFound, "account not found")
}
routes := make([]*route.Route, 0, len(account.Routes))
for _, item := range account.Routes {
routes = append(routes, item)
}
return routes, nil
}
func toProtocolRoute(route *route.Route) *proto.Route {
return &proto.Route{
ID: route.ID,
Prefix: route.Prefix.String(),
PrefixType: int64(route.PrefixType),
Peer: route.Peer,
Metric: int64(route.Metric),
Masquerade: route.Masquerade,
}
}
func (am *DefaultAccountManager) getPeersRoutes(peers []*Peer) []*route.Route {
routes := make([]*route.Route, 0)
for _, peer := range peers {
peerRoutes, err := am.Store.GetPeerRoutes(peer.Key)
if err != nil {
errorStatus, ok := status.FromError(err)
if !ok && errorStatus.Code() != codes.NotFound {
log.Error(err)
}
continue
}
activeRoutes := make([]*route.Route, 0)
for _, pr := range peerRoutes {
if pr.Enabled {
activeRoutes = append(activeRoutes, pr)
}
}
if len(activeRoutes) > 0 {
routes = append(routes, activeRoutes...)
}
}
return routes
}
func toProtocolRoutes(routes []*route.Route) []*proto.Route {
protoRoutes := make([]*proto.Route, 0)
for _, r := range routes {
protoRoutes = append(protoRoutes, toProtocolRoute(r))
}
return protoRoutes
}

View File

@ -0,0 +1,751 @@
package server
import (
"github.com/netbirdio/netbird/route"
"github.com/rs/xid"
"github.com/stretchr/testify/require"
"net/netip"
"testing"
)
const peer1Key = "BhRPtynAAYRDy08+q4HTMsos8fs4plTP4NOSh7C1ry8="
const peer2Key = "/yF0+vCfv+mRR5k0dca0TrGdO/oiNeAI58gToZm5NyI="
func TestCreateRoute(t *testing.T) {
type input struct {
prefix string
peer string
description string
masquerade bool
metric int
enabled bool
}
testCases := []struct {
name string
inputArgs input
shouldCreate bool
errFunc require.ErrorAssertionFunc
expectedRoute *route.Route
}{
{
name: "Happy Path",
inputArgs: input{
prefix: "192.168.0.0/16",
peer: peer1Key,
description: "super",
masquerade: false,
metric: 9999,
enabled: true,
},
errFunc: require.NoError,
shouldCreate: true,
expectedRoute: &route.Route{
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
},
},
{
name: "Bad Prefix",
inputArgs: input{
prefix: "192.168.0.0/34",
peer: peer1Key,
description: "super",
masquerade: false,
metric: 9999,
enabled: true,
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Bad Peer",
inputArgs: input{
prefix: "192.168.0.0/16",
peer: "notExistingPeer",
description: "super",
masquerade: false,
metric: 9999,
enabled: true,
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Empty Peer",
inputArgs: input{
prefix: "192.168.0.0/16",
peer: "",
description: "super",
masquerade: false,
metric: 9999,
enabled: false,
},
errFunc: require.NoError,
shouldCreate: true,
expectedRoute: &route.Route{
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: "",
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: false,
},
},
{
name: "Large Metric",
inputArgs: input{
prefix: "192.168.0.0/16",
peer: peer1Key,
description: "super",
masquerade: false,
metric: 99999,
enabled: true,
},
errFunc: require.Error,
shouldCreate: false,
},
{
name: "Small Metric",
inputArgs: input{
prefix: "192.168.0.0/16",
peer: peer1Key,
description: "super",
masquerade: false,
metric: 0,
enabled: true,
},
errFunc: require.Error,
shouldCreate: false,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
am, err := createRouterManager(t)
if err != nil {
t.Error("failed to create account manager")
}
account, err := initTestRouteAccount(t, am)
if err != nil {
t.Error("failed to init testing account")
}
outRoute, err := am.CreateRoute(
account.Id,
testCase.inputArgs.prefix,
testCase.inputArgs.peer,
testCase.inputArgs.description,
testCase.inputArgs.masquerade,
testCase.inputArgs.metric,
testCase.inputArgs.enabled,
)
testCase.errFunc(t, err)
if !testCase.shouldCreate {
return
}
// assign generated ID
testCase.expectedRoute.ID = outRoute.ID
if !testCase.expectedRoute.IsEqual(outRoute) {
t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", outRoute, testCase.expectedRoute)
}
})
}
}
func TestSaveRoute(t *testing.T) {
validPeer := peer2Key
invalidPeer := "nonExisting"
validPrefix := netip.MustParsePrefix("192.168.0.0/24")
invalidPrefix, _ := netip.ParsePrefix("192.168.0.0/34")
validMetric := 1000
invalidMetric := 99999
testCases := []struct {
name string
existingRoute *route.Route
newPeer *string
newMetric *int
newPrefix *netip.Prefix
skipCopying bool
shouldCreate bool
errFunc require.ErrorAssertionFunc
expectedRoute *route.Route
}{
{
name: "Happy Path",
existingRoute: &route.Route{
ID: "testingRoute",
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
},
newPeer: &validPeer,
newMetric: &validMetric,
newPrefix: &validPrefix,
errFunc: require.NoError,
shouldCreate: true,
expectedRoute: &route.Route{
ID: "testingRoute",
Prefix: validPrefix,
PrefixType: route.IPv4Prefix,
Peer: validPeer,
Description: "super",
Masquerade: false,
Metric: validMetric,
Enabled: true,
},
},
{
name: "Bad Prefix",
existingRoute: &route.Route{
ID: "testingRoute",
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
},
newPrefix: &invalidPrefix,
errFunc: require.Error,
},
{
name: "Bad Peer",
existingRoute: &route.Route{
ID: "testingRoute",
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
},
newPeer: &invalidPeer,
errFunc: require.Error,
},
{
name: "Invalid Metric",
existingRoute: &route.Route{
ID: "testingRoute",
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
},
newMetric: &invalidMetric,
errFunc: require.Error,
},
{
name: "Nil Route",
existingRoute: &route.Route{
ID: "testingRoute",
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
},
skipCopying: true,
errFunc: require.Error,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
am, err := createRouterManager(t)
if err != nil {
t.Error("failed to create account manager")
}
account, err := initTestRouteAccount(t, am)
if err != nil {
t.Error("failed to init testing account")
}
account.Routes[testCase.existingRoute.ID] = testCase.existingRoute
err = am.Store.SaveAccount(account)
if err != nil {
t.Error("account should be saved")
}
var routeToSave *route.Route
if !testCase.skipCopying {
routeToSave = testCase.existingRoute.Copy()
if testCase.newPeer != nil {
routeToSave.Peer = *testCase.newPeer
}
if testCase.newMetric != nil {
routeToSave.Metric = *testCase.newMetric
}
if testCase.newPrefix != nil {
routeToSave.Prefix = *testCase.newPrefix
}
}
err = am.SaveRoute(account.Id, routeToSave)
testCase.errFunc(t, err)
if !testCase.shouldCreate {
return
}
savedRoute, saved := account.Routes[testCase.expectedRoute.ID]
require.True(t, saved)
if !testCase.expectedRoute.IsEqual(savedRoute) {
t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", savedRoute, testCase.expectedRoute)
}
})
}
}
func TestUpdateRoute(t *testing.T) {
routeID := "testingRouteID"
existingRoute := &route.Route{
ID: routeID,
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
}
testCases := []struct {
name string
existingRoute *route.Route
operations []RouteUpdateOperation
shouldCreate bool
errFunc require.ErrorAssertionFunc
expectedRoute *route.Route
}{
{
name: "Happy Path Single OPS",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
Type: UpdateRoutePeer,
Values: []string{peer2Key},
},
},
errFunc: require.NoError,
shouldCreate: true,
expectedRoute: &route.Route{
ID: routeID,
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: peer2Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
},
},
{
name: "Happy Path Multiple OPS",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
Type: UpdateRouteDescription,
Values: []string{"great"},
},
RouteUpdateOperation{
Type: UpdateRoutePrefix,
Values: []string{"192.168.0.0/24"},
},
RouteUpdateOperation{
Type: UpdateRoutePeer,
Values: []string{peer2Key},
},
RouteUpdateOperation{
Type: UpdateRouteMetric,
Values: []string{"3030"},
},
RouteUpdateOperation{
Type: UpdateRouteMasquerade,
Values: []string{"true"},
},
RouteUpdateOperation{
Type: UpdateRouteEnabled,
Values: []string{"false"},
},
},
errFunc: require.NoError,
shouldCreate: true,
expectedRoute: &route.Route{
ID: routeID,
Prefix: netip.MustParsePrefix("192.168.0.0/24"),
PrefixType: route.IPv4Prefix,
Peer: peer2Key,
Description: "great",
Masquerade: true,
Metric: 3030,
Enabled: false,
},
},
{
name: "Empty Values",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
Type: UpdateRoutePeer,
},
},
errFunc: require.Error,
},
{
name: "Multiple Values",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
Type: UpdateRoutePeer,
Values: []string{peer2Key, peer1Key},
},
},
errFunc: require.Error,
},
{
name: "Bad Prefix",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
Type: UpdateRoutePrefix,
Values: []string{"192.168.0.0/34"},
},
},
errFunc: require.Error,
},
{
name: "Bad Peer",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
Type: UpdateRoutePeer,
Values: []string{"non existing Peer"},
},
},
errFunc: require.Error,
},
{
name: "Empty Peer",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
Type: UpdateRoutePeer,
Values: []string{""},
},
},
errFunc: require.NoError,
shouldCreate: true,
expectedRoute: &route.Route{
ID: routeID,
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: "",
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
},
},
{
name: "Invalid Metric",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
Type: UpdateRouteMetric,
Values: []string{"999999"},
},
},
errFunc: require.Error,
},
{
name: "Invalid Boolean",
existingRoute: existingRoute,
operations: []RouteUpdateOperation{
RouteUpdateOperation{
Type: UpdateRouteMasquerade,
Values: []string{"yes"},
},
},
errFunc: require.Error,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
am, err := createRouterManager(t)
if err != nil {
t.Error("failed to create account manager")
}
account, err := initTestRouteAccount(t, am)
if err != nil {
t.Error("failed to init testing account")
}
account.Routes[testCase.existingRoute.ID] = testCase.existingRoute
err = am.Store.SaveAccount(account)
if err != nil {
t.Error("account should be saved")
}
updatedRoute, err := am.UpdateRoute(account.Id, testCase.existingRoute.ID, testCase.operations)
testCase.errFunc(t, err)
if !testCase.shouldCreate {
return
}
testCase.expectedRoute.ID = updatedRoute.ID
if !testCase.expectedRoute.IsEqual(updatedRoute) {
t.Errorf("new route didn't match expected route:\nGot %#v\nExpected:%#v\n", updatedRoute, testCase.expectedRoute)
}
})
}
}
func TestDeleteRoute(t *testing.T) {
testingRoute := &route.Route{
ID: "testingRoute",
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
}
am, err := createRouterManager(t)
if err != nil {
t.Error("failed to create account manager")
}
account, err := initTestRouteAccount(t, am)
if err != nil {
t.Error("failed to init testing account")
}
account.Routes[testingRoute.ID] = testingRoute
err = am.Store.SaveAccount(account)
if err != nil {
t.Error("failed to save account")
}
err = am.DeleteRoute(account.Id, testingRoute.ID)
if err != nil {
t.Error("deleting route failed with error: ", err)
}
savedAccount, err := am.Store.GetAccount(account.Id)
if err != nil {
t.Error("failed to retrieve saved account with error: ", err)
}
_, found := savedAccount.Routes[testingRoute.ID]
if found {
t.Error("route shouldn't be found after delete")
}
}
func TestGetNetworkMap_RouteSync(t *testing.T) {
// no routes for peer in different groups
// no routes when route is deleted
baseRoute := &route.Route{
ID: "testingRoute",
Prefix: netip.MustParsePrefix("192.168.0.0/16"),
PrefixType: route.IPv4Prefix,
Peer: peer1Key,
Description: "super",
Masquerade: false,
Metric: 9999,
Enabled: true,
}
am, err := createRouterManager(t)
if err != nil {
t.Error("failed to create account manager")
}
account, err := initTestRouteAccount(t, am)
if err != nil {
t.Error("failed to init testing account")
}
newAccountRoutes, err := am.GetNetworkMap(peer1Key)
require.NoError(t, err)
require.Len(t, newAccountRoutes.Routes, 0, "new accounts should have no routes")
createdRoute, err := am.CreateRoute(account.Id, baseRoute.Prefix.String(), baseRoute.Peer,
baseRoute.Description, baseRoute.Masquerade, baseRoute.Metric, false)
require.NoError(t, err)
noDisabledRoutes, err := am.GetNetworkMap(peer1Key)
require.NoError(t, err)
require.Len(t, noDisabledRoutes.Routes, 0, "no routes for disabled routes")
enabledRoute := createdRoute.Copy()
enabledRoute.Enabled = true
err = am.SaveRoute(account.Id, enabledRoute)
require.NoError(t, err)
peer1Routes, err := am.GetNetworkMap(peer1Key)
require.NoError(t, err)
require.Len(t, peer1Routes.Routes, 1, "we should receive one route for peer1")
require.True(t, enabledRoute.IsEqual(peer1Routes.Routes[0]), "received route should be equal")
peer2Routes, err := am.GetNetworkMap(peer2Key)
require.NoError(t, err)
require.Len(t, peer2Routes.Routes, 1, "we should receive one route for peer2")
require.True(t, peer1Routes.Routes[0].IsEqual(peer2Routes.Routes[0]), "routes should be the same for peers in the same group")
newGroup := &Group{
ID: xid.New().String(),
Name: "peer1 group",
Peers: []string{peer1Key},
}
err = am.SaveGroup(account.Id, newGroup)
require.NoError(t, err)
rules, err := am.ListRules(account.Id)
require.NoError(t, err)
defaultRule := rules[0]
newRule := defaultRule.Copy()
newRule.ID = xid.New().String()
newRule.Name = "peer1 only"
newRule.Source = []string{newGroup.ID}
newRule.Destination = []string{newGroup.ID}
err = am.SaveRule(account.Id, newRule)
require.NoError(t, err)
err = am.DeleteRule(account.Id, defaultRule.ID)
require.NoError(t, err)
peer1GroupRoutes, err := am.GetNetworkMap(peer1Key)
require.NoError(t, err)
require.Len(t, peer1GroupRoutes.Routes, 1, "we should receive one route for peer1")
peer2GroupRoutes, err := am.GetNetworkMap(peer2Key)
require.NoError(t, err)
require.Len(t, peer2GroupRoutes.Routes, 0, "we should not receive routes for peer2")
err = am.DeleteRoute(account.Id, enabledRoute.ID)
require.NoError(t, err)
peer1DeletedRoute, err := am.GetNetworkMap(peer1Key)
require.NoError(t, err)
require.Len(t, peer1DeletedRoute.Routes, 0, "we should receive one route for peer1")
}
func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
store, err := createRouterStore(t)
if err != nil {
return nil, err
}
return BuildManager(store, NewPeersUpdateManager(), nil)
}
func createRouterStore(t *testing.T) (Store, error) {
dataDir := t.TempDir()
store, err := NewStore(dataDir)
if err != nil {
return nil, err
}
return store, nil
}
func initTestRouteAccount(t *testing.T, am *DefaultAccountManager) (*Account, error) {
peer1 := &Peer{
Key: peer1Key,
Name: "test-host1@netbird.io",
Meta: PeerSystemMeta{
Hostname: "test-host1@netbird.io",
GoOS: "linux",
Kernel: "Linux",
Core: "21.04",
Platform: "x86_64",
OS: "Ubuntu",
WtVersion: "development",
UIVersion: "development",
},
}
peer2 := &Peer{
Key: peer2Key,
Name: "test-host2@netbird.io",
Meta: PeerSystemMeta{
Hostname: "test-host2@netbird.io",
GoOS: "linux",
Kernel: "Linux",
Core: "21.04",
Platform: "x86_64",
OS: "Ubuntu",
WtVersion: "development",
UIVersion: "development",
},
}
accountID := "testingAcc"
userID := "testingUser"
domain := "example.com"
account := newAccountWithId(accountID, userID, domain)
err := am.Store.SaveAccount(account)
if err != nil {
return nil, err
}
_, err = am.AddPeer("", userID, peer1)
if err != nil {
return nil, err
}
_, err = am.AddPeer("", userID, peer2)
if err != nil {
return nil, err
}
return account, nil
}

View File

@ -1,5 +1,10 @@
package server
import (
"github.com/netbirdio/netbird/route"
"net/netip"
)
type Store interface {
GetPeer(peerKey string) (*Peer, error)
DeletePeer(accountId string, peerKey string) (*Peer, error)
@ -14,4 +19,6 @@ type Store interface {
GetAccountBySetupKey(setupKey string) (*Account, error)
GetAccountByPrivateDomain(domain string) (*Account, error)
SaveAccount(account *Account) error
GetPeerRoutes(peerKey string) ([]*route.Route, error)
GetRoutesByPrefix(accountID string, prefix netip.Prefix) ([]*route.Route, error)
}

119
route/route.go Normal file
View File

@ -0,0 +1,119 @@
package route
import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"net/netip"
)
// Windows has some limitation regarding metric size that differ from Unix-like systems.
// Because of that we are limiting the min and max metric size based on Windows limits:
// see based on info from https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/route_ws2008
const (
// MinMetric max metric input
MinMetric = 1
// MaxMetric max metric input
MaxMetric = 9999
)
const (
// InvalidPrefixString invalid prefix type string
InvalidPrefixString = "Invalid"
// IPv4PrefixString IPv4 prefix type string
IPv4PrefixString = "IPv4"
// IPv6PrefixString IPv6 prefix type string
IPv6PrefixString = "IPv6"
)
const (
// InvalidPrefix invalid prefix type
InvalidPrefix PrefixType = iota
// IPv4Prefix IPv4 prefix type
IPv4Prefix
// IPv6Prefix IPv6 prefix type
IPv6Prefix
)
// PrefixType route prefix type
type PrefixType int
// String returns prefix type string
func (p PrefixType) String() string {
switch p {
case IPv4Prefix:
return IPv4PrefixString
case IPv6Prefix:
return IPv6PrefixString
default:
return InvalidPrefixString
}
}
// ToPrefixType returns a prefix type
func ToPrefixType(prefix string) PrefixType {
switch prefix {
case IPv4PrefixString:
return IPv4Prefix
case IPv6PrefixString:
return IPv6Prefix
default:
return InvalidPrefix
}
}
// Route represents a route
type Route struct {
Prefix netip.Prefix
ID string
Description string
Peer string
PrefixType PrefixType
Masquerade bool
Metric int
Enabled bool
}
// Copy copies a route object
func (r *Route) Copy() *Route {
return &Route{
ID: r.ID,
Description: r.Description,
Prefix: r.Prefix,
PrefixType: r.PrefixType,
Peer: r.Peer,
Metric: r.Metric,
Masquerade: r.Masquerade,
Enabled: r.Enabled,
}
}
// IsEqual compares one route with the other
func (r *Route) IsEqual(other *Route) bool {
return other.ID == r.ID &&
other.Description == r.Description &&
other.Prefix == r.Prefix &&
other.PrefixType == r.PrefixType &&
other.Peer == r.Peer &&
other.Metric == r.Metric &&
other.Masquerade == r.Masquerade &&
other.Enabled == r.Enabled
}
// ParsePrefix Parses a prefix string and returns a netip.Prefix object and if is invalid, IPv4 or IPv6
func ParsePrefix(prefixString string) (PrefixType, netip.Prefix, error) {
prefix, err := netip.ParsePrefix(prefixString)
if err != nil {
return InvalidPrefix, netip.Prefix{}, err
}
masked := prefix.Masked()
if !masked.IsValid() {
return InvalidPrefix, netip.Prefix{}, status.Errorf(codes.InvalidArgument, "invalid range %s", prefixString)
}
if masked.Addr().Is6() {
return IPv6Prefix, masked, nil
}
return IPv4Prefix, masked, nil
}