move to reverse proxy and update api

This commit is contained in:
pascal
2026-01-27 15:34:01 +01:00
parent 4b89427447
commit 8dd22f3a4f
21 changed files with 1101 additions and 733 deletions

View File

@@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
nbcontext "github.com/netbirdio/netbird/management/server/context" nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/shared/management/http/api" "github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util" "github.com/netbirdio/netbird/shared/management/http/util"

View File

@@ -4,7 +4,7 @@ import (
"context" "context"
"testing" "testing"
"github.com/netbirdio/netbird/management/internals/modules/services/domains" "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/domains"
) )
type resolver struct { type resolver struct {

View File

@@ -0,0 +1,13 @@
package reverseproxy
import (
"context"
)
type Manager interface {
GetAllReverseProxies(ctx context.Context, accountID, userID string) ([]*ReverseProxy, error)
GetReverseProxy(ctx context.Context, accountID, userID, reverseProxyID string) (*ReverseProxy, error)
CreateReverseProxy(ctx context.Context, accountID, userID string, reverseProxy *ReverseProxy) (*ReverseProxy, error)
UpdateReverseProxy(ctx context.Context, accountID, userID string, reverseProxy *ReverseProxy) (*ReverseProxy, error)
DeleteReverseProxy(ctx context.Context, accountID, userID, reverseProxyID string) error
}

View File

@@ -0,0 +1,161 @@
package manager
import (
"encoding/json"
"net/http"
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
"github.com/netbirdio/netbird/shared/management/status"
)
type handler struct {
manager reverseproxy.Manager
}
func RegisterEndpoints(router *mux.Router, manager reverseproxy.Manager) {
h := &handler{
manager: manager,
}
router.HandleFunc("/reverse-proxies", h.getAllReverseProxies).Methods("GET", "OPTIONS")
router.HandleFunc("/reverse-proxies", h.createReverseProxy).Methods("POST", "OPTIONS")
router.HandleFunc("/reverse-proxies/{reverseProxyId}", h.getReverseProxy).Methods("GET", "OPTIONS")
router.HandleFunc("/reverse-proxies/{reverseProxyId}", h.updateReverseProxy).Methods("PUT", "OPTIONS")
router.HandleFunc("/reverse-proxies/{reverseProxyId}", h.deleteReverseProxy).Methods("DELETE", "OPTIONS")
}
func (h *handler) getAllReverseProxies(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
allReverseProxies, err := h.manager.GetAllReverseProxies(r.Context(), userAuth.AccountId, userAuth.UserId)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
apiReverseProxies := make([]*api.ReverseProxy, 0, len(allReverseProxies))
for _, reverseProxy := range allReverseProxies {
apiReverseProxies = append(apiReverseProxies, reverseProxy.ToAPIResponse())
}
util.WriteJSONObject(r.Context(), w, apiReverseProxies)
}
func (h *handler) createReverseProxy(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
var req api.PostApiReverseProxyJSONRequestBody
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
return
}
reverseProxy := new(reverseproxy.ReverseProxy)
reverseProxy.FromAPIRequest(&req)
if err = reverseProxy.Validate(); err != nil {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "%s", err.Error()), w)
return
}
createdReverseProxy, err := h.manager.CreateReverseProxy(r.Context(), userAuth.AccountId, userAuth.UserId, reverseProxy)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
util.WriteJSONObject(r.Context(), w, createdReverseProxy.ToAPIResponse())
}
func (h *handler) getReverseProxy(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
reverseProxyID := mux.Vars(r)["proxyId"]
if reverseProxyID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "reverse proxy ID is required"), w)
return
}
reverseProxy, err := h.manager.GetReverseProxy(r.Context(), userAuth.AccountId, userAuth.UserId, reverseProxyID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
util.WriteJSONObject(r.Context(), w, reverseProxy.ToAPIResponse())
}
func (h *handler) updateReverseProxy(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
reverseProxyID := mux.Vars(r)["proxyId"]
if reverseProxyID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "reverse proxy ID is required"), w)
return
}
var req api.PutApiReverseProxyProxyIdJSONRequestBody
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
return
}
reverseProxy := new(reverseproxy.ReverseProxy)
reverseProxy.ID = reverseProxyID
reverseProxy.FromAPIRequest(&req)
if err = reverseProxy.Validate(); err != nil {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "%s", err.Error()), w)
return
}
updatedReverseProxy, err := h.manager.UpdateReverseProxy(r.Context(), userAuth.AccountId, userAuth.UserId, reverseProxy)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
util.WriteJSONObject(r.Context(), w, updatedReverseProxy.ToAPIResponse())
}
func (h *handler) deleteReverseProxy(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
reverseProxyID := mux.Vars(r)["proxyId"]
if reverseProxyID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "reverse proxy ID is required"), w)
return
}
if err := h.manager.DeleteReverseProxy(r.Context(), userAuth.AccountId, userAuth.UserId, reverseProxyID); err != nil {
util.WriteError(r.Context(), err, w)
return
}
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
}

View File

@@ -0,0 +1,171 @@
package manager
import (
"context"
"fmt"
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/permissions/modules"
"github.com/netbirdio/netbird/management/server/permissions/operations"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/shared/management/status"
)
type managerImpl struct {
store store.Store
accountManager account.Manager
permissionsManager permissions.Manager
}
func NewManager(store store.Store, accountManager account.Manager, permissionsManager permissions.Manager) reverseproxy.Manager {
return &managerImpl{
store: store,
accountManager: accountManager,
permissionsManager: permissionsManager,
}
}
func (m *managerImpl) GetAllReverseProxies(ctx context.Context, accountID, userID string) ([]*reverseproxy.ReverseProxy, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
if err != nil {
return nil, status.NewPermissionValidationError(err)
}
if !ok {
return nil, status.NewPermissionDeniedError()
}
return m.store.GetAccountReverseProxies(ctx, store.LockingStrengthNone, accountID)
}
func (m *managerImpl) GetReverseProxy(ctx context.Context, accountID, userID, reverseProxyID string) (*reverseproxy.ReverseProxy, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
if err != nil {
return nil, status.NewPermissionValidationError(err)
}
if !ok {
return nil, status.NewPermissionDeniedError()
}
return m.store.GetReverseProxyByID(ctx, store.LockingStrengthNone, accountID, reverseProxyID)
}
func (m *managerImpl) CreateReverseProxy(ctx context.Context, accountID, userID string, reverseProxy *reverseproxy.ReverseProxy) (*reverseproxy.ReverseProxy, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Create)
if err != nil {
return nil, status.NewPermissionValidationError(err)
}
if !ok {
return nil, status.NewPermissionDeniedError()
}
authConfig := reverseProxy.Auth
reverseProxy = reverseproxy.NewReverseProxy(accountID, reverseProxy.Name, reverseProxy.Domain, reverseProxy.Targets, reverseProxy.Enabled)
reverseProxy.Auth = authConfig
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
// Check for duplicate domain
existingReverseProxy, err := transaction.GetReverseProxyByDomain(ctx, accountID, reverseProxy.Domain)
if err != nil {
if sErr, ok := status.FromError(err); !ok || sErr.Type() != status.NotFound {
return fmt.Errorf("failed to check existing reverse proxy: %w", err)
}
}
if existingReverseProxy != nil {
return status.Errorf(status.AlreadyExists, "reverse proxy with domain %s already exists", reverseProxy.Domain)
}
if err = transaction.CreateReverseProxy(ctx, reverseProxy); err != nil {
return fmt.Errorf("failed to create reverse proxy: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
m.accountManager.StoreEvent(ctx, userID, reverseProxy.ID, accountID, activity.ReverseProxyCreated, reverseProxy.EventMeta())
return reverseProxy, nil
}
func (m *managerImpl) UpdateReverseProxy(ctx context.Context, accountID, userID string, reverseProxy *reverseproxy.ReverseProxy) (*reverseproxy.ReverseProxy, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Update)
if err != nil {
return nil, status.NewPermissionValidationError(err)
}
if !ok {
return nil, status.NewPermissionDeniedError()
}
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
// Get existing reverse proxy
existingReverseProxy, err := transaction.GetReverseProxyByID(ctx, store.LockingStrengthUpdate, accountID, reverseProxy.ID)
if err != nil {
return err
}
// Check if domain changed and if it conflicts
if existingReverseProxy.Domain != reverseProxy.Domain {
conflictReverseProxy, err := transaction.GetReverseProxyByDomain(ctx, accountID, reverseProxy.Domain)
if err != nil {
if sErr, ok := status.FromError(err); !ok || sErr.Type() != status.NotFound {
return fmt.Errorf("failed to check existing reverse proxy: %w", err)
}
}
if conflictReverseProxy != nil && conflictReverseProxy.ID != reverseProxy.ID {
return status.Errorf(status.AlreadyExists, "reverse proxy with domain %s already exists", reverseProxy.Domain)
}
}
if err = transaction.UpdateReverseProxy(ctx, reverseProxy); err != nil {
return fmt.Errorf("failed to update reverse proxy: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
m.accountManager.StoreEvent(ctx, userID, reverseProxy.ID, accountID, activity.ReverseProxyUpdated, reverseProxy.EventMeta())
return reverseProxy, nil
}
func (m *managerImpl) DeleteReverseProxy(ctx context.Context, accountID, userID, reverseProxyID string) error {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Delete)
if err != nil {
return status.NewPermissionValidationError(err)
}
if !ok {
return status.NewPermissionDeniedError()
}
var reverseProxy *reverseproxy.ReverseProxy
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
var err error
reverseProxy, err = transaction.GetReverseProxyByID(ctx, store.LockingStrengthUpdate, accountID, reverseProxyID)
if err != nil {
return err
}
if err = transaction.DeleteReverseProxy(ctx, accountID, reverseProxyID); err != nil {
return fmt.Errorf("failed to delete reverse proxy: %w", err)
}
return nil
})
if err != nil {
return err
}
m.accountManager.StoreEvent(ctx, userID, reverseProxyID, accountID, activity.ReverseProxyDeleted, reverseProxy.EventMeta())
return nil
}

View File

