add networks structure

This commit is contained in:
Pascal Fischer
2025-05-27 14:24:25 +02:00
parent ae0bb0011a
commit 8cf7b07be7
25 changed files with 675 additions and 86 deletions
+1 -1
View File
@@ -24,6 +24,7 @@ require (
github.com/stretchr/testify v1.10.0
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6
google.golang.org/grpc v1.67.3
gopkg.in/natefinch/lumberjack.v2 v2.0.0
gorm.io/driver/postgres v1.5.11
gorm.io/driver/sqlite v1.5.7
gorm.io/gorm v1.25.12
@@ -166,7 +167,6 @@ require (
google.golang.org/api v0.215.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
google.golang.org/protobuf v1.36.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/driver/mysql v1.5.7 // indirect
@@ -15,8 +15,11 @@ import (
const channelBufferSize = 100
type UpdateMessage struct {
Update *proto.SyncResponse
NetworkMap *types.NetworkMap
Update *proto.SyncResponse
NetworkMap *types.NetworkMap
PeerManager *nbpeer.Manager
PolicyManager *nbpeer.PolicyManager
GroupManager *nbpeer.GroupManager
}
type UpdateChannel struct {
+16
View File
@@ -0,0 +1,16 @@
package networks
import (
"context"
"management/internal/shared/db"
)
type Manager interface {
Using(tx db.Transaction) Manager
GetAllNetworks(ctx context.Context, strength db.LockingStrength, accountID, userID string) ([]*Network, error)
GetNetwork(ctx context.Context, strength db.LockingStrength, accountID, userID, networkID string) (*Network, error)
CreateNetwork(ctx context.Context, userID string, network *Network) (*Network, error)
UpdateNetwork(ctx context.Context, userID string, network *Network) (*Network, error)
DeleteNetwork(ctx context.Context, accountID, userID, networkID string) error
}
+1
View File
@@ -0,0 +1 @@
package manager
@@ -0,0 +1,145 @@
package manager
import (
"context"
"fmt"
"github.com/netbirdio/netbird/management/server/account"
"github.com/rs/xid"
"management/internal/modules/networks"
"management/internal/modules/networks/resources"
"management/internal/modules/networks/routers"
"management/internal/shared/db"
"management/internal/shared/errors"
"management/internal/shared/permissions"
"management/internal/shared/permissions/modules"
"management/internal/shared/permissions/operations"
)
type managerImpl struct {
repo Repository
permissionsManager permissions.Manager
resourcesManager resources.Manager
routersManager routers.Manager
}
func NewManager(store *db.Store, permissionsManager permissions.Manager, resourceManager resources.Manager, routersManager routers.Manager) resources.Manager {
repo := newRepository(store)
m := &managerImpl{repo: repo}
// api := newHandler(m, permissionsManager)
// api.RegisterEndpoints(router)
return m
}
func (m *managerImpl) GetAllNetworks(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID, userID string) ([]*networks.Network, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
if err != nil {
return nil, errors.NewPermissionValidationError(err)
}
if !ok {
return nil, errors.NewPermissionDeniedError()
}
return m.repo.GetAccountNetworks(tx, strength, accountID)
}
func (m *managerImpl) CreateNetwork(ctx context.Context, tx db.Transaction, userID string, network *networks.Network) (*networks.Network, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, network.AccountID, userID, modules.Networks, operations.Write)
if err != nil {
return nil, errors.NewPermissionValidationError(err)
}
if !ok {
return nil, errors.NewPermissionDeniedError()
}
network.ID = xid.New().String()
err = m.repo.CreateNetwork(tx, network)
if err != nil {
return nil, fmt.Errorf("failed to save network: %w", err)
}
// m.accountManager.StoreEvent(ctx, userID, network.ID, network.AccountID, activity.NetworkCreated, network.EventMeta())
return network, nil
}
func (m *managerImpl) GetNetwork(ctx context.Context, tx db.Transaction, strength db.LockingStrength, accountID, userID, networkID string) (*networks.Network, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
if err != nil {
return nil, errors.NewPermissionValidationError(err)
}
if !ok {
return nil, errors.NewPermissionDeniedError()
}
return m.repo.GetNetworkByID(tx, strength, accountID, networkID)
}
func (m *managerImpl) UpdateNetwork(ctx context.Context, tx db.Transaction, userID string, network *networks.Network) (*networks.Network, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, network.AccountID, userID, modules.Networks, operations.Write)
if err != nil {
return nil, errors.NewPermissionValidationError(err)
}
if !ok {
return nil, errors.NewPermissionDeniedError()
}
_, err = m.repo.GetNetworkByID(tx, db.LockingStrengthUpdate, network.AccountID, network.ID)
if err != nil {
return nil, fmt.Errorf("failed to get network: %w", err)
}
// m.accountManager.StoreEvent(ctx, userID, network.ID, network.AccountID, activity.NetworkUpdated, network.EventMeta())
return network, m.repo.UpdateNetwork(tx, network)
}
func (m *managerImpl) DeleteNetwork(ctx context.Context, tx db.Transaction, accountID, userID, networkID string) error {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Write)
if err != nil {
return errors.NewPermissionValidationError(err)
}
if !ok {
return errors.NewPermissionDeniedError()
}
network, err := m.repo.GetNetworkByID(tx, db.LockingStrengthUpdate, accountID, networkID)
if err != nil {
return fmt.Errorf("failed to get network: %w", err)
}
resources, err := m.resourcesManager.GetNetworkResourcesByNetID(ctx, tx, db.LockingStrengthUpdate, accountID, userID, networkID)
if err != nil {
return fmt.Errorf("failed to get resources in network: %w", err)
}
for _, resource := range resources {
err = m.resourcesManager.DeleteResource(ctx, tx, accountID, userID, networkID, resource.ID)
if err != nil {
return fmt.Errorf("failed to delete resource: %w", err)
}
}
routers, err := m.routersManager.GetNetworkRoutersByNetID(ctx, tx, db.LockingStrengthUpdate, accountID, userID, networkID)
if err != nil {
return fmt.Errorf("failed to get routers in network: %w", err)
}
for _, router := range routers {
err := m.routersManager.DeleteRouter(ctx, tx, accountID, userID, networkID, router.ID)
if err != nil {
return fmt.Errorf("failed to delete router: %w", err)
}
}
err = m.repo.DeleteNetwork(tx, &networks.Network{ID: networkID})
if err != nil {
return fmt.Errorf("failed to delete network: %w", err)
}
// TODO: send delete event with network
return nil
}
@@ -0,0 +1 @@
package manager
@@ -0,0 +1,57 @@
package manager
import (
"management/internal/modules/networks"
"management/internal/shared/db"
)
type Repository interface {
RunInTx(fn func(tx db.Transaction) error) error
CreateNetwork(tx db.Transaction, network *networks.Network) error
UpdateNetwork(tx db.Transaction, network *networks.Network) error
DeleteNetwork(tx db.Transaction, network *networks.Network) error
GetNetworkByID(tx db.Transaction, lockingStrength db.LockingStrength, accountID, networkID string) (*networks.Network, error)
GetAccountNetworks(tx db.Transaction, lockingStrength db.LockingStrength, accountID string) ([]*networks.Network, error)
}
type repository struct {
store *db.Store
}
func newRepository(s *db.Store) Repository {
return &repository{store: s}
}
func (r *repository) RunInTx(fn func(tx db.Transaction) error) error {
return r.store.RunInTx(fn)
}
func (r *repository) CreateNetwork(tx db.Transaction, network *networks.Network) error {
return r.store.Create(tx, network)
}
func (r *repository) UpdateNetwork(tx db.Transaction, network *networks.Network) error {
return r.store.Update(tx, network)
}
func (r *repository) DeleteNetwork(tx db.Transaction, network *networks.Network) error {
return r.store.Delete(tx, network)
}
func (r *repository) GetNetworkByID(tx db.Transaction, lockingStrength db.LockingStrength, accountID, networkID string) (*networks.Network, error) {
var network networks.Network
err := r.store.GetOne(tx, lockingStrength, &network, "account_id = ? AND network_id = ?", accountID, networkID)
if err != nil {
return nil, err
}
return &network, nil
}
func (r *repository) GetAccountNetworks(tx db.Transaction, lockingStrength db.LockingStrength, accountID string) ([]*networks.Network, error) {
var networks []*networks.Network
err := r.store.GetMany(tx, lockingStrength, &networks, "account_id = ?", accountID)
if err != nil {
return nil, err
}
return networks, nil
}
+55
View File
@@ -0,0 +1,55 @@
package networks
import (
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/rs/xid"
)
type Network struct {
ID string
AccountID string
Name string
Description string
}
func NewNetwork(accountId, name, description string) *Network {
return &Network{
ID: xid.New().String(),
AccountID: accountId,
Name: name,
Description: description,
}
}
func (n *Network) ToAPIResponse(routerIDs []string, resourceIDs []string, routingPeersCount int, policyIDs []string) *api.Network {
return &api.Network{
Id: n.ID,
Name: n.Name,
Description: &n.Description,
Routers: routerIDs,
Resources: resourceIDs,
RoutingPeersCount: routingPeersCount,
Policies: policyIDs,
}
}
func (n *Network) FromAPIRequest(req *api.NetworkRequest) {
n.Name = req.Name
if req.Description != nil {
n.Description = *req.Description
}
}
// Copy returns a copy of a posture checks.
func (n *Network) Copy() *Network {
return &Network{
ID: n.ID,
AccountID: n.AccountID,
Name: n.Name,
Description: n.Description,
}
}
func (n *Network) EventMeta() map[string]any {
return map[string]any{"name": n.Name}
}
@@ -0,0 +1 @@
package networks
@@ -0,0 +1,12 @@
package resources
import (
"context"
"management/internal/shared/db"
)
type Manager interface {
DeleteResource(ctx context.Context, tx db.Transaction, accountID, userID, networkID, resourceID string) error
GetNetworkResourcesByNetID(ctx context.Context, tx db.Transaction, lockingStrength db.LockingStrength, accountID, userID, networkID string) ([]*NetworkResource, error)
}
@@ -0,0 +1 @@
package manager
@@ -0,0 +1 @@
package manager
@@ -0,0 +1 @@
package manager
@@ -0,0 +1,61 @@
package manager
import (
"management/internal/modules/networks"
"management/internal/shared/db"
)
type Repository interface {
RunInTx(fn func(tx db.Transaction) error) error
Using(tx db.Transaction) Repository
CreateNetwork(tx db.Transaction, network *networks.Network) error
UpdateNetwork(tx db.Transaction, network *networks.Network) error
GetNetworkByID(tx db.Transaction, lockingStrength db.LockingStrength, accountID, networkID string) (*networks.Network, error)
GetAccountNetworks(tx db.Transaction, lockingStrength db.LockingStrength, accountID string) ([]*networks.Network, error)
}
type repository struct {
store *db.Store
}
func newRepository(s *db.Store) Repository {
return &repository{store: s}
}
func (r *repository) RunInTx(fn func(tx db.Transaction) error) error {
return r.store.RunInTx(fn)
}
func (r *repository) Using(tx db.Transaction) Repository {
return &repository{store: r.store.Using(tx)}
}
func (r *repository) DeleteNetwork(tx db.Transaction, network *networks.Network) error {
return r.store.Create(tx, network)
}
func (r *repository) UpdateNetwork(tx db.Transaction, network *networks.Network) error {
return r.store.Update(tx, network)
}
func (r *repository) GetNetworkByID(tx db.Transaction, lockingStrength db.LockingStrength, accountID, networkID string) (*networks.Network, error) {
var network networks.Network
err := r.store.GetOne(tx, lockingStrength, &network, "account_id = ? AND network_id = ?", accountID, networkID)
if err != nil {
return nil, err
}
return &network, nil
}
func (r *repository) GetAccountNetworks(tx db.Transaction, lockingStrength db.LockingStrength, accountID string) ([]*networks.Network, error) {
var networks []*networks.Network
err := r.store.GetMany(tx, lockingStrength, &networks, "account_id = ?", accountID)
if err != nil {
return nil, err
}
return networks, nil
}
func (r *repository) CreateNetwork(tx db.Transaction, network *networks.Network) error {
return r.store.Create(tx, network)
}
@@ -0,0 +1,173 @@
package resources
import (
"errors"
"fmt"
"net/netip"
"regexp"
"github.com/netbirdio/netbird/management/server/http/api"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/route"
"github.com/rs/xid"
"management/internal/modules/networks"
"management/internal/modules/networks/routers"
)
type NetworkResourceType string
const (
Host NetworkResourceType = "host"
Subnet NetworkResourceType = "subnet"
Domain NetworkResourceType = "domain"
)
func (p NetworkResourceType) String() string {
return string(p)
}
type NetworkResource struct {
ID string `gorm:"primaryKey"`
NetworkID string `gorm:"index"`
AccountID string `gorm:"index"`
Name string
Description string
Type NetworkResourceType
Address string `gorm:"-"`
GroupIDs []string `gorm:"-"`
Domain string
Prefix netip.Prefix `gorm:"serializer:json"`
Enabled bool
}
func NewNetworkResource(accountID, networkID, name, description, address string, groupIDs []string, enabled bool) (*NetworkResource, error) {
resourceType, domain, prefix, err := GetResourceType(address)
if err != nil {
return nil, fmt.Errorf("invalid address: %w", err)
}
return &NetworkResource{
ID: xid.New().String(),
AccountID: accountID,
NetworkID: networkID,
Name: name,
Description: description,
Type: resourceType,
Address: address,
Domain: domain,
Prefix: prefix,
GroupIDs: groupIDs,
Enabled: enabled,
}, nil
}
func (n *NetworkResource) ToAPIResponse(groups []api.GroupMinimum) *api.NetworkResource {
addr := n.Prefix.String()
if n.Type == Domain {
addr = n.Domain
}
return &api.NetworkResource{
Id: n.ID,
Name: n.Name,
Description: &n.Description,
Type: api.NetworkResourceType(n.Type.String()),
Address: addr,
Groups: groups,
Enabled: n.Enabled,
}
}
func (n *NetworkResource) FromAPIRequest(req *api.NetworkResourceRequest) {
n.Name = req.Name
if req.Description != nil {
n.Description = *req.Description
}
n.Address = req.Address
n.GroupIDs = req.Groups
n.Enabled = req.Enabled
}
func (n *NetworkResource) Copy() *NetworkResource {
return &NetworkResource{
ID: n.ID,
AccountID: n.AccountID,
NetworkID: n.NetworkID,
Name: n.Name,
Description: n.Description,
Type: n.Type,
Address: n.Address,
Domain: n.Domain,
Prefix: n.Prefix,
GroupIDs: n.GroupIDs,
Enabled: n.Enabled,
}
}
func (n *NetworkResource) ToRoute(peer *nbpeer.Peer, router *routers.NetworkRouter) *route.Route {
r := &route.Route{
ID: route.ID(fmt.Sprintf("%s:%s", n.ID, peer.ID)),
AccountID: n.AccountID,
KeepRoute: true,
NetID: route.NetID(n.Name),
Description: n.Description,
Peer: peer.Key,
PeerID: peer.ID,
PeerGroups: nil,
Masquerade: router.Masquerade,
Metric: router.Metric,
Enabled: n.Enabled,
Groups: nil,
AccessControlGroups: nil,
}
if n.Type == Host || n.Type == Subnet {
r.Network = n.Prefix
r.NetworkType = route.IPv4Network
if n.Prefix.Addr().Is6() {
r.NetworkType = route.IPv6Network
}
}
if n.Type == Domain {
// domainList, err := nbDomain.FromStringList([]string{n.Domain})
// if err != nil {
// return nil
// }
// r.Domains = domainList
r.NetworkType = route.DomainNetwork
// add default placeholder for domain network
r.Network = netip.PrefixFrom(netip.AddrFrom4([4]byte{192, 0, 2, 0}), 32)
}
return r
}
func (n *NetworkResource) EventMeta(network *networks.Network) map[string]any {
return map[string]any{"name": n.Name, "type": n.Type, "network_name": network.Name, "network_id": network.ID}
}
// GetResourceType returns the type of the resource based on the address
func GetResourceType(address string) (NetworkResourceType, string, netip.Prefix, error) {
if prefix, err := netip.ParsePrefix(address); err == nil {
if prefix.Bits() == 32 || prefix.Bits() == 128 {
return Host, "", prefix, nil
}
return Subnet, "", prefix, nil
}
if ip, err := netip.ParseAddr(address); err == nil {
return Host, "", netip.PrefixFrom(ip, ip.BitLen()), nil
}
domainRegex := regexp.MustCompile(`^(\*\.)?([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$`)
if domainRegex.MatchString(address) {
return Domain, address, netip.Prefix{}, nil
}
return "", "", netip.Prefix{}, errors.New("not a valid host, subnet, or domain")
}
@@ -0,0 +1,13 @@
package routers
import (
"context"
"management/internal/shared/db"
)
type Manager interface {
Using(tx db.Transaction) Manager
GetNetworkRoutersByNetID(ctx context.Context, tx db.Transaction, lockingStrength db.LockingStrength, accountID, userID, networkID string) ([]*NetworkRouter, error)
DeleteRouter(ctx context.Context, tx db.Transaction, accountID, userID, networkID, routerID string) error
}
@@ -0,0 +1 @@
package manager
@@ -0,0 +1 @@
package manager
@@ -0,0 +1 @@
package manager
@@ -0,0 +1 @@
package manager

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