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"
"github.com/gorilla/mux"
nbcontext "github.com/netbirdio/netbird/management/server/context"
"github.com/netbirdio/netbird/shared/management/http/api"
"github.com/netbirdio/netbird/shared/management/http/util"

View File

@@ -4,7 +4,7 @@ import (
"context"
"testing"
"github.com/netbirdio/netbird/management/internals/modules/services/domains"
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy/domains"
)
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 {
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 {
log.Fatalf("failed to create API handler: %v", err)
}
@@ -150,7 +150,7 @@ func (s *BaseServer) GRPCServer() *grpc.Server {
}
mgmtProto.RegisterManagementServiceServer(gRPCAPIHandler, srv)
proxyService := nbgrpc.NewProxyServiceServer(s.Store(), s.AccountManager())
proxyService := nbgrpc.NewProxyServiceServer(s.Store())
mgmtProto.RegisterProxyServiceServer(gRPCAPIHandler, proxyService)
log.Info("ProxyService registered on gRPC server")

View File

@@ -8,8 +8,8 @@ import (
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/management/internals/modules/peers"
"github.com/netbirdio/netbird/management/internals/modules/services"
nbservices "github.com/netbirdio/netbird/management/internals/modules/services/manager"
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
nbreverseproxy "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/manager"
"github.com/netbirdio/netbird/management/internals/modules/zones"
zonesManager "github.com/netbirdio/netbird/management/internals/modules/zones/manager"
"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 {
return Create(s, func() services.Manager {
return nbservices.NewManager(s.Store(), s.AccountManager(), s.PermissionsManager())
func (s *BaseServer) ReverseProxyManager() reverseproxy.Manager {
return Create(s, func() reverseproxy.Manager {
return nbreverseproxy.NewManager(s.Store(), s.AccountManager(), s.PermissionsManager())
})
}

View File

@@ -6,18 +6,19 @@ import (
"sync"
"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"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/peer"
"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 {
GetAccountServices(ctx context.Context, lockStrength store.LockingStrength, accountID string) ([]*services.Service, error)
type reverseProxyStore interface {
GetAccountReverseProxies(ctx context.Context, lockStrength store.LockingStrength, accountID string) ([]*reverseproxy.ReverseProxy, error)
}
type keyStore interface {
@@ -31,11 +32,11 @@ type ProxyServiceServer struct {
// Map of connected proxies: proxy_id -> proxy connection
connectedProxies sync.Map
// Channel for broadcasting service updates to all proxies
// Channel for broadcasting reverse proxy updates to all proxies
updatesChan chan *proto.ProxyMapping
// Store of services
serviceStore serviceStore
// Store of reverse proxies
reverseProxyStore reverseProxyStore
// Store for client setup keys
keyStore keyStore
@@ -52,10 +53,10 @@ type proxyConnection struct {
}
// NewProxyServiceServer creates a new proxy service server
func NewProxyServiceServer(store serviceStore) *ProxyServiceServer {
func NewProxyServiceServer(store reverseProxyStore) *ProxyServiceServer {
return &ProxyServiceServer{
updatesChan: make(chan *proto.ProxyMapping, 100),
serviceStore: store,
updatesChan: make(chan *proto.ProxyMapping, 100),
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 {
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 {
// 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 {
if !svc.Enabled || !svc.Exposed {
// We don't care about disabled services for snapshots.
for _, rp := range reverseProxies {
if !rp.Enabled {
// We don't care about disabled reverse proxies for snapshots.
continue
}
// 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.
auth := &proto.Authentication{}
if svc.AuthBearerEnabled {
if rp.Auth.BearerAuth != nil && rp.Auth.BearerAuth.Enabled {
auth.Oidc = &proto.OIDC{
Enabled: true,
// 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{
Enabled: true,
Username: svc.AuthBasicUsername,
Password: svc.AuthBasicPassword,
Username: "",
Password: rp.Auth.PasswordAuth.Password,
}
}
if svc.AuthPINValue != "" {
if rp.Auth.PinAuth != nil && rp.Auth.PinAuth.Pin != "" {
auth.Pin = &proto.Pin{
Enabled: true,
Pin: svc.AuthPINValue,
Pin: rp.Auth.PinAuth.Pin,
}
}
var paths []*proto.PathMapping
for _, t := range svc.Targets {
for _, t := range rp.Targets {
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
}
paths = append(paths, &proto.PathMapping{
Path: t.Path,
Path: *t.Path,
Target: t.Host,
})
}
@@ -179,8 +180,8 @@ func (s *ProxyServiceServer) sendSnapshot(ctx context.Context, conn *proxyConnec
Mapping: []*proto.ProxyMapping{
{
Type: proto.ProxyMappingUpdateType_UPDATE_TYPE_CREATED, // Initial snapshot, all records are "new" for the proxy.
Id: svc.ID,
Domain: svc.Domain,
Id: rp.ID,
Domain: rp.Domain,
Path: paths,
SetupKey: key.Key,
Auth: auth,
@@ -214,34 +215,34 @@ func (s *ProxyServiceServer) sender(conn *proxyConnection, errChan chan<- error)
// SendAccessLog processes access log from proxy
func (s *ProxyServiceServer) SendAccessLog(ctx context.Context, req *proto.SendAccessLogRequest) (*proto.SendAccessLogResponse, error) {
log.WithFields(log.Fields{
"proxy_id": "", // TODO: get proxy id, probably from context or maybe from request message.
"service_id": req.GetLog().GetServiceId(),
"host": req.GetLog().GetHost(),
"path": req.GetLog().GetPath(),
"method": req.GetLog().GetMethod(),
"response_code": req.GetLog().GetResponseCode(),
"duration_ms": req.GetLog().GetDurationMs(),
"source_ip": req.GetLog().GetSourceIp(),
"auth_mechanism": req.GetLog().GetAuthMechanism(),
"user_id": req.GetLog().GetUserId(),
"auth_success": req.GetLog().GetAuthSuccess(),
"proxy_id": "", // TODO: get proxy id, probably from context or maybe from request message.
"reverse_proxy_id": req.GetLog().GetServiceId(),
"host": req.GetLog().GetHost(),
"path": req.GetLog().GetPath(),
"method": req.GetLog().GetMethod(),
"response_code": req.GetLog().GetResponseCode(),
"duration_ms": req.GetLog().GetDurationMs(),
"source_ip": req.GetLog().GetSourceIp(),
"auth_mechanism": req.GetLog().GetAuthMechanism(),
"user_id": req.GetLog().GetUserId(),
"auth_success": req.GetLog().GetAuthSuccess(),
}).Info("Access log from proxy")
// TODO: Store access log in database/metrics system
return &proto.SendAccessLogResponse{}, nil
}
// SendServiceUpdate broadcasts a service update to all connected proxies.
// Management should call this when services are created/updated/removed
func (s *ProxyServiceServer) SendServiceUpdate(update *proto.ProxyMapping) {
// SendReverseProxyUpdate broadcasts a reverse proxy update to all connected proxies.
// Management should call this when reverse proxies are created/updated/removed
func (s *ProxyServiceServer) SendReverseProxyUpdate(update *proto.ProxyMapping) {
// Send it to all connected proxies
s.connectedProxies.Range(func(key, value interface{}) bool {
conn := value.(*proxyConnection)
select {
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:
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
})

View File

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

View File

@@ -12,8 +12,8 @@ import (
"github.com/rs/cors"
log "github.com/sirupsen/logrus"
nbservices "github.com/netbirdio/netbird/management/internals/modules/services"
services "github.com/netbirdio/netbird/management/internals/modules/services/manager"
"github.com/netbirdio/netbird/management/internals/modules/reverseproxy"
reverseproxymanager "github.com/netbirdio/netbird/management/internals/modules/reverseproxy/manager"
idpmanager "github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/management-integrations/integrations"
@@ -62,7 +62,7 @@ const (
)
// 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
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)
instance.AddEndpoints(instanceManager, router)
instance.AddVersionEndpoint(instanceManager, router)
services.RegisterEndpoints(router, serviceManager)
reverseproxymanager.RegisterEndpoints(router, reverseProxyManager)
// Mount embedded IdP handler at /oauth2 path if configured
if embeddedIdpEnabled {

View File

@@ -27,7 +27,7 @@ import (
"gorm.io/gorm/logger"
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/records"
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{},
&installation{}, &types.ExtraSettings{}, &posture.Checks{}, &nbpeer.NetworkAddress{},
&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 {
return nil, fmt.Errorf("auto migratePreAuto: %w", err)
@@ -4604,87 +4604,87 @@ func (s *SqlStore) GetPeerIDByKey(ctx context.Context, lockStrength LockingStren
return peerID, nil
}
func (s *SqlStore) CreateService(ctx context.Context, service *services.Service) error {
result := s.db.Create(service)
func (s *SqlStore) CreateReverseProxy(ctx context.Context, proxy *reverseproxy.ReverseProxy) error {
result := s.db.Create(proxy)
if result.Error != nil {
log.WithContext(ctx).Errorf("failed to create service to store: %v", result.Error)
return status.Errorf(status.Internal, "failed to create service to store")
log.WithContext(ctx).Errorf("failed to create reverse proxy to store: %v", result.Error)
return status.Errorf(status.Internal, "failed to create reverse proxy to store")
}
return nil
}
func (s *SqlStore) UpdateService(ctx context.Context, service *services.Service) error {
result := s.db.Select("*").Save(service)
func (s *SqlStore) UpdateReverseProxy(ctx context.Context, proxy *reverseproxy.ReverseProxy) error {
result := s.db.Select("*").Save(proxy)
if result.Error != nil {
log.WithContext(ctx).Errorf("failed to update service to store: %v", result.Error)
return status.Errorf(status.Internal, "failed to update service to store")
log.WithContext(ctx).Errorf("failed to update reverse proxy to store: %v", result.Error)
return status.Errorf(status.Internal, "failed to update reverse proxy to store")
}
return nil
}
func (s *SqlStore) DeleteService(ctx context.Context, accountID, serviceID string) error {
result := s.db.Delete(&services.Service{}, accountAndIDQueryCondition, accountID, serviceID)
func (s *SqlStore) DeleteReverseProxy(ctx context.Context, accountID, proxyID string) error {
result := s.db.Delete(&reverseproxy.ReverseProxy{}, accountAndIDQueryCondition, accountID, proxyID)
if result.Error != nil {
log.WithContext(ctx).Errorf("failed to delete service from store: %v", result.Error)
return status.Errorf(status.Internal, "failed to delete service from store")
log.WithContext(ctx).Errorf("failed to delete reverse proxy from store: %v", result.Error)
return status.Errorf(status.Internal, "failed to delete reverse proxy from store")
}
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
}
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
if lockStrength != LockingStrengthNone {
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
}
var service *services.Service
result := tx.Take(&service, accountAndIDQueryCondition, accountID, serviceID)
var proxy *reverseproxy.ReverseProxy
result := tx.Take(&proxy, accountAndIDQueryCondition, accountID, proxyID)
if result.Error != nil {
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)
return nil, status.Errorf(status.Internal, "failed to get service from store")
log.WithContext(ctx).Errorf("failed to get reverse proxy from store: %v", result.Error)
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) {
var service *services.Service
result := s.db.Where("account_id = ? AND domain = ?", accountID, domain).First(&service)
func (s *SqlStore) GetReverseProxyByDomain(ctx context.Context, accountID, domain string) (*reverseproxy.ReverseProxy, error) {
var proxy *reverseproxy.ReverseProxy
result := s.db.Where("account_id = ? AND domain = ?", accountID, domain).First(&proxy)
if result.Error != nil {
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)
return nil, status.Errorf(status.Internal, "failed to get service by domain from store")
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 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
if lockStrength != LockingStrengthNone {
tx = tx.Clauses(clause.Locking{Strength: string(lockStrength)})
}
var servicesList []*services.Service
result := tx.Find(&servicesList, accountIDCondition, accountID)
var proxyList []*reverseproxy.ReverseProxy
result := tx.Find(&proxyList, accountIDCondition, accountID)
if result.Error != nil {
log.WithContext(ctx).Errorf("failed to get services from the store: %s", result.Error)
return nil, status.Errorf(status.Internal, "failed to get services from store")
log.WithContext(ctx).Errorf("failed to get reverse proxy from the store: %s", result.Error)
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"
"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/records"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -242,12 +242,12 @@ type Store interface {
MarkAllPendingJobsAsFailed(ctx context.Context, accountID, peerID, reason string) error
GetPeerIDByKey(ctx context.Context, lockStrength LockingStrength, key string) (string, error)
CreateService(ctx context.Context, service *services.Service) error
UpdateService(ctx context.Context, service *services.Service) error
DeleteService(ctx context.Context, accountID, serviceID string) error
GetServiceByID(ctx context.Context, lockStrength LockingStrength, accountID, serviceID string) (*services.Service, error)
GetServiceByDomain(ctx context.Context, accountID, domain string) (*services.Service, error)
GetAccountServices(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*services.Service, error)
CreateReverseProxy(ctx context.Context, service *reverseproxy.ReverseProxy) error
UpdateReverseProxy(ctx context.Context, service *reverseproxy.ReverseProxy) error
DeleteReverseProxy(ctx context.Context, accountID, serviceID string) error
GetReverseProxyByID(ctx context.Context, lockStrength LockingStrength, accountID, serviceID string) (*reverseproxy.ReverseProxy, error)
GetReverseProxyByDomain(ctx context.Context, accountID, domain string) (*reverseproxy.ReverseProxy, error)
GetAccountReverseProxies(ctx context.Context, lockStrength LockingStrength, accountID string) ([]*reverseproxy.ReverseProxy, error)
}
const (

View File

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

View File

@@ -1,10 +1,14 @@
// 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
import (
"encoding/json"
"errors"
"time"
"github.com/oapi-codegen/runtime"
)
const (
@@ -21,56 +25,118 @@ const (
// Defines values for EventActivityCode.
const (
EventActivityCodeAccountCreate EventActivityCode = "account.create"
EventActivityCodeAccountSettingPeerLoginExpirationDisable EventActivityCode = "account.setting.peer.login.expiration.disable"
EventActivityCodeAccountSettingPeerLoginExpirationEnable EventActivityCode = "account.setting.peer.login.expiration.enable"
EventActivityCodeAccountSettingPeerLoginExpirationUpdate EventActivityCode = "account.setting.peer.login.expiration.update"
EventActivityCodeDnsSettingDisabledManagementGroupAdd EventActivityCode = "dns.setting.disabled.management.group.add"
EventActivityCodeDnsSettingDisabledManagementGroupDelete EventActivityCode = "dns.setting.disabled.management.group.delete"
EventActivityCodeGroupAdd EventActivityCode = "group.add"
EventActivityCodeGroupUpdate EventActivityCode = "group.update"
EventActivityCodeNameserverGroupAdd EventActivityCode = "nameserver.group.add"
EventActivityCodeNameserverGroupDelete EventActivityCode = "nameserver.group.delete"
EventActivityCodeNameserverGroupUpdate EventActivityCode = "nameserver.group.update"
EventActivityCodePeerLoginExpirationDisable EventActivityCode = "peer.login.expiration.disable"
EventActivityCodePeerLoginExpirationEnable EventActivityCode = "peer.login.expiration.enable"
EventActivityCodePeerLoginExpire EventActivityCode = "peer.login.expire"
EventActivityCodePeerRename EventActivityCode = "peer.rename"
EventActivityCodePeerSshDisable EventActivityCode = "peer.ssh.disable"
EventActivityCodePeerSshEnable EventActivityCode = "peer.ssh.enable"
EventActivityCodePersonalAccessTokenCreate EventActivityCode = "personal.access.token.create"
EventActivityCodePersonalAccessTokenDelete EventActivityCode = "personal.access.token.delete"
EventActivityCodePolicyAdd EventActivityCode = "policy.add"
EventActivityCodePolicyDelete EventActivityCode = "policy.delete"
EventActivityCodePolicyUpdate EventActivityCode = "policy.update"
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"
EventActivityCodeSetupkeyGroupAdd EventActivityCode = "setupkey.group.add"
EventActivityCodeSetupkeyGroupDelete EventActivityCode = "setupkey.group.delete"
EventActivityCodeSetupkeyOveruse EventActivityCode = "setupkey.overuse"
EventActivityCodeSetupkeyPeerAdd EventActivityCode = "setupkey.peer.add"
EventActivityCodeSetupkeyRevoke EventActivityCode = "setupkey.revoke"
EventActivityCodeSetupkeyUpdate EventActivityCode = "setupkey.update"
EventActivityCodeUserBlock EventActivityCode = "user.block"
EventActivityCodeUserGroupAdd EventActivityCode = "user.group.add"
EventActivityCodeUserGroupDelete EventActivityCode = "user.group.delete"
EventActivityCodeUserInvite EventActivityCode = "user.invite"
EventActivityCodeUserJoin EventActivityCode = "user.join"
EventActivityCodeUserPeerAdd EventActivityCode = "user.peer.add"
EventActivityCodeUserPeerDelete EventActivityCode = "user.peer.delete"
EventActivityCodeUserPeerLogin EventActivityCode = "user.peer.login"
EventActivityCodeUserRoleUpdate EventActivityCode = "user.role.update"
EventActivityCodeUserUnblock EventActivityCode = "user.unblock"
EventActivityCodeAccountCreate EventActivityCode = "account.create"
EventActivityCodeAccountDelete EventActivityCode = "account.delete"
EventActivityCodeAccountDnsDomainUpdate EventActivityCode = "account.dns.domain.update"
EventActivityCodeAccountNetworkRangeUpdate EventActivityCode = "account.network.range.update"
EventActivityCodeAccountPeerInactivityExpirationDisable EventActivityCode = "account.peer.inactivity.expiration.disable"
EventActivityCodeAccountPeerInactivityExpirationEnable EventActivityCode = "account.peer.inactivity.expiration.enable"
EventActivityCodeAccountPeerInactivityExpirationUpdate EventActivityCode = "account.peer.inactivity.expiration.update"
EventActivityCodeAccountSettingGroupPropagationDisable EventActivityCode = "account.setting.group.propagation.disable"
EventActivityCodeAccountSettingGroupPropagationEnable EventActivityCode = "account.setting.group.propagation.enable"
EventActivityCodeAccountSettingLazyConnectionDisable EventActivityCode = "account.setting.lazy.connection.disable"
EventActivityCodeAccountSettingLazyConnectionEnable EventActivityCode = "account.setting.lazy.connection.enable"
EventActivityCodeAccountSettingPeerApprovalDisable EventActivityCode = "account.setting.peer.approval.disable"
EventActivityCodeAccountSettingPeerApprovalEnable EventActivityCode = "account.setting.peer.approval.enable"
EventActivityCodeAccountSettingPeerLoginExpirationDisable EventActivityCode = "account.setting.peer.login.expiration.disable"
EventActivityCodeAccountSettingPeerLoginExpirationEnable EventActivityCode = "account.setting.peer.login.expiration.enable"
EventActivityCodeAccountSettingPeerLoginExpirationUpdate EventActivityCode = "account.setting.peer.login.expiration.update"
EventActivityCodeAccountSettingRoutingPeerDnsResolutionDisable EventActivityCode = "account.setting.routing.peer.dns.resolution.disable"
EventActivityCodeAccountSettingRoutingPeerDnsResolutionEnable EventActivityCode = "account.setting.routing.peer.dns.resolution.enable"
EventActivityCodeAccountSettingsAutoVersionUpdate EventActivityCode = "account.settings.auto.version.update"
EventActivityCodeDashboardLogin EventActivityCode = "dashboard.login"
EventActivityCodeDnsSettingDisabledManagementGroupAdd EventActivityCode = "dns.setting.disabled.management.group.add"
EventActivityCodeDnsSettingDisabledManagementGroupDelete EventActivityCode = "dns.setting.disabled.management.group.delete"
EventActivityCodeDnsZoneCreate EventActivityCode = "dns.zone.create"
EventActivityCodeDnsZoneDelete EventActivityCode = "dns.zone.delete"
EventActivityCodeDnsZoneRecordCreate EventActivityCode = "dns.zone.record.create"
EventActivityCodeDnsZoneRecordDelete EventActivityCode = "dns.zone.record.delete"
EventActivityCodeDnsZoneRecordUpdate EventActivityCode = "dns.zone.record.update"
EventActivityCodeDnsZoneUpdate EventActivityCode = "dns.zone.update"
EventActivityCodeGroupAdd EventActivityCode = "group.add"
EventActivityCodeGroupDelete EventActivityCode = "group.delete"
EventActivityCodeGroupUpdate EventActivityCode = "group.update"
EventActivityCodeIdentityproviderCreate EventActivityCode = "identityprovider.create"
EventActivityCodeIdentityproviderDelete EventActivityCode = "identityprovider.delete"
EventActivityCodeIdentityproviderUpdate EventActivityCode = "identityprovider.update"
EventActivityCodeIntegrationCreate EventActivityCode = "integration.create"
EventActivityCodeIntegrationDelete EventActivityCode = "integration.delete"
EventActivityCodeIntegrationUpdate EventActivityCode = "integration.update"
EventActivityCodeNameserverGroupAdd EventActivityCode = "nameserver.group.add"
EventActivityCodeNameserverGroupDelete EventActivityCode = "nameserver.group.delete"
EventActivityCodeNameserverGroupUpdate EventActivityCode = "nameserver.group.update"
EventActivityCodeNetworkCreate EventActivityCode = "network.create"
EventActivityCodeNetworkDelete EventActivityCode = "network.delete"
EventActivityCodeNetworkResourceCreate EventActivityCode = "network.resource.create"
EventActivityCodeNetworkResourceDelete EventActivityCode = "network.resource.delete"
EventActivityCodeNetworkResourceUpdate EventActivityCode = "network.resource.update"
EventActivityCodeNetworkRouterCreate EventActivityCode = "network.router.create"
EventActivityCodeNetworkRouterDelete EventActivityCode = "network.router.delete"
EventActivityCodeNetworkRouterUpdate EventActivityCode = "network.router.update"
EventActivityCodeNetworkUpdate EventActivityCode = "network.update"
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.
@@ -125,6 +191,13 @@ const (
IngressPortAllocationRequestPortRangeProtocolUdp IngressPortAllocationRequestPortRangeProtocol = "udp"
)
// Defines values for JobResponseStatus.
const (
JobResponseStatusFailed JobResponseStatus = "failed"
JobResponseStatusPending JobResponseStatus = "pending"
JobResponseStatusSucceeded JobResponseStatus = "succeeded"
)
// Defines values for NameserverNsType.
const (
NameserverNsTypeUdp NameserverNsType = "udp"
@@ -196,14 +269,6 @@ const (
ResourceTypeSubnet ResourceType = "subnet"
)
// Defines values for ReverseProxyAuthConfigType.
const (
ReverseProxyAuthConfigTypeBearer ReverseProxyAuthConfigType = "bearer"
ReverseProxyAuthConfigTypeLink ReverseProxyAuthConfigType = "link"
ReverseProxyAuthConfigTypePassword ReverseProxyAuthConfigType = "password"
ReverseProxyAuthConfigTypePin ReverseProxyAuthConfigType = "pin"
)
// Defines values for ReverseProxyDomainType.
const (
ReverseProxyDomainTypeCustom ReverseProxyDomainType = "custom"
@@ -229,6 +294,11 @@ const (
UserStatusInvited UserStatus = "invited"
)
// Defines values for WorkloadType.
const (
WorkloadTypeBundle WorkloadType = "bundle"
)
// Defines values for GetApiEventsNetworkTrafficParamsType.
const (
GetApiEventsNetworkTrafficParamsTypeTYPEDROP GetApiEventsNetworkTrafficParamsType = "TYPE_DROP"
@@ -406,6 +476,47 @@ type BearerAuthConfig struct {
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
type Checks struct {
// GeoLocationCheck Posture check for geo location
@@ -793,6 +904,40 @@ type InstanceStatus struct {
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.
type LinkAuthConfig struct {
// Enabled Whether link auth is enabled
@@ -1187,6 +1332,15 @@ type PasswordAuthConfig struct {
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.
type Peer struct {
// ApprovalRequired (Cloud only) Indicates whether peer needs approval
@@ -1774,14 +1928,8 @@ type ReverseProxyAuthConfig struct {
LinkAuth *LinkAuthConfig `json:"link_auth,omitempty"`
PasswordAuth *PasswordAuthConfig `json:"password_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.
type ReverseProxyDomain struct {
// Domain Domain name
@@ -2190,6 +2338,99 @@ type UserCreateRequest struct {
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.
type UserPermissions struct {
// IsRestricted Indicates whether this User's Peers view is restricted
@@ -2209,6 +2450,20 @@ type UserRequest struct {
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.
type Zone struct {
// 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.
type PutApiPeersPeerIdIngressPortsAllocationIdJSONRequestBody = IngressPortAllocationRequest
// PostApiPeersPeerIdJobsJSONRequestBody defines body for PostApiPeersPeerIdJobs for application/json ContentType.
type PostApiPeersPeerIdJobsJSONRequestBody = JobRequest
// PostApiPeersPeerIdTemporaryAccessJSONRequestBody defines body for PostApiPeersPeerIdTemporaryAccess for application/json ContentType.
type PostApiPeersPeerIdTemporaryAccessJSONRequestBody = PeerTemporaryAccessRequest
@@ -2434,8 +2692,138 @@ type PutApiSetupKeysKeyIdJSONRequestBody = SetupKeyRequest
// PostApiUsersJSONRequestBody defines body for PostApiUsers for application/json ContentType.
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.
type PutApiUsersUserIdJSONRequestBody = UserRequest
// PutApiUsersUserIdPasswordJSONRequestBody defines body for PutApiUsersUserIdPassword for application/json ContentType.
type PutApiUsersUserIdPasswordJSONRequestBody = PasswordChangeRequest
// PostApiUsersUserIdTokensJSONRequestBody defines body for PostApiUsersUserIdTokens for application/json ContentType.
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
}