@@ -0,0 +1,196 @@
package reverseproxy
import (
"errors"
"github.com/rs/xid"
"github.com/netbirdio/netbird/shared/management/http/api"
)
type Target struct {
Path *string `json:"path,omitempty"`
Host string `json:"host"`
Port int `json:"port"`
Protocol string `json:"protocol"`
TargetId string `json:"target_id"`
TargetType string `json:"target_type"`
Enabled bool `json:"enabled"`
}
type PasswordAuthConfig struct {
Enabled bool `json:"enabled"`
Password string `json:"password"`
}
type PINAuthConfig struct {
Enabled bool `json:"enabled"`
Pin string `json:"pin"`
}
type BearerAuthConfig struct {
Enabled bool `json:"enabled"`
DistributionGroups []string `json:"distribution_groups,omitempty" gorm:"serializer:json"`
}
type LinkAuthConfig struct {
Enabled bool `json:"enabled"`
}
type AuthConfig struct {
PasswordAuth *PasswordAuthConfig `json:"password_auth,omitempty" gorm:"serializer:json"`
PinAuth *PINAuthConfig `json:"pin_auth,omitempty" gorm:"serializer:json"`
BearerAuth *BearerAuthConfig `json:"bearer_auth,omitempty" gorm:"serializer:json"`
LinkAuth *LinkAuthConfig `json:"link_auth,omitempty" gorm:"serializer:json"`
}
type ReverseProxy struct {
ID string `gorm:"primaryKey"`
AccountID string `gorm:"index"`
Name string
Domain string `gorm:"index"`
Targets []Target `gorm:"serializer:json"`
Enabled bool
Auth AuthConfig `gorm:"serializer:json"`
}
func NewReverseProxy(accountID, name, domain string, targets []Target, enabled bool) *ReverseProxy {
return &ReverseProxy{
ID: xid.New().String(),
AccountID: accountID,
Name: name,
Domain: domain,
Targets: targets,
Enabled: enabled,
}
}
func (r *ReverseProxy) ToAPIResponse() *api.ReverseProxy {
authConfig := api.ReverseProxyAuthConfig{}
if r.Auth.PasswordAuth != nil {
authConfig.PasswordAuth = &api.PasswordAuthConfig{
Enabled: r.Auth.PasswordAuth.Enabled,
Password: r.Auth.PasswordAuth.Password,
}
}
if r.Auth.PinAuth != nil {
authConfig.PinAuth = &api.PINAuthConfig{
Enabled: r.Auth.PinAuth.Enabled,
Pin: r.Auth.PinAuth.Pin,
}
}
if r.Auth.BearerAuth != nil {
authConfig.BearerAuth = &api.BearerAuthConfig{
Enabled: r.Auth.BearerAuth.Enabled,
DistributionGroups: &r.Auth.BearerAuth.DistributionGroups,
}
}
if r.Auth.LinkAuth != nil {
authConfig.LinkAuth = &api.LinkAuthConfig{
Enabled: r.Auth.LinkAuth.Enabled,
}
}
// Convert internal targets to API targets
apiTargets := make([]api.ReverseProxyTarget, 0, len(r.Targets))
for _, target := range r.Targets {
apiTargets = append(apiTargets, api.ReverseProxyTarget{
Path: target.Path,
Host: target.Host,
Port: target.Port,
Protocol: api.ReverseProxyTargetProtocol(target.Protocol),
TargetId: target.TargetId,
TargetType: api.ReverseProxyTargetTargetType(target.TargetType),
Enabled: target.Enabled,
})
}
return &api.ReverseProxy{
Id: r.ID,
Name: r.Name,
Domain: r.Domain,
Targets: apiTargets,
Enabled: r.Enabled,
Auth: authConfig,
}
}
func (r *ReverseProxy) FromAPIRequest(req *api.ReverseProxyRequest) {
r.Name = req.Name
r.Domain = req.Domain
// Convert API targets to internal targets
targets := make([]Target, 0, len(req.Targets))
for _, apiTarget := range req.Targets {
targets = append(targets, Target{
Path: apiTarget.Path,
Host: apiTarget.Host,
Port: apiTarget.Port,
Protocol: string(apiTarget.Protocol),
TargetId: apiTarget.TargetId,
TargetType: string(apiTarget.TargetType),
Enabled: apiTarget.Enabled,
})
}
r.Targets = targets
r.Enabled = req.Enabled
if req.Auth.PasswordAuth != nil {
r.Auth.PasswordAuth = &PasswordAuthConfig{
Enabled: req.Auth.PasswordAuth.Enabled,
Password: req.Auth.PasswordAuth.Password,
}
}
if req.Auth.PinAuth != nil {
r.Auth.PinAuth = &PINAuthConfig{
Enabled: req.Auth.PinAuth.Enabled,
Pin: req.Auth.PinAuth.Pin,
}
}
if req.Auth.BearerAuth != nil {
bearerAuth := &BearerAuthConfig{
Enabled: req.Auth.BearerAuth.Enabled,
}
if req.Auth.BearerAuth.DistributionGroups != nil {
bearerAuth.DistributionGroups = *req.Auth.BearerAuth.DistributionGroups
}
r.Auth.BearerAuth = bearerAuth
}
if req.Auth.LinkAuth != nil {
r.Auth.LinkAuth = &LinkAuthConfig{
Enabled: req.Auth.LinkAuth.Enabled,
}
}
}
func (r *ReverseProxy) Validate() error {
if r.Name == "" {
return errors.New("reverse proxy name is required")
}
if len(r.Name) > 255 {
return errors.New("reverse proxy name exceeds maximum length of 255 characters")
}
if r.Domain == "" {
return errors.New("reverse proxy domain is required")
}
if len(r.Targets) == 0 {
return errors.New("at least one target is required")
}
return nil
}
func (r *ReverseProxy) EventMeta() map[string]any {
return map[string]any{"name": r.Name, "domain": r.Domain}
}

View File

@@ -1,13 +0,0 @@
package services
import (
"context"
)
type Manager interface {
GetAllServices(ctx context.Context, accountID, userID string) ([]*Service, error)
GetService(ctx context.Context, accountID, userID, serviceID string) (*Service, error)
CreateService(ctx context.Context, accountID, userID string, service *Service) (*Service, error)
UpdateService(ctx context.Context, accountID, userID string, service *Service) (*Service, error)
DeleteService(ctx context.Context, accountID, userID, serviceID string) error
}

View File

@@ -1,161 +0,0 @@
package manager
import (
"encoding/json"
"net/http"
"github.com/gorilla/mux"
"github.com/netbirdio/netbird/management/internals/modules/services"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"
"github.com/netbirdio/netbird/shared/management/status"
)
type handler struct {
manager services.Manager
}
func RegisterEndpoints(router *mux.Router, manager services.Manager) {
h := &handler{
manager: manager,
}
router.HandleFunc("/services", h.getAllServices).Methods("GET", "OPTIONS")
router.HandleFunc("/services", h.createService).Methods("POST", "OPTIONS")
router.HandleFunc("/services/{serviceId}", h.getService).Methods("GET", "OPTIONS")
router.HandleFunc("/services/{serviceId}", h.updateService).Methods("PUT", "OPTIONS")
router.HandleFunc("/services/{serviceId}", h.deleteService).Methods("DELETE", "OPTIONS")
}
func (h *handler) getAllServices(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
allServices, err := h.manager.GetAllServices(r.Context(), userAuth.AccountId, userAuth.UserId)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
apiServices := make([]*api.Service, 0, len(allServices))
for _, service := range allServices {
apiServices = append(apiServices, service.ToAPIResponse())
}
util.WriteJSONObject(r.Context(), w, apiServices)
}
func (h *handler) createService(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
var req api.PostApiServicesJSONRequestBody
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
return
}
service := new(services.Service)
service.FromAPIRequest(&req)
if err = service.Validate(); err != nil {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "%s", err.Error()), w)
return
}
createdService, err := h.manager.CreateService(r.Context(), userAuth.AccountId, userAuth.UserId, service)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
util.WriteJSONObject(r.Context(), w, createdService.ToAPIResponse())
}
func (h *handler) getService(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
serviceID := mux.Vars(r)["serviceId"]
if serviceID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "service ID is required"), w)
return
}
service, err := h.manager.GetService(r.Context(), userAuth.AccountId, userAuth.UserId, serviceID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
util.WriteJSONObject(r.Context(), w, service.ToAPIResponse())
}
func (h *handler) updateService(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
serviceID := mux.Vars(r)["serviceId"]
if serviceID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "service ID is required"), w)
return
}
var req api.PutApiServicesServiceIdJSONRequestBody
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
return
}
service := new(services.Service)
service.ID = serviceID
service.FromAPIRequest(&req)
if err = service.Validate(); err != nil {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "%s", err.Error()), w)
return
}
updatedService, err := h.manager.UpdateService(r.Context(), userAuth.AccountId, userAuth.UserId, service)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
util.WriteJSONObject(r.Context(), w, updatedService.ToAPIResponse())
}
func (h *handler) deleteService(w http.ResponseWriter, r *http.Request) {
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
serviceID := mux.Vars(r)["serviceId"]
if serviceID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "service ID is required"), w)
return
}
if err := h.manager.DeleteService(r.Context(), userAuth.AccountId, userAuth.UserId, serviceID); err != nil {
util.WriteError(r.Context(), err, w)
return
}
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
}

View File

@@ -1,199 +0,0 @@
package manager
import (
"context"
"fmt"
"github.com/netbirdio/netbird/management/internals/modules/services"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/permissions/modules"
"github.com/netbirdio/netbird/management/server/permissions/operations"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/shared/management/status"
)
type managerImpl struct {
store store.Store
accountManager account.Manager
permissionsManager permissions.Manager
}
func NewManager(store store.Store, accountManager account.Manager, permissionsManager permissions.Manager) services.Manager {
return &managerImpl{
store: store,
accountManager: accountManager,
permissionsManager: permissionsManager,
}
}
func (m *managerImpl) GetAllServices(ctx context.Context, accountID, userID string) ([]*services.Service, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
if err != nil {
return nil, status.NewPermissionValidationError(err)
}
if !ok {
return nil, status.NewPermissionDeniedError()
}
return m.store.GetAccountServices(ctx, store.LockingStrengthNone, accountID)
}
func (m *managerImpl) GetService(ctx context.Context, accountID, userID, serviceID string) (*services.Service, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
if err != nil {
return nil, status.NewPermissionValidationError(err)
}
if !ok {
return nil, status.NewPermissionDeniedError()
}
return m.store.GetServiceByID(ctx, store.LockingStrengthNone, accountID, serviceID)
}
func (m *managerImpl) CreateService(ctx context.Context, accountID, userID string, service *services.Service) (*services.Service, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Create)
if err != nil {
return nil, status.NewPermissionValidationError(err)
}
if !ok {
return nil, status.NewPermissionDeniedError()
}
// Store auth config before creating new service
authType := service.AuthType
authBasicUsername := service.AuthBasicUsername
authBasicPassword := service.AuthBasicPassword
authPINValue := service.AuthPINValue
authPINHeader := service.AuthPINHeader
authBearerEnabled := service.AuthBearerEnabled
service = services.NewService(accountID, service.Name, service.Description, service.Domain, service.Targets, service.DistributionGroups, service.Enabled, service.Exposed)
// Restore auth config
service.AuthType = authType
service.AuthBasicUsername = authBasicUsername
service.AuthBasicPassword = authBasicPassword
service.AuthPINValue = authPINValue
service.AuthPINHeader = authPINHeader
service.AuthBearerEnabled = authBearerEnabled
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
// Check for duplicate domain
existingService, err := transaction.GetServiceByDomain(ctx, accountID, service.Domain)
if err != nil {
if sErr, ok := status.FromError(err); !ok || sErr.Type() != status.NotFound {
return fmt.Errorf("failed to check existing service: %w", err)
}
}
if existingService != nil {
return status.Errorf(status.AlreadyExists, "service with domain %s already exists", service.Domain)
}
// Validate distribution groups exist
for _, groupID := range service.DistributionGroups {
_, err = transaction.GetGroupByID(ctx, store.LockingStrengthNone, accountID, groupID)
if err != nil {
return status.Errorf(status.InvalidArgument, "%s", err.Error())
}
}
if err = transaction.CreateService(ctx, service); err != nil {
return fmt.Errorf("failed to create service: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
m.accountManager.StoreEvent(ctx, userID, service.ID, accountID, activity.ServiceCreated, service.EventMeta())
return service, nil
}
func (m *managerImpl) UpdateService(ctx context.Context, accountID, userID string, service *services.Service) (*services.Service, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Update)
if err != nil {
return nil, status.NewPermissionValidationError(err)
}
if !ok {
return nil, status.NewPermissionDeniedError()
}
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
// Get existing service
existingService, err := transaction.GetServiceByID(ctx, store.LockingStrengthUpdate, accountID, service.ID)
if err != nil {
return err
}
// Check if domain changed and if it conflicts
if existingService.Domain != service.Domain {
conflictService, err := transaction.GetServiceByDomain(ctx, accountID, service.Domain)
if err != nil {
if sErr, ok := status.FromError(err); !ok || sErr.Type() != status.NotFound {
return fmt.Errorf("failed to check existing service: %w", err)
}
}
if conflictService != nil && conflictService.ID != service.ID {
return status.Errorf(status.AlreadyExists, "service with domain %s already exists", service.Domain)
}
}
// Validate distribution groups exist
for _, groupID := range service.DistributionGroups {
_, err = transaction.GetGroupByID(ctx, store.LockingStrengthNone, accountID, groupID)
if err != nil {
return status.Errorf(status.InvalidArgument, "%s", err.Error())
}
}
if err = transaction.UpdateService(ctx, service); err != nil {
return fmt.Errorf("failed to update service: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
m.accountManager.StoreEvent(ctx, userID, service.ID, accountID, activity.ServiceUpdated, service.EventMeta())
return service, nil
}
func (m *managerImpl) DeleteService(ctx context.Context, accountID, userID, serviceID string) error {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Delete)
if err != nil {
return status.NewPermissionValidationError(err)
}
if !ok {
return status.NewPermissionDeniedError()
}
var service *services.Service
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
var err error
service, err = transaction.GetServiceByID(ctx, store.LockingStrengthUpdate, accountID, serviceID)
if err != nil {
return err
}
if err = transaction.DeleteService(ctx, accountID, serviceID); err != nil {
return fmt.Errorf("failed to delete service: %w", err)
}
return nil
})
if err != nil {
return err
}
m.accountManager.StoreEvent(ctx, userID, serviceID, accountID, activity.ServiceDeleted, service.EventMeta())
return nil
}

View File

@@ -1,184 +0,0 @@
package services
import (
"errors"
"github.com/rs/xid"
"github.com/netbirdio/netbird/shared/management/http/api"
)
type Target struct {
Path string `json:"path"`
Host string `json:"host"`
Enabled bool `json:"enabled"`
}
type Service struct {
ID string `gorm:"primaryKey"`
AccountID string `gorm:"index"`
Name string
Description string
Domain string `gorm:"index"`
Targets []Target `gorm:"serializer:json"`
DistributionGroups []string `gorm:"serializer:json"`
Enabled bool
Exposed bool
// Authentication configuration
AuthType string
AuthBasicUsername string
AuthBasicPassword string
AuthPINValue string
AuthPINHeader string
AuthBearerEnabled bool
}
func NewService(accountID, name, description, domain string, targets []Target, distributionGroups []string, enabled, exposed bool) *Service {
return &Service{
ID: xid.New().String(),
AccountID: accountID,
Name: name,
Description: description,
Domain: domain,
Targets: targets,
DistributionGroups: distributionGroups,
Enabled: enabled,
Exposed: exposed,
}
}
func (s *Service) ToAPIResponse() *api.Service {
var authConfig *api.ServiceAuthConfig
switch s.AuthType {
case "basic":
authConfig = &api.ServiceAuthConfig{
Type: "basic",
BasicAuth: &api.BasicAuthConfig{
Username: s.AuthBasicUsername,
Password: s.AuthBasicPassword,
},
}
case "pin":
authConfig = &api.ServiceAuthConfig{
Type: "pin",
PinAuth: &api.PINAuthConfig{
Pin: s.AuthPINValue,
Header: s.AuthPINHeader,
},
}
case "bearer":
authConfig = &api.ServiceAuthConfig{
Type: "bearer",
BearerAuth: &api.BearerAuthConfig{
Enabled: s.AuthBearerEnabled,
},
}
}
// Convert internal targets to API targets
apiTargets := make([]api.ServiceTarget, 0, len(s.Targets))
for _, target := range s.Targets {
apiTargets = append(apiTargets, api.ServiceTarget{
Path: target.Path,
Host: target.Host,
Enabled: target.Enabled,
})
}
return &api.Service{
Id: s.ID,
Name: s.Name,
Description: &s.Description,
Domain: s.Domain,
Targets: apiTargets,
DistributionGroups: s.DistributionGroups,
Enabled: s.Enabled,
Exposed: s.Exposed,
Auth: authConfig,
}
}
func (s *Service) FromAPIRequest(req *api.ServiceRequest) {
s.Name = req.Name
s.Domain = req.Domain
// Convert API targets to internal targets
targets := make([]Target, 0, len(req.Targets))
for _, apiTarget := range req.Targets {
targets = append(targets, Target{
Path: apiTarget.Path,
Host: apiTarget.Host,
Enabled: apiTarget.Enabled,
})
}
s.Targets = targets
s.DistributionGroups = req.DistributionGroups
if req.Description != nil {
s.Description = *req.Description
}
enabled := true
if req.Enabled != nil {
enabled = *req.Enabled
}
s.Enabled = enabled
exposed := false
if req.Exposed != nil {
exposed = *req.Exposed
}
s.Exposed = exposed
// Handle auth config
if req.Auth != nil {
s.AuthType = string(req.Auth.Type)
switch req.Auth.Type {
case "basic":
if req.Auth.BasicAuth != nil {
s.AuthBasicUsername = req.Auth.BasicAuth.Username
s.AuthBasicPassword = req.Auth.BasicAuth.Password
}
case "pin":
if req.Auth.PinAuth != nil {
s.AuthPINValue = req.Auth.PinAuth.Pin
s.AuthPINHeader = req.Auth.PinAuth.Header
}
case "bearer":
if req.Auth.BearerAuth != nil {
s.AuthBearerEnabled = req.Auth.BearerAuth.Enabled
}
}
}
}
func (s *Service) Validate() error {
if s.Name == "" {
return errors.New("service name is required")
}
if len(s.Name) > 255 {
return errors.New("service name exceeds maximum length of 255 characters")
}
if s.Domain == "" {
return errors.New("service domain is required")
}
if len(s.Targets) == 0 {
return errors.New("at least one target is required")
}
if len(s.DistributionGroups) == 0 {
return errors.New("at least one distribution group is required")
}
return nil
}
func (s *Service) EventMeta() map[string]any {
return map[string]any{"name": s.Name, "domain": s.Domain}
}

View File

@@ -92,7 +92,7 @@ func (s *BaseServer) EventStore() activity.Store {
func (s *BaseServer) APIHandler() http.Handler { func (s *BaseServer) APIHandler() http.Handler {
return Create(s, func() http.Handler { return Create(s, func() http.Handler {
httpAPIHandler, err := nbhttp.NewAPIHandler(context.Background(), s.AccountManager(), s.NetworksManager(), s.ResourcesManager(), s.RoutesManager(), s.GroupsManager(), s.GeoLocationManager(), s.AuthManager(), s.Metrics(), s.IntegratedValidator(), s.ProxyController(), s.PermissionsManager(), s.PeersManager(), s.SettingsManager(), s.ZonesManager(), s.RecordsManager(), s.NetworkMapController(), s.IdpManager(), s.ServiceManager()) httpAPIHandler, err := nbhttp.NewAPIHandler(context.Background(), s.AccountManager(), s.NetworksManager(), s.ResourcesManager(), s.RoutesManager(), s.GroupsManager(), s.GeoLocationManager(), s.AuthManager(), s.Metrics(), s.IntegratedValidator(), s.ProxyController(), s.PermissionsManager(), s.PeersManager(), s.SettingsManager(), s.ZonesManager(), s.RecordsManager(), s.NetworkMapController(), s.IdpManager(), s.ReverseProxyManager())
if err != nil { if err != nil {
log.Fatalf("failed to create API handler: %v", err) log.Fatalf("failed to create API handler: %v", err)
} }
@@ -150,7 +150,7 @@ func (s *BaseServer) GRPCServer() *grpc.Server {
} }
mgmtProto.RegisterManagementServiceServer(gRPCAPIHandler, srv) mgmtProto.RegisterManagementServiceServer(gRPCAPIHandler, srv)
proxyService := nbgrpc.NewProxyServiceServer(s.Store(), s.AccountManager()) proxyService := nbgrpc.NewProxyServiceServer(s.Store())
mgmtProto.RegisterProxyServiceServer(gRPCAPIHandler, proxyService) mgmtProto.RegisterProxyServiceServer(gRPCAPIHandler, proxyService)
log.Info("ProxyService registered on gRPC server") log.Info("ProxyService registered on gRPC server")

View File

@@ -8,8 +8,8 @@ import (
"github.com/netbirdio/management-integrations/integrations" "github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/management/internals/modules/peers" "github.com/netbirdio/netbird/management/internals/modules/peers"
"github.com/netbirdio/netbird/management/internals/modules/services" "github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
nbservices "github.com/netbirdio/netbird/management/internals/modules/services/manager" nbreverseproxy "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/manager"
"github.com/netbirdio/netbird/management/internals/modules/zones" "github.com/netbirdio/netbird/management/internals/modules/zones"
zonesManager "github.com/netbirdio/netbird/management/internals/modules/zones/manager" zonesManager "github.com/netbirdio/netbird/management/internals/modules/zones/manager"
"github.com/netbirdio/netbird/management/internals/modules/zones/records" "github.com/netbirdio/netbird/management/internals/modules/zones/records"
@@ -177,8 +177,8 @@ func (s *BaseServer) RecordsManager() records.Manager {
}) })
} }
func (s *BaseServer) ServiceManager() services.Manager { func (s *BaseServer) ReverseProxyManager() reverseproxy.Manager {
return Create(s, func() services.Manager { return Create(s, func() reverseproxy.Manager {
return nbservices.NewManager(s.Store(), s.AccountManager(), s.PermissionsManager()) return nbreverseproxy.NewManager(s.Store(), s.AccountManager(), s.PermissionsManager())
}) })
} }

View File

@@ -6,18 +6,19 @@ import (
"sync" "sync"
"time" "time"
"github.com/netbirdio/netbird/management/internals/modules/services"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/shared/management/proto"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/peer" "google.golang.org/grpc/peer"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/shared/management/proto"
) )
type serviceStore interface { type reverseProxyStore interface {
GetAccountServices(ctx context.Context, lockStrength store.LockingStrength, accountID string) ([]*services.Service, error) GetAccountReverseProxies(ctx context.Context, lockStrength store.LockingStrength, accountID string) ([]*reverseproxy.ReverseProxy, error)
} }
type keyStore interface { type keyStore interface {
@@ -31,11 +32,11 @@ type ProxyServiceServer struct {
// Map of connected proxies: proxy_id -> proxy connection // Map of connected proxies: proxy_id -> proxy connection
connectedProxies sync.Map connectedProxies sync.Map
// Channel for broadcasting service updates to all proxies // Channel for broadcasting reverse proxy updates to all proxies
updatesChan chan *proto.ProxyMapping updatesChan chan *proto.ProxyMapping
// Store of services // Store of reverse proxies
serviceStore serviceStore reverseProxyStore reverseProxyStore
// Store for client setup keys // Store for client setup keys
keyStore keyStore keyStore keyStore
@@ -52,10 +53,10 @@ type proxyConnection struct {
} }
// NewProxyServiceServer creates a new proxy service server // NewProxyServiceServer creates a new proxy service server
func NewProxyServiceServer(store serviceStore) *ProxyServiceServer { func NewProxyServiceServer(store reverseProxyStore) *ProxyServiceServer {
return &ProxyServiceServer{ return &ProxyServiceServer{
updatesChan: make(chan *proto.ProxyMapping, 100), updatesChan: make(chan *proto.ProxyMapping, 100),
serviceStore: store, reverseProxyStore: store,
} }
} }
@@ -110,51 +111,51 @@ func (s *ProxyServiceServer) GetMappingUpdate(req *proto.GetMappingUpdateRequest
} }
} }
// sendSnapshot sends the initial snapshot of all services to proxy // sendSnapshot sends the initial snapshot of all reverse proxies to proxy
func (s *ProxyServiceServer) sendSnapshot(ctx context.Context, conn *proxyConnection) error { func (s *ProxyServiceServer) sendSnapshot(ctx context.Context, conn *proxyConnection) error {
svcs, err := s.serviceStore.GetAccountServices(ctx, store.LockingStrengthNone, conn.proxyID) // TODO: check locking strength and accountID. reverseProxies, err := s.reverseProxyStore.GetAccountReverseProxies(ctx, store.LockingStrengthNone, conn.proxyID) // TODO: check locking strength and accountID.
if err != nil { if err != nil {
// TODO: something // TODO: something
return fmt.Errorf("get account services from store: %w", err) return fmt.Errorf("get account reverse proxies from store: %w", err)
} }
for _, svc := range svcs { for _, rp := range reverseProxies {
if !svc.Enabled || !svc.Exposed { if !rp.Enabled {
// We don't care about disabled services for snapshots. // We don't care about disabled reverse proxies for snapshots.
continue continue
} }
// Fill auth values. // Fill auth values.
// TODO: This will be removed soon as the management server should be handling authentication rather than the proxy, so probably not much use in fleshing this out too much. // TODO: This will be removed soon as the management server should be handling authentication rather than the proxy, so probably not much use in fleshing this out too much.
auth := &proto.Authentication{} auth := &proto.Authentication{}
if svc.AuthBearerEnabled { if rp.Auth.BearerAuth != nil && rp.Auth.BearerAuth.Enabled {
auth.Oidc = &proto.OIDC{ auth.Oidc = &proto.OIDC{
Enabled: true, Enabled: true,
// TODO: fill other OIDC fields from account OIDC settings. // TODO: fill other OIDC fields from account OIDC settings.
} }
} }
if svc.AuthBasicPassword != "" { if rp.Auth.PasswordAuth != nil && rp.Auth.PasswordAuth.Password != "" {
auth.Basic = &proto.HTTPBasic{ auth.Basic = &proto.HTTPBasic{
Enabled: true, Enabled: true,
Username: svc.AuthBasicUsername, Username: "",
Password: svc.AuthBasicPassword, Password: rp.Auth.PasswordAuth.Password,
} }
} }
if svc.AuthPINValue != "" { if rp.Auth.PinAuth != nil && rp.Auth.PinAuth.Pin != "" {
auth.Pin = &proto.Pin{ auth.Pin = &proto.Pin{
Enabled: true, Enabled: true,
Pin: svc.AuthPINValue, Pin: rp.Auth.PinAuth.Pin,
} }
} }
var paths []*proto.PathMapping var paths []*proto.PathMapping
for _, t := range svc.Targets { for _, t := range rp.Targets {
if !t.Enabled { if !t.Enabled {
// We don't care about disabled service targets for snapshots. // We don't care about disabled reverse proxy targets for snapshots.
continue continue
} }
paths = append(paths, &proto.PathMapping{ paths = append(paths, &proto.PathMapping{
Path: t.Path, Path: *t.Path,
Target: t.Host, Target: t.Host,
}) })
} }
@@ -179,8 +180,8 @@ func (s *ProxyServiceServer) sendSnapshot(ctx context.Context, conn *proxyConnec
Mapping: []*proto.ProxyMapping{ Mapping: []*proto.ProxyMapping{
{ {
Type: proto.ProxyMappingUpdateType_UPDATE_TYPE_CREATED, // Initial snapshot, all records are "new" for the proxy. Type: proto.ProxyMappingUpdateType_UPDATE_TYPE_CREATED, // Initial snapshot, all records are "new" for the proxy.
Id: svc.ID, Id: rp.ID,
Domain: svc.Domain, Domain: rp.Domain,
Path: paths, Path: paths,
SetupKey: key.Key, SetupKey: key.Key,
Auth: auth, Auth: auth,
@@ -214,34 +215,34 @@ func (s *ProxyServiceServer) sender(conn *proxyConnection, errChan chan<- error)
// SendAccessLog processes access log from proxy // SendAccessLog processes access log from proxy
func (s *ProxyServiceServer) SendAccessLog(ctx context.Context, req *proto.SendAccessLogRequest) (*proto.SendAccessLogResponse, error) { func (s *ProxyServiceServer) SendAccessLog(ctx context.Context, req *proto.SendAccessLogRequest) (*proto.SendAccessLogResponse, error) {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"proxy_id": "", // TODO: get proxy id, probably from context or maybe from request message. "proxy_id": "", // TODO: get proxy id, probably from context or maybe from request message.
"service_id": req.GetLog().GetServiceId(), "reverse_proxy_id": req.GetLog().GetServiceId(),
"host": req.GetLog().GetHost(), "host": req.GetLog().GetHost(),
"path": req.GetLog().GetPath(), "path": req.GetLog().GetPath(),
"method": req.GetLog().GetMethod(), "method": req.GetLog().GetMethod(),
"response_code": req.GetLog().GetResponseCode(), "response_code": req.GetLog().GetResponseCode(),
"duration_ms": req.GetLog().GetDurationMs(), "duration_ms": req.GetLog().GetDurationMs(),
"source_ip": req.GetLog().GetSourceIp(), "source_ip": req.GetLog().GetSourceIp(),
"auth_mechanism": req.GetLog().GetAuthMechanism(), "auth_mechanism": req.GetLog().GetAuthMechanism(),
"user_id": req.GetLog().GetUserId(), "user_id": req.GetLog().GetUserId(),
"auth_success": req.GetLog().GetAuthSuccess(), "auth_success": req.GetLog().GetAuthSuccess(),
}).Info("Access log from proxy") }).Info("Access log from proxy")
// TODO: Store access log in database/metrics system // TODO: Store access log in database/metrics system
return &proto.SendAccessLogResponse{}, nil return &proto.SendAccessLogResponse{}, nil
} }
// SendServiceUpdate broadcasts a service update to all connected proxies. // SendReverseProxyUpdate broadcasts a reverse proxy update to all connected proxies.
// Management should call this when services are created/updated/removed // Management should call this when reverse proxies are created/updated/removed
func (s *ProxyServiceServer) SendServiceUpdate(update *proto.ProxyMapping) { func (s *ProxyServiceServer) SendReverseProxyUpdate(update *proto.ProxyMapping) {
// Send it to all connected proxies // Send it to all connected proxies
s.connectedProxies.Range(func(key, value interface{}) bool { s.connectedProxies.Range(func(key, value interface{}) bool {
conn := value.(*proxyConnection) conn := value.(*proxyConnection)
select { select {
case conn.sendChan <- update: case conn.sendChan <- update:
log.Debugf("Sent service update to proxy %s", conn.proxyID) log.Debugf("Sent reverse proxy update to proxy %s", conn.proxyID)
default: default:
log.Warnf("Failed to send service update to proxy %s (channel full)", conn.proxyID) log.Warnf("Failed to send reverse proxy update to proxy %s (channel full)", conn.proxyID)
} }
return true return true
}) })

View File

@@ -204,9 +204,9 @@ const (
UserInviteLinkRegenerated Activity = 106 UserInviteLinkRegenerated Activity = 106
UserInviteLinkDeleted Activity = 107 UserInviteLinkDeleted Activity = 107
ServiceCreated Activity = 108 ReverseProxyCreated Activity = 108
ServiceUpdated Activity = 109 ReverseProxyUpdated Activity = 109
ServiceDeleted Activity = 110 ReverseProxyDeleted Activity = 110
AccountDeleted Activity = 99999 AccountDeleted Activity = 99999
) )
@@ -342,9 +342,9 @@ var activityMap = map[Activity]Code{
UserInviteLinkRegenerated: {"User invite link regenerated", "user.invite.link.regenerate"}, UserInviteLinkRegenerated: {"User invite link regenerated", "user.invite.link.regenerate"},
UserInviteLinkDeleted: {"User invite link deleted", "user.invite.link.delete"}, UserInviteLinkDeleted: {"User invite link deleted", "user.invite.link.delete"},
ServiceCreated: {"Service created", "service.create"}, ReverseProxyCreated: {"Reverse proxy created", "reverseproxy.create"},
ServiceUpdated: {"Service updated", "service.update"}, ReverseProxyUpdated: {"Reverse proxy updated", "reverseproxy.update"},
ServiceDeleted: {"Service deleted", "service.delete"}, ReverseProxyDeleted: {"Reverse proxy deleted", "reverseproxy.delete"},
} }
// StringCode returns a string code of the activity // StringCode returns a string code of the activity

View File

@@ -12,8 +12,8 @@ import (
"github.com/rs/cors" "github.com/rs/cors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
nbservices "github.com/netbirdio/netbird/management/internals/modules/services" "github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
services "github.com/netbirdio/netbird/management/internals/modules/services/manager" reverseproxymanager "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/manager"
idpmanager "github.com/netbirdio/netbird/management/server/idp" idpmanager "github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/management-integrations/integrations" "github.com/netbirdio/management-integrations/integrations"
@@ -62,7 +62,7 @@ const (
) )
// NewAPIHandler creates the Management service HTTP API handler registering all the available endpoints. // NewAPIHandler creates the Management service HTTP API handler registering all the available endpoints.
func NewAPIHandler(ctx context.Context, accountManager account.Manager, networksManager nbnetworks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager nbgroups.Manager, LocationManager geolocation.Geolocation, authManager auth.Manager, appMetrics telemetry.AppMetrics, integratedValidator integrated_validator.IntegratedValidator, proxyController port_forwarding.Controller, permissionsManager permissions.Manager, peersManager nbpeers.Manager, settingsManager settings.Manager, zManager zones.Manager, rManager records.Manager, networkMapController network_map.Controller, idpManager idpmanager.Manager, serviceManager nbservices.Manager) (http.Handler, error) { func NewAPIHandler(ctx context.Context, accountManager account.Manager, networksManager nbnetworks.Manager, resourceManager resources.Manager, routerManager routers.Manager, groupsManager nbgroups.Manager, LocationManager geolocation.Geolocation, authManager auth.Manager, appMetrics telemetry.AppMetrics, integratedValidator integrated_validator.IntegratedValidator, proxyController port_forwarding.Controller, permissionsManager permissions.Manager, peersManager nbpeers.Manager, settingsManager settings.Manager, zManager zones.Manager, rManager records.Manager, networkMapController network_map.Controller, idpManager idpmanager.Manager, reverseProxyManager reverseproxy.Manager) (http.Handler, error) {
// Register bypass paths for unauthenticated endpoints // Register bypass paths for unauthenticated endpoints
if err := bypass.AddBypassPath("/api/instance"); err != nil { if err := bypass.AddBypassPath("/api/instance"); err != nil {
@@ -158,7 +158,7 @@ func NewAPIHandler(ctx context.Context, accountManager account.Manager, networks
idp.AddEndpoints(accountManager, router) idp.AddEndpoints(accountManager, router)
instance.AddEndpoints(instanceManager, router) instance.AddEndpoints(instanceManager, router)
instance.AddVersionEndpoint(instanceManager, router) instance.AddVersionEndpoint(instanceManager, router)
services.RegisterEndpoints(router, serviceManager) reverseproxymanager.RegisterEndpoints(router, reverseProxyManager)
// Mount embedded IdP handler at /oauth2 path if configured // Mount embedded IdP handler at /oauth2 path if configured
if embeddedIdpEnabled { if embeddedIdpEnabled {

View File

@@ -27,7 +27,7 @@ import (
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
nbdns "github.com/netbirdio/netbird/dns" nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/internals/modules/services" "github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
"github.com/netbirdio/netbird/management/internals/modules/zones" "github.com/netbirdio/netbird/management/internals/modules/zones"
"github.com/netbirdio/netbird/management/internals/modules/zones/records" "github.com/netbirdio/netbird/management/internals/modules/zones/records"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types" resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
@@ -127,7 +127,7 @@ func NewSqlStore(ctx context.Context, db *gorm.DB, storeEngine types.Engine, met
&types.Account{}, &types.Policy{}, &types.PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{}, &types.Account{}, &types.Policy{}, &types.PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{},
&installation{}, &types.ExtraSettings{}, &posture.Checks{}, &nbpeer.NetworkAddress{}, &installation{}, &types.ExtraSettings{}, &posture.Checks{}, &nbpeer.NetworkAddress{},
&networkTypes.Network{}, &routerTypes.NetworkRouter{}, &resourceTypes.NetworkResource{}, &types.AccountOnboarding{}, &networkTypes.Network{}, &routerTypes.NetworkRouter{}, &resourceTypes.NetworkResource{}, &types.AccountOnboarding{},
&types.Job{}, &zones.Zone{}, &records.Record{}, &types.UserInviteRecord{}, &services.Service{}, &types.Job{}, &zones.Zone{}, &records.Record{}, &types.UserInviteRecord{}, &reverseproxy.ReverseProxy{},
) )
if err != nil { if err != nil {
return nil, fmt.Errorf("auto migratePreAuto: %w", err) return nil, fmt.Errorf("auto migratePreAuto: %w", err)
@@ -4604,87 +4604,87 @@ func (s *SqlStore) GetPeerIDByKey(ctx context.Context, lockStrength LockingStren
return peerID, nil return peerID, nil
} }
func (s *SqlStore) CreateService(ctx context.Context, service *services.Service) error { func (s *SqlStore) CreateReverseProxy(ctx context.Context, proxy *reverseproxy.ReverseProxy) error {
result := s.db.Create(service) result := s.db.Create(proxy)
if result.Error != nil { if result.Error != nil {
log.WithContext(ctx).Errorf("failed to create service to store: %v", result.Error) log.WithContext(ctx).Errorf("failed to create reverse proxy to store: %v", result.Error)
return status.Errorf(status.Internal, "failed to create service to store") return status.Errorf(status.Internal, "failed to create reverse proxy to store")
} }
return nil return nil
} }
func (s *SqlStore) UpdateService(ctx context.Context, service *services.Service) error { func (s *SqlStore) UpdateReverseProxy(ctx context.Context, proxy *reverseproxy.ReverseProxy) error {
result := s.db.Select("*").Save(service) result := s.db.Select("*").Save(proxy)
if result.Error != nil { if result.Error != nil {
log.WithContext(ctx).Errorf("failed to update service to store: %v", result.Error) log.WithContext(ctx).Errorf("failed to update reverse proxy to store: %v", result.Error)
return status.Errorf(status.Internal, "failed to update service to store") return status.Errorf(status.Internal, "failed to update reverse proxy to store")
} }
return nil return nil
} }
func (s *SqlStore) DeleteService(ctx context.Context, accountID, serviceID string) error { func (s *SqlStore) DeleteReverseProxy(ctx context.Context, accountID, proxyID string) error {
result := s.db.Delete(&services.Service{}, accountAndIDQueryCondition, accountID, serviceID) result := s.db.Delete(&reverseproxy.ReverseProxy{}, accountAndIDQueryCondition, accountID, proxyID)
if result.Error != nil { if result.Error != nil {
log.WithContext(ctx).Errorf("failed to delete service from store: %v", result.Error) log.WithContext(ctx).Errorf("failed to delete reverse proxy from store: %v", result.Error)
return status.Errorf(status.Internal, "failed to delete service from store") return status.Errorf(status.Internal, "failed to delete reverse proxy from store")
} }
if result.RowsAffected == 0 { if result.RowsAffected == 0 {
return status.Errorf(status.NotFound, "service %s not found", serviceID) return status.Errorf(status.NotFound, "reverse proxy %s not found", proxyID)
} }
return nil return nil
} }
func (s *SqlStore) GetServiceByID(ctx context.Context, lockStrength LockingStrength, accountID, serviceID string) (*services.Service, error) { func (s *SqlStore) GetReverseProxyByID(ctx context.Context, lockStrength LockingStrength, accountID, proxyID string) (*reverseproxy.ReverseProxy, error) {
tx := s.db tx := s.db
if lockStrength != LockingStrengthNone { if lockStrength != LockingStrengthNone {
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)}) tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
} }
var service *services.Service var proxy *reverseproxy.ReverseProxy
result := tx.Take(&service, accountAndIDQueryCondition, accountID, serviceID) result := tx.Take(&proxy, accountAndIDQueryCondition, accountID, proxyID)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, status.Errorf(status.NotFound, "service %s not found", serviceID) return nil, status.Errorf(status.NotFound, "reverse proxy %s not found", proxyID)
} }
log.WithContext(ctx).Errorf("failed to get service from store: %v", result.Error) log.WithContext(ctx).Errorf("failed to get reverse proxy from store: %v", result.Error)
return nil, status.Errorf(status.Internal, "failed to get service from store") return nil, status.Errorf(status.Internal, "failed to get reverse proxy from store")
} }
return service, nil return proxy, nil
} }
func (s *SqlStore) GetServiceByDomain(ctx context.Context, accountID, domain string) (*services.Service, error) { func (s *SqlStore) GetReverseProxyByDomain(ctx context.Context, accountID, domain string) (*reverseproxy.ReverseProxy, error) {
var service *services.Service var proxy *reverseproxy.ReverseProxy
result := s.db.Where("account_id = ? AND domain = ?", accountID, domain).First(&service) result := s.db.Where("account_id = ? AND domain = ?", accountID, domain).First(&proxy)
if result.Error != nil { if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, status.Errorf(status.NotFound, "service with domain %s not found", domain) return nil, status.Errorf(status.NotFound, "reverse proxy with domain %s not found", domain)
} }
log.WithContext(ctx).Errorf("failed to get service by domain from store: %v", result.Error) log.WithContext(ctx).Errorf("failed to get reverse proxy by domain from store: %v", result.Error)
return nil, status.Errorf(status.Internal, "failed to get service by domain from store") return nil, status.Errorf(status.Internal, "failed to get reverse proxy by domain from store")
} }
return service, nil return proxy, nil
} }
func (s *SqlStore) GetAccountServices(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*services.Service, error) { func (s *SqlStore) GetAccountReverseProxies(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*reverseproxy.ReverseProxy, error) {
tx := s.db tx := s.db
if lockStrength != LockingStrengthNone { if lockStrength != LockingStrengthNone {
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)}) tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
} }
var servicesList []*services.Service var proxyList []*reverseproxy.ReverseProxy
result := tx.Find(&servicesList, accountIDCondition, accountID) result := tx.Find(&proxyList, accountIDCondition, accountID)
if result.Error != nil { if result.Error != nil {
log.WithContext(ctx).Errorf("failed to get services from the store: %s", result.Error) log.WithContext(ctx).Errorf("failed to get reverse proxy from the store: %s", result.Error)
return nil, status.Errorf(status.Internal, "failed to get services from store") return nil, status.Errorf(status.Internal, "failed to get reverse proxy from store")
} }
return servicesList, nil return proxyList, nil
} }

View File

@@ -23,7 +23,7 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
"github.com/netbirdio/netbird/dns" "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/internals/modules/services" "github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
"github.com/netbirdio/netbird/management/internals/modules/zones" "github.com/netbirdio/netbird/management/internals/modules/zones"
"github.com/netbirdio/netbird/management/internals/modules/zones/records" "github.com/netbirdio/netbird/management/internals/modules/zones/records"
"github.com/netbirdio/netbird/management/server/telemetry" "github.com/netbirdio/netbird/management/server/telemetry"
@@ -242,12 +242,12 @@ type Store interface {
MarkAllPendingJobsAsFailed(ctx context.Context, accountID, peerID, reason string) error MarkAllPendingJobsAsFailed(ctx context.Context, accountID, peerID, reason string) error
GetPeerIDByKey(ctx context.Context, lockStrength LockingStrength, key string) (string, error) GetPeerIDByKey(ctx context.Context, lockStrength LockingStrength, key string) (string, error)
CreateService(ctx context.Context, service *services.Service) error CreateReverseProxy(ctx context.Context, service *reverseproxy.ReverseProxy) error
UpdateService(ctx context.Context, service *services.Service) error UpdateReverseProxy(ctx context.Context, service *reverseproxy.ReverseProxy) error
DeleteService(ctx context.Context, accountID, serviceID string) error DeleteReverseProxy(ctx context.Context, accountID, serviceID string) error
GetServiceByID(ctx context.Context, lockStrength LockingStrength, accountID, serviceID string) (*services.Service, error) GetReverseProxyByID(ctx context.Context, lockStrength LockingStrength, accountID, serviceID string) (*reverseproxy.ReverseProxy, error)
GetServiceByDomain(ctx context.Context, accountID, domain string) (*services.Service, error) GetReverseProxyByDomain(ctx context.Context, accountID, domain string) (*reverseproxy.ReverseProxy, error)
GetAccountServices(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*services.Service, error) GetAccountReverseProxies(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*reverseproxy.ReverseProxy, error)
} }
const ( const (

View File

@@ -2855,10 +2855,6 @@ components:
ReverseProxyAuthConfig: ReverseProxyAuthConfig:
type: object type: object
properties: properties:
type:
type: string
enum: [password, pin, bearer, link]
description: Authentication type
password_auth: password_auth:
$ref: '#/components/schemas/PasswordAuthConfig' $ref: '#/components/schemas/PasswordAuthConfig'
pin_auth: pin_auth:
@@ -2867,8 +2863,6 @@ components:
$ref: '#/components/schemas/BearerAuthConfig' $ref: '#/components/schemas/BearerAuthConfig'
link_auth: link_auth:
$ref: '#/components/schemas/LinkAuthConfig' $ref: '#/components/schemas/LinkAuthConfig'
required:
- type
PasswordAuthConfig: PasswordAuthConfig:
type: object type: object
properties: properties:

View File

@@ -1,10 +1,14 @@
// Package api provides primitives to interact with the openapi HTTP API. // Package api provides primitives to interact with the openapi HTTP API.
// //
// Code generated by github.com/deepmap/oapi-codegen version v1.11.1-0.20220912230023-4a1477f6a8ba DO NOT EDIT. // Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.1 DO NOT EDIT.
package api package api
import ( import (
"encoding/json"
"errors"
"time" "time"
"github.com/oapi-codegen/runtime"
) )
const ( const (
@@ -21,56 +25,118 @@ const (
// Defines values for EventActivityCode. // Defines values for EventActivityCode.
const ( const (
EventActivityCodeAccountCreate EventActivityCode = "account.create" EventActivityCodeAccountCreate EventActivityCode = "account.create"
EventActivityCodeAccountSettingPeerLoginExpirationDisable EventActivityCode = "account.setting.peer.login.expiration.disable" EventActivityCodeAccountDelete EventActivityCode = "account.delete"
EventActivityCodeAccountSettingPeerLoginExpirationEnable EventActivityCode = "account.setting.peer.login.expiration.enable" EventActivityCodeAccountDnsDomainUpdate EventActivityCode = "account.dns.domain.update"
EventActivityCodeAccountSettingPeerLoginExpirationUpdate EventActivityCode = "account.setting.peer.login.expiration.update" EventActivityCodeAccountNetworkRangeUpdate EventActivityCode = "account.network.range.update"
EventActivityCodeDnsSettingDisabledManagementGroupAdd EventActivityCode = "dns.setting.disabled.management.group.add" EventActivityCodeAccountPeerInactivityExpirationDisable EventActivityCode = "account.peer.inactivity.expiration.disable"
EventActivityCodeDnsSettingDisabledManagementGroupDelete EventActivityCode = "dns.setting.disabled.management.group.delete" EventActivityCodeAccountPeerInactivityExpirationEnable EventActivityCode = "account.peer.inactivity.expiration.enable"
EventActivityCodeGroupAdd EventActivityCode = "group.add" EventActivityCodeAccountPeerInactivityExpirationUpdate EventActivityCode = "account.peer.inactivity.expiration.update"
EventActivityCodeGroupUpdate EventActivityCode = "group.update" EventActivityCodeAccountSettingGroupPropagationDisable EventActivityCode = "account.setting.group.propagation.disable"
EventActivityCodeNameserverGroupAdd EventActivityCode = "nameserver.group.add" EventActivityCodeAccountSettingGroupPropagationEnable EventActivityCode = "account.setting.group.propagation.enable"
EventActivityCodeNameserverGroupDelete EventActivityCode = "nameserver.group.delete" EventActivityCodeAccountSettingLazyConnectionDisable EventActivityCode = "account.setting.lazy.connection.disable"
EventActivityCodeNameserverGroupUpdate EventActivityCode = "nameserver.group.update" EventActivityCodeAccountSettingLazyConnectionEnable EventActivityCode = "account.setting.lazy.connection.enable"
EventActivityCodePeerLoginExpirationDisable EventActivityCode = "peer.login.expiration.disable" EventActivityCodeAccountSettingPeerApprovalDisable EventActivityCode = "account.setting.peer.approval.disable"
EventActivityCodePeerLoginExpirationEnable EventActivityCode = "peer.login.expiration.enable" EventActivityCodeAccountSettingPeerApprovalEnable EventActivityCode = "account.setting.peer.approval.enable"
EventActivityCodePeerLoginExpire EventActivityCode = "peer.login.expire" EventActivityCodeAccountSettingPeerLoginExpirationDisable EventActivityCode = "account.setting.peer.login.expiration.disable"
EventActivityCodePeerRename EventActivityCode = "peer.rename" EventActivityCodeAccountSettingPeerLoginExpirationEnable EventActivityCode = "account.setting.peer.login.expiration.enable"
EventActivityCodePeerSshDisable EventActivityCode = "peer.ssh.disable" EventActivityCodeAccountSettingPeerLoginExpirationUpdate EventActivityCode = "account.setting.peer.login.expiration.update"
EventActivityCodePeerSshEnable EventActivityCode = "peer.ssh.enable" EventActivityCodeAccountSettingRoutingPeerDnsResolutionDisable EventActivityCode = "account.setting.routing.peer.dns.resolution.disable"
EventActivityCodePersonalAccessTokenCreate EventActivityCode = "personal.access.token.create" EventActivityCodeAccountSettingRoutingPeerDnsResolutionEnable EventActivityCode = "account.setting.routing.peer.dns.resolution.enable"
EventActivityCodePersonalAccessTokenDelete EventActivityCode = "personal.access.token.delete" EventActivityCodeAccountSettingsAutoVersionUpdate EventActivityCode = "account.settings.auto.version.update"
EventActivityCodePolicyAdd EventActivityCode = "policy.add" EventActivityCodeDashboardLogin EventActivityCode = "dashboard.login"
EventActivityCodePolicyDelete EventActivityCode = "policy.delete" EventActivityCodeDnsSettingDisabledManagementGroupAdd EventActivityCode = "dns.setting.disabled.management.group.add"
EventActivityCodePolicyUpdate EventActivityCode = "policy.update" EventActivityCodeDnsSettingDisabledManagementGroupDelete EventActivityCode = "dns.setting.disabled.management.group.delete"
EventActivityCodeRouteAdd EventActivityCode = "route.add" EventActivityCodeDnsZoneCreate EventActivityCode = "dns.zone.create"
EventActivityCodeRouteDelete EventActivityCode = "route.delete" EventActivityCodeDnsZoneDelete EventActivityCode = "dns.zone.delete"
EventActivityCodeRouteUpdate EventActivityCode = "route.update" EventActivityCodeDnsZoneRecordCreate EventActivityCode = "dns.zone.record.create"
EventActivityCodeRuleAdd EventActivityCode = "rule.add" EventActivityCodeDnsZoneRecordDelete EventActivityCode = "dns.zone.record.delete"
EventActivityCodeRuleDelete EventActivityCode = "rule.delete" EventActivityCodeDnsZoneRecordUpdate EventActivityCode = "dns.zone.record.update"
EventActivityCodeRuleUpdate EventActivityCode = "rule.update" EventActivityCodeDnsZoneUpdate EventActivityCode = "dns.zone.update"
EventActivityCodeServiceCreate EventActivityCode = "service.create" EventActivityCodeGroupAdd EventActivityCode = "group.add"
EventActivityCodeServiceDelete EventActivityCode = "service.delete" EventActivityCodeGroupDelete EventActivityCode = "group.delete"
EventActivityCodeServiceUpdate EventActivityCode = "service.update" EventActivityCodeGroupUpdate EventActivityCode = "group.update"
EventActivityCodeServiceUserCreate EventActivityCode = "service.user.create" EventActivityCodeIdentityproviderCreate EventActivityCode = "identityprovider.create"
EventActivityCodeServiceUserDelete EventActivityCode = "service.user.delete" EventActivityCodeIdentityproviderDelete EventActivityCode = "identityprovider.delete"
EventActivityCodeSetupkeyAdd EventActivityCode = "setupkey.add" EventActivityCodeIdentityproviderUpdate EventActivityCode = "identityprovider.update"
EventActivityCodeSetupkeyGroupAdd EventActivityCode = "setupkey.group.add" EventActivityCodeIntegrationCreate EventActivityCode = "integration.create"
EventActivityCodeSetupkeyGroupDelete EventActivityCode = "setupkey.group.delete" EventActivityCodeIntegrationDelete EventActivityCode = "integration.delete"
EventActivityCodeSetupkeyOveruse EventActivityCode = "setupkey.overuse" EventActivityCodeIntegrationUpdate EventActivityCode = "integration.update"
EventActivityCodeSetupkeyPeerAdd EventActivityCode = "setupkey.peer.add" EventActivityCodeNameserverGroupAdd EventActivityCode = "nameserver.group.add"
EventActivityCodeSetupkeyRevoke EventActivityCode = "setupkey.revoke" EventActivityCodeNameserverGroupDelete EventActivityCode = "nameserver.group.delete"
EventActivityCodeSetupkeyUpdate EventActivityCode = "setupkey.update" EventActivityCodeNameserverGroupUpdate EventActivityCode = "nameserver.group.update"
EventActivityCodeUserBlock EventActivityCode = "user.block" EventActivityCodeNetworkCreate EventActivityCode = "network.create"
EventActivityCodeUserGroupAdd EventActivityCode = "user.group.add" EventActivityCodeNetworkDelete EventActivityCode = "network.delete"
EventActivityCodeUserGroupDelete EventActivityCode = "user.group.delete" EventActivityCodeNetworkResourceCreate EventActivityCode = "network.resource.create"
EventActivityCodeUserInvite EventActivityCode = "user.invite" EventActivityCodeNetworkResourceDelete EventActivityCode = "network.resource.delete"
EventActivityCodeUserJoin EventActivityCode = "user.join" EventActivityCodeNetworkResourceUpdate EventActivityCode = "network.resource.update"
EventActivityCodeUserPeerAdd EventActivityCode = "user.peer.add" EventActivityCodeNetworkRouterCreate EventActivityCode = "network.router.create"
EventActivityCodeUserPeerDelete EventActivityCode = "user.peer.delete" EventActivityCodeNetworkRouterDelete EventActivityCode = "network.router.delete"
EventActivityCodeUserPeerLogin EventActivityCode = "user.peer.login" EventActivityCodeNetworkRouterUpdate EventActivityCode = "network.router.update"
EventActivityCodeUserRoleUpdate EventActivityCode = "user.role.update" EventActivityCodeNetworkUpdate EventActivityCode = "network.update"
EventActivityCodeUserUnblock EventActivityCode = "user.unblock" EventActivityCodePeerApprovalRevoke EventActivityCode = "peer.approval.revoke"
EventActivityCodePeerApprove EventActivityCode = "peer.approve"
EventActivityCodePeerGroupAdd EventActivityCode = "peer.group.add"
EventActivityCodePeerGroupDelete EventActivityCode = "peer.group.delete"
EventActivityCodePeerInactivityExpirationDisable EventActivityCode = "peer.inactivity.expiration.disable"
EventActivityCodePeerInactivityExpirationEnable EventActivityCode = "peer.inactivity.expiration.enable"
EventActivityCodePeerIpUpdate EventActivityCode = "peer.ip.update"
EventActivityCodePeerJobCreate EventActivityCode = "peer.job.create"
EventActivityCodePeerLoginExpirationDisable EventActivityCode = "peer.login.expiration.disable"
EventActivityCodePeerLoginExpirationEnable EventActivityCode = "peer.login.expiration.enable"
EventActivityCodePeerLoginExpire EventActivityCode = "peer.login.expire"
EventActivityCodePeerRename EventActivityCode = "peer.rename"
EventActivityCodePeerSetupkeyAdd EventActivityCode = "peer.setupkey.add"
EventActivityCodePeerSshDisable EventActivityCode = "peer.ssh.disable"
EventActivityCodePeerSshEnable EventActivityCode = "peer.ssh.enable"
EventActivityCodePeerUserAdd EventActivityCode = "peer.user.add"
EventActivityCodePersonalAccessTokenCreate EventActivityCode = "personal.access.token.create"
EventActivityCodePersonalAccessTokenDelete EventActivityCode = "personal.access.token.delete"
EventActivityCodePolicyAdd EventActivityCode = "policy.add"
EventActivityCodePolicyDelete EventActivityCode = "policy.delete"
EventActivityCodePolicyUpdate EventActivityCode = "policy.update"
EventActivityCodePostureCheckCreate EventActivityCode = "posture.check.create"
EventActivityCodePostureCheckDelete EventActivityCode = "posture.check.delete"
EventActivityCodePostureCheckUpdate EventActivityCode = "posture.check.update"
EventActivityCodeResourceGroupAdd EventActivityCode = "resource.group.add"
EventActivityCodeResourceGroupDelete EventActivityCode = "resource.group.delete"
EventActivityCodeRouteAdd EventActivityCode = "route.add"
EventActivityCodeRouteDelete EventActivityCode = "route.delete"
EventActivityCodeRouteUpdate EventActivityCode = "route.update"
EventActivityCodeRuleAdd EventActivityCode = "rule.add"
EventActivityCodeRuleDelete EventActivityCode = "rule.delete"
EventActivityCodeRuleUpdate EventActivityCode = "rule.update"
EventActivityCodeServiceCreate EventActivityCode = "service.create"
EventActivityCodeServiceDelete EventActivityCode = "service.delete"
EventActivityCodeServiceUpdate EventActivityCode = "service.update"
EventActivityCodeServiceUserCreate EventActivityCode = "service.user.create"
EventActivityCodeServiceUserDelete EventActivityCode = "service.user.delete"
EventActivityCodeSetupkeyAdd EventActivityCode = "setupkey.add"
EventActivityCodeSetupkeyDelete EventActivityCode = "setupkey.delete"
EventActivityCodeSetupkeyGroupAdd EventActivityCode = "setupkey.group.add"
EventActivityCodeSetupkeyGroupDelete EventActivityCode = "setupkey.group.delete"
EventActivityCodeSetupkeyOveruse EventActivityCode = "setupkey.overuse"
EventActivityCodeSetupkeyRevoke EventActivityCode = "setupkey.revoke"
EventActivityCodeSetupkeyUpdate EventActivityCode = "setupkey.update"
EventActivityCodeTransferredOwnerRole EventActivityCode = "transferred.owner.role"
EventActivityCodeUserApprove EventActivityCode = "user.approve"
EventActivityCodeUserBlock EventActivityCode = "user.block"
EventActivityCodeUserCreate EventActivityCode = "user.create"
EventActivityCodeUserDelete EventActivityCode = "user.delete"
EventActivityCodeUserGroupAdd EventActivityCode = "user.group.add"
EventActivityCodeUserGroupDelete EventActivityCode = "user.group.delete"
EventActivityCodeUserInvite EventActivityCode = "user.invite"
EventActivityCodeUserInviteLinkAccept EventActivityCode = "user.invite.link.accept"
EventActivityCodeUserInviteLinkCreate EventActivityCode = "user.invite.link.create"
EventActivityCodeUserInviteLinkDelete EventActivityCode = "user.invite.link.delete"
EventActivityCodeUserInviteLinkRegenerate EventActivityCode = "user.invite.link.regenerate"
EventActivityCodeUserJoin EventActivityCode = "user.join"
EventActivityCodeUserPasswordChange EventActivityCode = "user.password.change"
EventActivityCodeUserPeerDelete EventActivityCode = "user.peer.delete"
EventActivityCodeUserPeerLogin EventActivityCode = "user.peer.login"
EventActivityCodeUserReject EventActivityCode = "user.reject"
EventActivityCodeUserRoleUpdate EventActivityCode = "user.role.update"
EventActivityCodeUserUnblock EventActivityCode = "user.unblock"
) )
// Defines values for GeoLocationCheckAction. // Defines values for GeoLocationCheckAction.
@@ -125,6 +191,13 @@ const (
IngressPortAllocationRequestPortRangeProtocolUdp IngressPortAllocationRequestPortRangeProtocol = "udp" IngressPortAllocationRequestPortRangeProtocolUdp IngressPortAllocationRequestPortRangeProtocol = "udp"
) )
// Defines values for JobResponseStatus.
const (
JobResponseStatusFailed JobResponseStatus = "failed"
JobResponseStatusPending JobResponseStatus = "pending"
JobResponseStatusSucceeded JobResponseStatus = "succeeded"
)
// Defines values for NameserverNsType. // Defines values for NameserverNsType.
const ( const (
NameserverNsTypeUdp NameserverNsType = "udp" NameserverNsTypeUdp NameserverNsType = "udp"
@@ -196,14 +269,6 @@ const (
ResourceTypeSubnet ResourceType = "subnet" ResourceTypeSubnet ResourceType = "subnet"
) )
// Defines values for ReverseProxyAuthConfigType.
const (
ReverseProxyAuthConfigTypeBearer ReverseProxyAuthConfigType = "bearer"
ReverseProxyAuthConfigTypeLink ReverseProxyAuthConfigType = "link"
ReverseProxyAuthConfigTypePassword ReverseProxyAuthConfigType = "password"
ReverseProxyAuthConfigTypePin ReverseProxyAuthConfigType = "pin"
)
// Defines values for ReverseProxyDomainType. // Defines values for ReverseProxyDomainType.
const ( const (
ReverseProxyDomainTypeCustom ReverseProxyDomainType = "custom" ReverseProxyDomainTypeCustom ReverseProxyDomainType = "custom"
@@ -229,6 +294,11 @@ const (
UserStatusInvited UserStatus = "invited" UserStatusInvited UserStatus = "invited"
) )
// Defines values for WorkloadType.
const (
WorkloadTypeBundle WorkloadType = "bundle"
)
// Defines values for GetApiEventsNetworkTrafficParamsType. // Defines values for GetApiEventsNetworkTrafficParamsType.
const ( const (
GetApiEventsNetworkTrafficParamsTypeTYPEDROP GetApiEventsNetworkTrafficParamsType = "TYPE_DROP" GetApiEventsNetworkTrafficParamsTypeTYPEDROP GetApiEventsNetworkTrafficParamsType = "TYPE_DROP"
@@ -406,6 +476,47 @@ type BearerAuthConfig struct {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
} }
// BundleParameters These parameters control what gets included in the bundle and how it is processed.
type BundleParameters struct {
// Anonymize Whether sensitive data should be anonymized in the bundle.
Anonymize bool `json:"anonymize"`
// BundleFor Whether to generate a bundle for the given timeframe.
BundleFor bool `json:"bundle_for"`
// BundleForTime Time period in minutes for which to generate the bundle.
BundleForTime int `json:"bundle_for_time"`
// LogFileCount Maximum number of log files to include in the bundle.
LogFileCount int `json:"log_file_count"`
}
// BundleResult defines model for BundleResult.
type BundleResult struct {
UploadKey *string `json:"upload_key"`
}
// BundleWorkloadRequest defines model for BundleWorkloadRequest.
type BundleWorkloadRequest struct {
// Parameters These parameters control what gets included in the bundle and how it is processed.
Parameters BundleParameters `json:"parameters"`
// Type Identifies the type of workload the job will execute.
// Currently only `"bundle"` is supported.
Type WorkloadType `json:"type"`
}
// BundleWorkloadResponse defines model for BundleWorkloadResponse.
type BundleWorkloadResponse struct {
// Parameters These parameters control what gets included in the bundle and how it is processed.
Parameters BundleParameters `json:"parameters"`
Result BundleResult `json:"result"`
// Type Identifies the type of workload the job will execute.
// Currently only `"bundle"` is supported.
Type WorkloadType `json:"type"`
}
// Checks List of objects that perform the actual checks // Checks List of objects that perform the actual checks
type Checks struct { type Checks struct {
// GeoLocationCheck Posture check for geo location // GeoLocationCheck Posture check for geo location
@@ -793,6 +904,40 @@ type InstanceStatus struct {
SetupRequired bool `json:"setup_required"` SetupRequired bool `json:"setup_required"`
} }
// InstanceVersionInfo Version information for NetBird components
type InstanceVersionInfo struct {
// DashboardAvailableVersion The latest available version of the dashboard (from GitHub releases)
DashboardAvailableVersion *string `json:"dashboard_available_version,omitempty"`
// ManagementAvailableVersion The latest available version of the management server (from GitHub releases)
ManagementAvailableVersion *string `json:"management_available_version,omitempty"`
// ManagementCurrentVersion The current running version of the management server
ManagementCurrentVersion string `json:"management_current_version"`
// ManagementUpdateAvailable Indicates if a newer management version is available
ManagementUpdateAvailable bool `json:"management_update_available"`
}
// JobRequest defines model for JobRequest.
type JobRequest struct {
Workload WorkloadRequest `json:"workload"`
}
// JobResponse defines model for JobResponse.
type JobResponse struct {
CompletedAt *time.Time `json:"completed_at"`
CreatedAt time.Time `json:"created_at"`
FailedReason *string `json:"failed_reason"`
Id string `json:"id"`
Status JobResponseStatus `json:"status"`
TriggeredBy string `json:"triggered_by"`
Workload WorkloadResponse `json:"workload"`
}
// JobResponseStatus defines model for JobResponse.Status.
type JobResponseStatus string
// LinkAuthConfig defines model for LinkAuthConfig. // LinkAuthConfig defines model for LinkAuthConfig.
type LinkAuthConfig struct { type LinkAuthConfig struct {
// Enabled Whether link auth is enabled // Enabled Whether link auth is enabled
@@ -1187,6 +1332,15 @@ type PasswordAuthConfig struct {
Password string `json:"password"` Password string `json:"password"`
} }
// PasswordChangeRequest defines model for PasswordChangeRequest.
type PasswordChangeRequest struct {
// NewPassword The new password to set
NewPassword string `json:"new_password"`
// OldPassword The current password
OldPassword string `json:"old_password"`
}
// Peer defines model for Peer. // Peer defines model for Peer.
type Peer struct { type Peer struct {
// ApprovalRequired (Cloud only) Indicates whether peer needs approval // ApprovalRequired (Cloud only) Indicates whether peer needs approval
@@ -1774,14 +1928,8 @@ type ReverseProxyAuthConfig struct {
LinkAuth *LinkAuthConfig `json:"link_auth,omitempty"` LinkAuth *LinkAuthConfig `json:"link_auth,omitempty"`
PasswordAuth *PasswordAuthConfig `json:"password_auth,omitempty"` PasswordAuth *PasswordAuthConfig `json:"password_auth,omitempty"`
PinAuth *PINAuthConfig `json:"pin_auth,omitempty"` PinAuth *PINAuthConfig `json:"pin_auth,omitempty"`
// Type Authentication type
Type ReverseProxyAuthConfigType `json:"type"`
} }
// ReverseProxyAuthConfigType Authentication type
type ReverseProxyAuthConfigType string
// ReverseProxyDomain defines model for ReverseProxyDomain. // ReverseProxyDomain defines model for ReverseProxyDomain.
type ReverseProxyDomain struct { type ReverseProxyDomain struct {
// Domain Domain name // Domain Domain name
@@ -2190,6 +2338,99 @@ type UserCreateRequest struct {
Role string `json:"role"` Role string `json:"role"`
} }
// UserInvite A user invite
type UserInvite struct {
// AutoGroups Group IDs to auto-assign to peers registered by this user
AutoGroups []string `json:"auto_groups"`
// CreatedAt Invite creation time
CreatedAt time.Time `json:"created_at"`
// Email User's email address
Email string `json:"email"`
// Expired Whether the invite has expired
Expired bool `json:"expired"`
// ExpiresAt Invite expiration time
ExpiresAt time.Time `json:"expires_at"`
// Id Invite ID
Id string `json:"id"`
// InviteToken The invite link to be shared with the user. Only returned when the invite is created or regenerated.
InviteToken *string `json:"invite_token,omitempty"`
// Name User's full name
Name string `json:"name"`
// Role User's NetBird account role
Role string `json:"role"`
}
// UserInviteAcceptRequest Request to accept an invite and set password
type UserInviteAcceptRequest struct {
// Password The password the user wants to set. Must be at least 8 characters long and contain at least one uppercase letter, one digit, and one special character (any character that is not a letter or digit, including spaces).
Password string `json:"password"`
}
// UserInviteAcceptResponse Response after accepting an invite
type UserInviteAcceptResponse struct {
// Success Whether the invite was accepted successfully
Success bool `json:"success"`
}
// UserInviteCreateRequest Request to create a user invite link
type UserInviteCreateRequest struct {
// AutoGroups Group IDs to auto-assign to peers registered by this user
AutoGroups []string `json:"auto_groups"`
// Email User's email address
Email string `json:"email"`
// ExpiresIn Invite expiration time in seconds (default 72 hours)
ExpiresIn *int `json:"expires_in,omitempty"`
// Name User's full name
Name string `json:"name"`
// Role User's NetBird account role
Role string `json:"role"`
}
// UserInviteInfo Public information about an invite
type UserInviteInfo struct {
// Email User's email address
Email string `json:"email"`
// ExpiresAt Invite expiration time
ExpiresAt time.Time `json:"expires_at"`
// InvitedBy Name of the user who sent the invite
InvitedBy string `json:"invited_by"`
// Name User's full name
Name string `json:"name"`
// Valid Whether the invite is still valid (not expired)
Valid bool `json:"valid"`
}
// UserInviteRegenerateRequest Request to regenerate an invite link
type UserInviteRegenerateRequest struct {
// ExpiresIn Invite expiration time in seconds (default 72 hours)
ExpiresIn *int `json:"expires_in,omitempty"`
}
// UserInviteRegenerateResponse Response after regenerating an invite
type UserInviteRegenerateResponse struct {
// InviteExpiresAt New invite expiration time
InviteExpiresAt time.Time `json:"invite_expires_at"`
// InviteToken The new invite token
InviteToken string `json:"invite_token"`
}
// UserPermissions defines model for UserPermissions. // UserPermissions defines model for UserPermissions.
type UserPermissions struct { type UserPermissions struct {
// IsRestricted Indicates whether this User's Peers view is restricted // IsRestricted Indicates whether this User's Peers view is restricted
@@ -2209,6 +2450,20 @@ type UserRequest struct {
Role string `json:"role"` Role string `json:"role"`
} }
// WorkloadRequest defines model for WorkloadRequest.
type WorkloadRequest struct {
union json.RawMessage
}
// WorkloadResponse defines model for WorkloadResponse.
type WorkloadResponse struct {
union json.RawMessage
}
// WorkloadType Identifies the type of workload the job will execute.
// Currently only `"bundle"` is supported.
type WorkloadType string
// Zone defines model for Zone. // Zone defines model for Zone.
type Zone struct { type Zone struct {
// DistributionGroups Group IDs that defines groups of peers that will resolve this zone // DistributionGroups Group IDs that defines groups of peers that will resolve this zone
@@ -2392,6 +2647,9 @@ type PostApiPeersPeerIdIngressPortsJSONRequestBody = IngressPortAllocationReques
// PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody defines body for PutApiPeersPeerIdIngressPortsAllocationId for application/json ContentType. // PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody defines body for PutApiPeersPeerIdIngressPortsAllocationId for application/json ContentType.
type PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody = IngressPortAllocationRequest type PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody = IngressPortAllocationRequest
// PostApiPeersPeerIdJobsJSONRequestBody defines body for PostApiPeersPeerIdJobs for application/json ContentType.
type PostApiPeersPeerIdJobsJSONRequestBody = JobRequest
// PostApiPeersPeerIdTemporaryAccessJSONRequestBody defines body for PostApiPeersPeerIdTemporaryAccess for application/json ContentType. // PostApiPeersPeerIdTemporaryAccessJSONRequestBody defines body for PostApiPeersPeerIdTemporaryAccess for application/json ContentType.
type PostApiPeersPeerIdTemporaryAccessJSONRequestBody = PeerTemporaryAccessRequest type PostApiPeersPeerIdTemporaryAccessJSONRequestBody = PeerTemporaryAccessRequest
@@ -2434,8 +2692,138 @@ type PutApiSetupKeysKeyIdJSONRequestBody = SetupKeyRequest
// PostApiUsersJSONRequestBody defines body for PostApiUsers for application/json ContentType. // PostApiUsersJSONRequestBody defines body for PostApiUsers for application/json ContentType.
type PostApiUsersJSONRequestBody = UserCreateRequest type PostApiUsersJSONRequestBody = UserCreateRequest
// PostApiUsersInvitesJSONRequestBody defines body for PostApiUsersInvites for application/json ContentType.
type PostApiUsersInvitesJSONRequestBody = UserInviteCreateRequest
// PostApiUsersInvitesInviteIdRegenerateJSONRequestBody defines body for PostApiUsersInvitesInviteIdRegenerate for application/json ContentType.
type PostApiUsersInvitesInviteIdRegenerateJSONRequestBody = UserInviteRegenerateRequest
// PostApiUsersInvitesTokenAcceptJSONRequestBody defines body for PostApiUsersInvitesTokenAccept for application/json ContentType.
type PostApiUsersInvitesTokenAcceptJSONRequestBody = UserInviteAcceptRequest
// PutApiUsersUserIdJSONRequestBody defines body for PutApiUsersUserId for application/json ContentType. // PutApiUsersUserIdJSONRequestBody defines body for PutApiUsersUserId for application/json ContentType.
type PutApiUsersUserIdJSONRequestBody = UserRequest type PutApiUsersUserIdJSONRequestBody = UserRequest
// PutApiUsersUserIdPasswordJSONRequestBody defines body for PutApiUsersUserIdPassword for application/json ContentType.
type PutApiUsersUserIdPasswordJSONRequestBody = PasswordChangeRequest
// PostApiUsersUserIdTokensJSONRequestBody defines body for PostApiUsersUserIdTokens for application/json ContentType. // PostApiUsersUserIdTokensJSONRequestBody defines body for PostApiUsersUserIdTokens for application/json ContentType.
type PostApiUsersUserIdTokensJSONRequestBody = PersonalAccessTokenRequest type PostApiUsersUserIdTokensJSONRequestBody = PersonalAccessTokenRequest
// AsBundleWorkloadRequest returns the union data inside the WorkloadRequest as a BundleWorkloadRequest
func (t WorkloadRequest) AsBundleWorkloadRequest() (BundleWorkloadRequest, error) {
var body BundleWorkloadRequest
err := json.Unmarshal(t.union, &body)
return body, err
}
// FromBundleWorkloadRequest overwrites any union data inside the WorkloadRequest as the provided BundleWorkloadRequest
func (t *WorkloadRequest) FromBundleWorkloadRequest(v BundleWorkloadRequest) error {
v.Type = "bundle"
b, err := json.Marshal(v)
t.union = b
return err
}
// MergeBundleWorkloadRequest performs a merge with any union data inside the WorkloadRequest, using the provided BundleWorkloadRequest
func (t *WorkloadRequest) MergeBundleWorkloadRequest(v BundleWorkloadRequest) error {
v.Type = "bundle"
b, err := json.Marshal(v)
if err != nil {
return err
}
merged, err := runtime.JSONMerge(t.union, b)
t.union = merged
return err
}
func (t WorkloadRequest) Discriminator() (string, error) {
var discriminator struct {
Discriminator string `json:"type"`
}
err := json.Unmarshal(t.union, &discriminator)
return discriminator.Discriminator, err
}
func (t WorkloadRequest) ValueByDiscriminator() (interface{}, error) {
discriminator, err := t.Discriminator()
if err != nil {
return nil, err
}
switch discriminator {
case "bundle":
return t.AsBundleWorkloadRequest()
default:
return nil, errors.New("unknown discriminator value: " + discriminator)
}
}
func (t WorkloadRequest) MarshalJSON() ([]byte, error) {
b, err := t.union.MarshalJSON()
return b, err
}
func (t *WorkloadRequest) UnmarshalJSON(b []byte) error {
err := t.union.UnmarshalJSON(b)
return err
}
// AsBundleWorkloadResponse returns the union data inside the WorkloadResponse as a BundleWorkloadResponse
func (t WorkloadResponse) AsBundleWorkloadResponse() (BundleWorkloadResponse, error) {
var body BundleWorkloadResponse
err := json.Unmarshal(t.union, &body)
return body, err
}
// FromBundleWorkloadResponse overwrites any union data inside the WorkloadResponse as the provided BundleWorkloadResponse
func (t *WorkloadResponse) FromBundleWorkloadResponse(v BundleWorkloadResponse) error {
v.Type = "bundle"
b, err := json.Marshal(v)
t.union = b
return err
}
// MergeBundleWorkloadResponse performs a merge with any union data inside the WorkloadResponse, using the provided BundleWorkloadResponse
func (t *WorkloadResponse) MergeBundleWorkloadResponse(v BundleWorkloadResponse) error {
v.Type = "bundle"
b, err := json.Marshal(v)
if err != nil {
return err
}
merged, err := runtime.JSONMerge(t.union, b)
t.union = merged
return err
}
func (t WorkloadResponse) Discriminator() (string, error) {
var discriminator struct {
Discriminator string `json:"type"`
}
err := json.Unmarshal(t.union, &discriminator)
return discriminator.Discriminator, err
}
func (t WorkloadResponse) ValueByDiscriminator() (interface{}, error) {
discriminator, err := t.Discriminator()
if err != nil {
return nil, err
}
switch discriminator {
case "bundle":
return t.AsBundleWorkloadResponse()
default:
return nil, errors.New("unknown discriminator value: " + discriminator)
}
}
func (t WorkloadResponse) MarshalJSON() ([]byte, error) {
b, err := t.union.MarshalJSON()
return b, err
}
func (t *WorkloadResponse) UnmarshalJSON(b []byte) error {
err := t.union.UnmarshalJSON(b)
return err
}