rename reverse proxy to services

This commit is contained in:
pascal
2026-02-11 21:39:51 +01:00
parent ebb1f4007d
commit 08ab1e3478
32 changed files with 1125 additions and 1471 deletions

View File

@@ -13,7 +13,7 @@ import (
type AccessLogEntry struct {
ID string `gorm:"primaryKey"`
AccountID string `gorm:"index"`
ProxyID string `gorm:"index"`
ServiceID string `gorm:"index"`
Timestamp time.Time `gorm:"index"`
GeoLocation peer.Location `gorm:"embedded;embeddedPrefix:location_"`
Method string `gorm:"index"`
@@ -27,28 +27,28 @@ type AccessLogEntry struct {
}
// FromProto creates an AccessLogEntry from a proto.AccessLog
func (a *AccessLogEntry) FromProto(proxyLog *proto.AccessLog) {
a.ID = proxyLog.GetLogId()
a.ProxyID = proxyLog.GetServiceId()
a.Timestamp = proxyLog.GetTimestamp().AsTime()
a.Method = proxyLog.GetMethod()
a.Host = proxyLog.GetHost()
a.Path = proxyLog.GetPath()
a.Duration = time.Duration(proxyLog.GetDurationMs()) * time.Millisecond
a.StatusCode = int(proxyLog.GetResponseCode())
a.UserId = proxyLog.GetUserId()
a.AuthMethodUsed = proxyLog.GetAuthMechanism()
a.AccountID = proxyLog.GetAccountId()
func (a *AccessLogEntry) FromProto(serviceLog *proto.AccessLog) {
a.ID = serviceLog.GetLogId()
a.ServiceID = serviceLog.GetServiceId()
a.Timestamp = serviceLog.GetTimestamp().AsTime()
a.Method = serviceLog.GetMethod()
a.Host = serviceLog.GetHost()
a.Path = serviceLog.GetPath()
a.Duration = time.Duration(serviceLog.GetDurationMs()) * time.Millisecond
a.StatusCode = int(serviceLog.GetResponseCode())
a.UserId = serviceLog.GetUserId()
a.AuthMethodUsed = serviceLog.GetAuthMechanism()
a.AccountID = serviceLog.GetAccountId()
if sourceIP := proxyLog.GetSourceIp(); sourceIP != "" {
if sourceIP := serviceLog.GetSourceIp(); sourceIP != "" {
if ip, err := netip.ParseAddr(sourceIP); err == nil {
a.GeoLocation.ConnectionIP = net.IP(ip.AsSlice())
}
}
if !proxyLog.GetAuthSuccess() {
if !serviceLog.GetAuthSuccess() {
a.Reason = "Authentication failed"
} else if proxyLog.GetResponseCode() >= 400 {
} else if serviceLog.GetResponseCode() >= 400 {
a.Reason = "Request failed"
}
}
@@ -88,7 +88,7 @@ func (a *AccessLogEntry) ToAPIResponse() *api.ProxyAccessLog {
return &api.ProxyAccessLog{
Id: a.ID,
ProxyId: a.ProxyID,
ServiceId: a.ServiceID,
Timestamp: a.Timestamp,
Method: a.Method,
Host: a.Host,

View File

@@ -44,11 +44,11 @@ func (m *managerImpl) SaveAccessLog(ctx context.Context, logEntry *accesslogs.Ac
if err := m.store.CreateAccessLog(ctx, logEntry); err != nil {
log.WithContext(ctx).WithFields(log.Fields{
"proxy_id": logEntry.ProxyID,
"method": logEntry.Method,
"host": logEntry.Host,
"path": logEntry.Path,
"status": logEntry.StatusCode,
"service_id": logEntry.ServiceID,
"method": logEntry.Method,
"host": logEntry.Host,
"path": logEntry.Path,
"status": logEntry.StatusCode,
}).Errorf("failed to save access log: %v", err)
return err
}

View File

@@ -5,17 +5,17 @@ import (
)
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
SetCertificateIssuedAt(ctx context.Context, accountID, reverseProxyID string) error
SetStatus(ctx context.Context, accountID, reverseProxyID string, status ProxyStatus) error
ReloadAllReverseProxiesForAccount(ctx context.Context, accountID string) error
ReloadReverseProxy(ctx context.Context, accountID, reverseProxyID string) error
GetGlobalReverseProxies(ctx context.Context) ([]*ReverseProxy, error)
GetProxyByID(ctx context.Context, accountID, reverseProxyID string) (*ReverseProxy, error)
GetAccountReverseProxies(ctx context.Context, accountID string) ([]*ReverseProxy, error)
GetProxyIDByTargetID(ctx context.Context, accountID string, resourceID string) (string, error)
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
SetCertificateIssuedAt(ctx context.Context, accountID, serviceID string) error
SetStatus(ctx context.Context, accountID, serviceID string, status ProxyStatus) error
ReloadAllServicesForAccount(ctx context.Context, accountID string) error
ReloadService(ctx context.Context, accountID, serviceID string) error
GetGlobalServices(ctx context.Context) ([]*Service, error)
GetServiceByID(ctx context.Context, accountID, serviceID string) (*Service, error)
GetAccountServices(ctx context.Context, accountID string) ([]*Service, error)
GetServiceIDByTargetID(ctx context.Context, accountID string, resourceID string) (string, error)
}

View File

@@ -20,148 +20,148 @@ type handler struct {
manager reverseproxy.Manager
}
// RegisterEndpoints registers all reverse proxy HTTP endpoints.
// RegisterEndpoints registers all service HTTP endpoints.
func RegisterEndpoints(manager reverseproxy.Manager, domainManager domain.Manager, accessLogsManager accesslogs.Manager, router *mux.Router) {
h := &handler{
manager: manager,
}
domainRouter := router.PathPrefix("/reverse-proxies").Subrouter()
domainRouter := router.PathPrefix("/services").Subrouter()
domain.RegisterEndpoints(domainRouter, domainManager)
accesslogsmanager.RegisterEndpoints(router, accessLogsManager)
router.HandleFunc("/reverse-proxies", h.getAllReverseProxies).Methods("GET", "OPTIONS")
router.HandleFunc("/reverse-proxies", h.createReverseProxy).Methods("POST", "OPTIONS")
router.HandleFunc("/reverse-proxies/{proxyId}", h.getReverseProxy).Methods("GET", "OPTIONS")
router.HandleFunc("/reverse-proxies/{proxyId}", h.updateReverseProxy).Methods("PUT", "OPTIONS")
router.HandleFunc("/reverse-proxies/{proxyId}", h.deleteReverseProxy).Methods("DELETE", "OPTIONS")
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) getAllReverseProxies(w http.ResponseWriter, r *http.Request) {
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
}
allReverseProxies, err := h.manager.GetAllReverseProxies(r.Context(), userAuth.AccountId, userAuth.UserId)
allServices, err := h.manager.GetAllServices(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())
apiServices := make([]*api.Service, 0, len(allServices))
for _, service := range allServices {
apiServices = append(apiServices, service.ToAPIResponse())
}
util.WriteJSONObject(r.Context(), w, apiReverseProxies)
util.WriteJSONObject(r.Context(), w, apiServices)
}
func (h *handler) createReverseProxy(w http.ResponseWriter, r *http.Request) {
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.ReverseProxyRequest
var req api.ServiceRequest
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, userAuth.AccountId)
service := new(reverseproxy.Service)
service.FromAPIRequest(&req, userAuth.AccountId)
if err = reverseProxy.Validate(); err != nil {
if err = service.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)
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, createdReverseProxy.ToAPIResponse())
util.WriteJSONObject(r.Context(), w, createdService.ToAPIResponse())
}
func (h *handler) getReverseProxy(w http.ResponseWriter, r *http.Request) {
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
}
reverseProxyID := mux.Vars(r)["proxyId"]
if reverseProxyID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "reverse proxy ID is required"), w)
serviceID := mux.Vars(r)["serviceId"]
if serviceID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "service ID is required"), w)
return
}
reverseProxy, err := h.manager.GetReverseProxy(r.Context(), userAuth.AccountId, userAuth.UserId, reverseProxyID)
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, reverseProxy.ToAPIResponse())
util.WriteJSONObject(r.Context(), w, service.ToAPIResponse())
}
func (h *handler) updateReverseProxy(w http.ResponseWriter, r *http.Request) {
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
}
reverseProxyID := mux.Vars(r)["proxyId"]
if reverseProxyID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "reverse proxy ID is required"), w)
serviceID := mux.Vars(r)["serviceId"]
if serviceID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "service ID is required"), w)
return
}
var req api.ReverseProxyRequest
var req api.ServiceRequest
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, userAuth.AccountId)
service := new(reverseproxy.Service)
service.ID = serviceID
service.FromAPIRequest(&req, userAuth.AccountId)
if err = reverseProxy.Validate(); err != nil {
if err = service.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)
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, updatedReverseProxy.ToAPIResponse())
util.WriteJSONObject(r.Context(), w, updatedService.ToAPIResponse())
}
func (h *handler) deleteReverseProxy(w http.ResponseWriter, r *http.Request) {
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
}
reverseProxyID := mux.Vars(r)["proxyId"]
if reverseProxyID == "" {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "reverse proxy ID is required"), w)
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.DeleteReverseProxy(r.Context(), userAuth.AccountId, userAuth.UserId, reverseProxyID); err != nil {
if err := h.manager.DeleteService(r.Context(), userAuth.AccountId, userAuth.UserId, serviceID); err != nil {
util.WriteError(r.Context(), err, w)
return
}

View File

@@ -35,7 +35,7 @@ type managerImpl struct {
clusterDeriver ClusterDeriver
}
// NewManager creates a new reverse proxy manager.
// NewManager creates a new service manager.
func NewManager(store store.Store, accountManager account.Manager, permissionsManager permissions.Manager, proxyGRPCServer *nbgrpc.ProxyServiceServer, tokenStore *nbgrpc.OneTimeTokenStore, clusterDeriver ClusterDeriver) reverseproxy.Manager {
return &managerImpl{
store: store,
@@ -47,7 +47,7 @@ func NewManager(store store.Store, accountManager account.Manager, permissionsMa
}
}
func (m *managerImpl) GetAllReverseProxies(ctx context.Context, accountID, userID string) ([]*reverseproxy.ReverseProxy, error) {
func (m *managerImpl) GetAllServices(ctx context.Context, accountID, userID string) ([]*reverseproxy.Service, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
if err != nil {
return nil, status.NewPermissionValidationError(err)
@@ -56,28 +56,28 @@ func (m *managerImpl) GetAllReverseProxies(ctx context.Context, accountID, userI
return nil, status.NewPermissionDeniedError()
}
proxies, err := m.store.GetAccountReverseProxies(ctx, store.LockingStrengthNone, accountID)
services, err := m.store.GetAccountServices(ctx, store.LockingStrengthNone, accountID)
if err != nil {
return nil, fmt.Errorf("failed to get reverse proxies: %w", err)
return nil, fmt.Errorf("failed to get services: %w", err)
}
for _, proxy := range proxies {
err = m.replaceHostByLookup(ctx, accountID, proxy)
for _, service := range services {
err = m.replaceHostByLookup(ctx, accountID, service)
if err != nil {
return nil, fmt.Errorf("failed to replace host by lookup for proxy %s: %w", proxy.ID, err)
return nil, fmt.Errorf("failed to replace host by lookup for service %s: %w", service.ID, err)
}
}
return proxies, nil
return services, nil
}
func (m *managerImpl) replaceHostByLookup(ctx context.Context, accountID string, proxy *reverseproxy.ReverseProxy) error {
for _, target := range proxy.Targets {
func (m *managerImpl) replaceHostByLookup(ctx context.Context, accountID string, service *reverseproxy.Service) error {
for _, target := range service.Targets {
switch target.TargetType {
case reverseproxy.TargetTypePeer:
peer, err := m.store.GetPeerByID(ctx, store.LockingStrengthNone, accountID, target.TargetId)
if err != nil {
log.WithContext(ctx).Warnf("failed to get peer by id %s for reverse proxy %s: %v", target.TargetId, proxy.ID, err)
log.WithContext(ctx).Warnf("failed to get peer by id %s for service %s: %v", target.TargetId, service.ID, err)
target.Host = unknownHostPlaceholder
continue
}
@@ -85,7 +85,7 @@ func (m *managerImpl) replaceHostByLookup(ctx context.Context, accountID string,
case reverseproxy.TargetTypeHost:
resource, err := m.store.GetNetworkResourceByID(ctx, store.LockingStrengthNone, accountID, target.TargetId)
if err != nil {
log.WithContext(ctx).Warnf("failed to get resource by id %s for reverse proxy %s: %v", target.TargetId, proxy.ID, err)
log.WithContext(ctx).Warnf("failed to get resource by id %s for service %s: %v", target.TargetId, service.ID, err)
target.Host = unknownHostPlaceholder
continue
}
@@ -93,7 +93,7 @@ func (m *managerImpl) replaceHostByLookup(ctx context.Context, accountID string,
case reverseproxy.TargetTypeDomain:
resource, err := m.store.GetNetworkResourceByID(ctx, store.LockingStrengthNone, accountID, target.TargetId)
if err != nil {
log.WithContext(ctx).Warnf("failed to get resource by id %s for reverse proxy %s: %v", target.TargetId, proxy.ID, err)
log.WithContext(ctx).Warnf("failed to get resource by id %s for service %s: %v", target.TargetId, service.ID, err)
target.Host = unknownHostPlaceholder
continue
}
@@ -107,7 +107,7 @@ func (m *managerImpl) replaceHostByLookup(ctx context.Context, accountID string,
return nil
}
func (m *managerImpl) GetReverseProxy(ctx context.Context, accountID, userID, reverseProxyID string) (*reverseproxy.ReverseProxy, error) {
func (m *managerImpl) GetService(ctx context.Context, accountID, userID, serviceID string) (*reverseproxy.Service, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Read)
if err != nil {
return nil, status.NewPermissionValidationError(err)
@@ -116,19 +116,19 @@ func (m *managerImpl) GetReverseProxy(ctx context.Context, accountID, userID, re
return nil, status.NewPermissionDeniedError()
}
proxy, err := m.store.GetReverseProxyByID(ctx, store.LockingStrengthNone, accountID, reverseProxyID)
service, err := m.store.GetServiceByID(ctx, store.LockingStrengthNone, accountID, serviceID)
if err != nil {
return nil, fmt.Errorf("failed to get reverse proxy: %w", err)
return nil, fmt.Errorf("failed to get service: %w", err)
}
err = m.replaceHostByLookup(ctx, accountID, proxy)
err = m.replaceHostByLookup(ctx, accountID, service)
if err != nil {
return nil, fmt.Errorf("failed to replace host by lookup for proxy %s: %w", proxy.ID, err)
return nil, fmt.Errorf("failed to replace host by lookup for service %s: %w", service.ID, err)
}
return proxy, nil
return service, nil
}
func (m *managerImpl) CreateReverseProxy(ctx context.Context, accountID, userID string, reverseProxy *reverseproxy.ReverseProxy) (*reverseproxy.ReverseProxy, error) {
func (m *managerImpl) CreateService(ctx context.Context, accountID, userID string, service *reverseproxy.Service) (*reverseproxy.Service, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Create)
if err != nil {
return nil, status.NewPermissionValidationError(err)
@@ -139,16 +139,16 @@ func (m *managerImpl) CreateReverseProxy(ctx context.Context, accountID, userID
var proxyCluster string
if m.clusterDeriver != nil {
proxyCluster, err = m.clusterDeriver.DeriveClusterFromDomain(ctx, reverseProxy.Domain)
proxyCluster, err = m.clusterDeriver.DeriveClusterFromDomain(ctx, service.Domain)
if err != nil {
log.WithError(err).Warnf("could not derive cluster from domain %s, updates will broadcast to all proxies", reverseProxy.Domain)
log.WithError(err).Warnf("could not derive cluster from domain %s, updates will broadcast to all proxy servers", service.Domain)
}
}
reverseProxy.AccountID = accountID
reverseProxy.ProxyCluster = proxyCluster
reverseProxy.InitNewRecord()
err = reverseProxy.Auth.HashSecrets()
service.AccountID = accountID
service.ProxyCluster = proxyCluster
service.InitNewRecord()
err = service.Auth.HashSecrets()
if err != nil {
return nil, fmt.Errorf("hash secrets: %w", err)
}
@@ -158,27 +158,27 @@ func (m *managerImpl) CreateReverseProxy(ctx context.Context, accountID, userID
if err != nil {
return nil, fmt.Errorf("generate session keys: %w", err)
}
reverseProxy.SessionPrivateKey = keyPair.PrivateKey
reverseProxy.SessionPublicKey = keyPair.PublicKey
service.SessionPrivateKey = keyPair.PrivateKey
service.SessionPublicKey = keyPair.PublicKey
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
// Check for duplicate domain
existingReverseProxy, err := transaction.GetReverseProxyByDomain(ctx, accountID, reverseProxy.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 reverse proxy: %w", err)
return fmt.Errorf("failed to check existing service: %w", err)
}
}
if existingReverseProxy != nil {
return status.Errorf(status.AlreadyExists, "reverse proxy with domain %s already exists", reverseProxy.Domain)
if existingService != nil {
return status.Errorf(status.AlreadyExists, "service with domain %s already exists", service.Domain)
}
if err = validateTargetReferences(ctx, transaction, accountID, reverseProxy.Targets); err != nil {
if err = validateTargetReferences(ctx, transaction, accountID, service.Targets); err != nil {
return err
}
if err = transaction.CreateReverseProxy(ctx, reverseProxy); err != nil {
return fmt.Errorf("failed to create reverse proxy: %w", err)
if err = transaction.CreateService(ctx, service); err != nil {
return fmt.Errorf("failed to create service: %w", err)
}
return nil
@@ -187,26 +187,26 @@ func (m *managerImpl) CreateReverseProxy(ctx context.Context, accountID, userID
return nil, err
}
token, err := m.tokenStore.GenerateToken(accountID, reverseProxy.ID, 5*time.Minute)
token, err := m.tokenStore.GenerateToken(accountID, service.ID, 5*time.Minute)
if err != nil {
return nil, fmt.Errorf("failed to generate authentication token: %w", err)
}
m.accountManager.StoreEvent(ctx, userID, reverseProxy.ID, accountID, activity.ReverseProxyCreated, reverseProxy.EventMeta())
m.accountManager.StoreEvent(ctx, userID, service.ID, accountID, activity.ServiceCreated, service.EventMeta())
err = m.replaceHostByLookup(ctx, accountID, reverseProxy)
err = m.replaceHostByLookup(ctx, accountID, service)
if err != nil {
return nil, fmt.Errorf("failed to replace host by lookup for proxy %s: %w", reverseProxy.ID, err)
return nil, fmt.Errorf("failed to replace host by lookup for service %s: %w", service.ID, err)
}
m.proxyGRPCServer.SendReverseProxyUpdateToCluster(reverseProxy.ToProtoMapping(reverseproxy.Create, token, m.proxyGRPCServer.GetOIDCValidationConfig()), reverseProxy.ProxyCluster)
m.proxyGRPCServer.SendServiceUpdateToCluster(service.ToProtoMapping(reverseproxy.Create, token, m.proxyGRPCServer.GetOIDCValidationConfig()), service.ProxyCluster)
m.accountManager.UpdateAccountPeers(ctx, accountID)
return reverseProxy, nil
return service, nil
}
func (m *managerImpl) UpdateReverseProxy(ctx context.Context, accountID, userID string, reverseProxy *reverseproxy.ReverseProxy) (*reverseproxy.ReverseProxy, error) {
func (m *managerImpl) UpdateService(ctx context.Context, accountID, userID string, service *reverseproxy.Service) (*reverseproxy.Service, error) {
ok, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Services, operations.Update)
if err != nil {
return nil, status.NewPermissionValidationError(err)
@@ -217,67 +217,67 @@ func (m *managerImpl) UpdateReverseProxy(ctx context.Context, accountID, userID
var oldCluster string
var domainChanged bool
var reverseProxyEnabledChanged bool
var serviceEnabledChanged bool
err = reverseProxy.Auth.HashSecrets()
err = service.Auth.HashSecrets()
if err != nil {
return nil, fmt.Errorf("hash secrets: %w", err)
}
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
existingReverseProxy, err := transaction.GetReverseProxyByID(ctx, store.LockingStrengthUpdate, accountID, reverseProxy.ID)
existingService, err := transaction.GetServiceByID(ctx, store.LockingStrengthUpdate, accountID, service.ID)
if err != nil {
return err
}
oldCluster = existingReverseProxy.ProxyCluster
oldCluster = existingService.ProxyCluster
if existingReverseProxy.Domain != reverseProxy.Domain {
if existingService.Domain != service.Domain {
domainChanged = true
conflictReverseProxy, err := transaction.GetReverseProxyByDomain(ctx, accountID, reverseProxy.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("check existing reverse proxy: %w", err)
return fmt.Errorf("check existing service: %w", err)
}
}
if conflictReverseProxy != nil && conflictReverseProxy.ID != reverseProxy.ID {
return status.Errorf(status.AlreadyExists, "reverse proxy with domain %s already exists", reverseProxy.Domain)
if conflictService != nil && conflictService.ID != service.ID {
return status.Errorf(status.AlreadyExists, "service with domain %s already exists", service.Domain)
}
if m.clusterDeriver != nil {
newCluster, err := m.clusterDeriver.DeriveClusterFromDomain(ctx, reverseProxy.Domain)
newCluster, err := m.clusterDeriver.DeriveClusterFromDomain(ctx, service.Domain)
if err != nil {
log.WithError(err).Warnf("could not derive cluster from domain %s", reverseProxy.Domain)
log.WithError(err).Warnf("could not derive cluster from domain %s", service.Domain)
}
reverseProxy.ProxyCluster = newCluster
service.ProxyCluster = newCluster
}
} else {
reverseProxy.ProxyCluster = existingReverseProxy.ProxyCluster
service.ProxyCluster = existingService.ProxyCluster
}
if reverseProxy.Auth.PasswordAuth != nil && reverseProxy.Auth.PasswordAuth.Enabled &&
existingReverseProxy.Auth.PasswordAuth != nil && existingReverseProxy.Auth.PasswordAuth.Enabled &&
reverseProxy.Auth.PasswordAuth.Password == "" {
reverseProxy.Auth.PasswordAuth = existingReverseProxy.Auth.PasswordAuth
if service.Auth.PasswordAuth != nil && service.Auth.PasswordAuth.Enabled &&
existingService.Auth.PasswordAuth != nil && existingService.Auth.PasswordAuth.Enabled &&
service.Auth.PasswordAuth.Password == "" {
service.Auth.PasswordAuth = existingService.Auth.PasswordAuth
}
if reverseProxy.Auth.PinAuth != nil && reverseProxy.Auth.PinAuth.Enabled &&
existingReverseProxy.Auth.PinAuth != nil && existingReverseProxy.Auth.PinAuth.Enabled &&
reverseProxy.Auth.PinAuth.Pin == "" {
reverseProxy.Auth.PinAuth = existingReverseProxy.Auth.PinAuth
if service.Auth.PinAuth != nil && service.Auth.PinAuth.Enabled &&
existingService.Auth.PinAuth != nil && existingService.Auth.PinAuth.Enabled &&
service.Auth.PinAuth.Pin == "" {
service.Auth.PinAuth = existingService.Auth.PinAuth
}
reverseProxy.Meta = existingReverseProxy.Meta
reverseProxy.SessionPrivateKey = existingReverseProxy.SessionPrivateKey
reverseProxy.SessionPublicKey = existingReverseProxy.SessionPublicKey
reverseProxyEnabledChanged = existingReverseProxy.Enabled != reverseProxy.Enabled
service.Meta = existingService.Meta
service.SessionPrivateKey = existingService.SessionPrivateKey
service.SessionPublicKey = existingService.SessionPublicKey
serviceEnabledChanged = existingService.Enabled != service.Enabled
if err = validateTargetReferences(ctx, transaction, accountID, reverseProxy.Targets); err != nil {
if err = validateTargetReferences(ctx, transaction, accountID, service.Targets); err != nil {
return err
}
if err = transaction.UpdateReverseProxy(ctx, reverseProxy); err != nil {
return fmt.Errorf("update reverse proxy: %w", err)
if err = transaction.UpdateService(ctx, service); err != nil {
return fmt.Errorf("update service: %w", err)
}
return nil
@@ -286,33 +286,33 @@ func (m *managerImpl) UpdateReverseProxy(ctx context.Context, accountID, userID
return nil, err
}
m.accountManager.StoreEvent(ctx, userID, reverseProxy.ID, accountID, activity.ReverseProxyUpdated, reverseProxy.EventMeta())
m.accountManager.StoreEvent(ctx, userID, service.ID, accountID, activity.ServiceUpdated, service.EventMeta())
err = m.replaceHostByLookup(ctx, accountID, reverseProxy)
err = m.replaceHostByLookup(ctx, accountID, service)
if err != nil {
return nil, fmt.Errorf("failed to replace host by lookup for proxy %s: %w", reverseProxy.ID, err)
return nil, fmt.Errorf("failed to replace host by lookup for service %s: %w", service.ID, err)
}
token, err := m.tokenStore.GenerateToken(accountID, reverseProxy.ID, 5*time.Minute)
token, err := m.tokenStore.GenerateToken(accountID, service.ID, 5*time.Minute)
if err != nil {
return nil, fmt.Errorf("failed to generate authentication token: %w", err)
}
switch {
case domainChanged && oldCluster != reverseProxy.ProxyCluster:
m.proxyGRPCServer.SendReverseProxyUpdateToCluster(reverseProxy.ToProtoMapping(reverseproxy.Delete, "", m.proxyGRPCServer.GetOIDCValidationConfig()), oldCluster)
m.proxyGRPCServer.SendReverseProxyUpdateToCluster(reverseProxy.ToProtoMapping(reverseproxy.Create, token, m.proxyGRPCServer.GetOIDCValidationConfig()), reverseProxy.ProxyCluster)
case !reverseProxy.Enabled && reverseProxyEnabledChanged:
m.proxyGRPCServer.SendReverseProxyUpdateToCluster(reverseProxy.ToProtoMapping(reverseproxy.Delete, "", m.proxyGRPCServer.GetOIDCValidationConfig()), reverseProxy.ProxyCluster)
case reverseProxy.Enabled && reverseProxyEnabledChanged:
m.proxyGRPCServer.SendReverseProxyUpdateToCluster(reverseProxy.ToProtoMapping(reverseproxy.Create, token, m.proxyGRPCServer.GetOIDCValidationConfig()), reverseProxy.ProxyCluster)
case domainChanged && oldCluster != service.ProxyCluster:
m.proxyGRPCServer.SendServiceUpdateToCluster(service.ToProtoMapping(reverseproxy.Delete, "", m.proxyGRPCServer.GetOIDCValidationConfig()), oldCluster)
m.proxyGRPCServer.SendServiceUpdateToCluster(service.ToProtoMapping(reverseproxy.Create, token, m.proxyGRPCServer.GetOIDCValidationConfig()), service.ProxyCluster)
case !service.Enabled && serviceEnabledChanged:
m.proxyGRPCServer.SendServiceUpdateToCluster(service.ToProtoMapping(reverseproxy.Delete, "", m.proxyGRPCServer.GetOIDCValidationConfig()), service.ProxyCluster)
case service.Enabled && serviceEnabledChanged:
m.proxyGRPCServer.SendServiceUpdateToCluster(service.ToProtoMapping(reverseproxy.Create, token, m.proxyGRPCServer.GetOIDCValidationConfig()), service.ProxyCluster)
default:
m.proxyGRPCServer.SendReverseProxyUpdateToCluster(reverseProxy.ToProtoMapping(reverseproxy.Update, "", m.proxyGRPCServer.GetOIDCValidationConfig()), reverseProxy.ProxyCluster)
m.proxyGRPCServer.SendServiceUpdateToCluster(service.ToProtoMapping(reverseproxy.Update, "", m.proxyGRPCServer.GetOIDCValidationConfig()), service.ProxyCluster)
}
m.accountManager.UpdateAccountPeers(ctx, accountID)
return reverseProxy, nil
return service, nil
}
// validateTargetReferences checks that all target IDs reference existing peers or resources in the account.
@@ -338,7 +338,7 @@ func validateTargetReferences(ctx context.Context, transaction store.Store, acco
return nil
}
func (m *managerImpl) DeleteReverseProxy(ctx context.Context, accountID, userID, reverseProxyID string) error {
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)
@@ -347,16 +347,16 @@ func (m *managerImpl) DeleteReverseProxy(ctx context.Context, accountID, userID,
return status.NewPermissionDeniedError()
}
var reverseProxy *reverseproxy.ReverseProxy
var service *reverseproxy.Service
err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
var err error
reverseProxy, err = transaction.GetReverseProxyByID(ctx, store.LockingStrengthUpdate, accountID, reverseProxyID)
service, err = transaction.GetServiceByID(ctx, store.LockingStrengthUpdate, accountID, serviceID)
if err != nil {
return err
}
if err = transaction.DeleteReverseProxy(ctx, accountID, reverseProxyID); err != nil {
return fmt.Errorf("failed to delete reverse proxy: %w", err)
if err = transaction.DeleteService(ctx, accountID, serviceID); err != nil {
return fmt.Errorf("failed to delete service: %w", err)
}
return nil
@@ -365,9 +365,9 @@ func (m *managerImpl) DeleteReverseProxy(ctx context.Context, accountID, userID,
return err
}
m.accountManager.StoreEvent(ctx, userID, reverseProxyID, accountID, activity.ReverseProxyDeleted, reverseProxy.EventMeta())
m.accountManager.StoreEvent(ctx, userID, serviceID, accountID, activity.ServiceDeleted, service.EventMeta())
m.proxyGRPCServer.SendReverseProxyUpdateToCluster(reverseProxy.ToProtoMapping(reverseproxy.Delete, "", m.proxyGRPCServer.GetOIDCValidationConfig()), reverseProxy.ProxyCluster)
m.proxyGRPCServer.SendServiceUpdateToCluster(service.ToProtoMapping(reverseproxy.Delete, "", m.proxyGRPCServer.GetOIDCValidationConfig()), service.ProxyCluster)
m.accountManager.UpdateAccountPeers(ctx, accountID)
@@ -376,71 +376,71 @@ func (m *managerImpl) DeleteReverseProxy(ctx context.Context, accountID, userID,
// SetCertificateIssuedAt sets the certificate issued timestamp to the current time.
// Call this when receiving a gRPC notification that the certificate was issued.
func (m *managerImpl) SetCertificateIssuedAt(ctx context.Context, accountID, reverseProxyID string) error {
func (m *managerImpl) SetCertificateIssuedAt(ctx context.Context, accountID, serviceID string) error {
return m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
proxy, err := transaction.GetReverseProxyByID(ctx, store.LockingStrengthUpdate, accountID, reverseProxyID)
service, err := transaction.GetServiceByID(ctx, store.LockingStrengthUpdate, accountID, serviceID)
if err != nil {
return fmt.Errorf("failed to get reverse proxy: %w", err)
return fmt.Errorf("failed to get service: %w", err)
}
proxy.Meta.CertificateIssuedAt = time.Now()
service.Meta.CertificateIssuedAt = time.Now()
if err = transaction.UpdateReverseProxy(ctx, proxy); err != nil {
return fmt.Errorf("failed to update reverse proxy certificate timestamp: %w", err)
if err = transaction.UpdateService(ctx, service); err != nil {
return fmt.Errorf("failed to update service certificate timestamp: %w", err)
}
return nil
})
}
// SetStatus updates the status of the reverse proxy (e.g., "active", "tunnel_not_created", etc.)
func (m *managerImpl) SetStatus(ctx context.Context, accountID, reverseProxyID string, status reverseproxy.ProxyStatus) error {
// SetStatus updates the status of the service (e.g., "active", "tunnel_not_created", etc.)
func (m *managerImpl) SetStatus(ctx context.Context, accountID, serviceID string, status reverseproxy.ProxyStatus) error {
return m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {
proxy, err := transaction.GetReverseProxyByID(ctx, store.LockingStrengthUpdate, accountID, reverseProxyID)
service, err := transaction.GetServiceByID(ctx, store.LockingStrengthUpdate, accountID, serviceID)
if err != nil {
return fmt.Errorf("failed to get reverse proxy: %w", err)
return fmt.Errorf("failed to get service: %w", err)
}
proxy.Meta.Status = string(status)
service.Meta.Status = string(status)
if err = transaction.UpdateReverseProxy(ctx, proxy); err != nil {
return fmt.Errorf("failed to update reverse proxy status: %w", err)
if err = transaction.UpdateService(ctx, service); err != nil {
return fmt.Errorf("failed to update service status: %w", err)
}
return nil
})
}
func (m *managerImpl) ReloadReverseProxy(ctx context.Context, accountID, reverseProxyID string) error {
proxy, err := m.store.GetReverseProxyByID(ctx, store.LockingStrengthNone, accountID, reverseProxyID)
func (m *managerImpl) ReloadService(ctx context.Context, accountID, serviceID string) error {
service, err := m.store.GetServiceByID(ctx, store.LockingStrengthNone, accountID, serviceID)
if err != nil {
return fmt.Errorf("failed to get reverse proxy: %w", err)
return fmt.Errorf("failed to get service: %w", err)
}
err = m.replaceHostByLookup(ctx, accountID, proxy)
err = m.replaceHostByLookup(ctx, accountID, service)
if err != nil {
return fmt.Errorf("failed to replace host by lookup for proxy %s: %w", proxy.ID, err)
return fmt.Errorf("failed to replace host by lookup for service %s: %w", service.ID, err)
}
m.proxyGRPCServer.SendReverseProxyUpdateToCluster(proxy.ToProtoMapping(reverseproxy.Update, "", m.proxyGRPCServer.GetOIDCValidationConfig()), proxy.ProxyCluster)
m.proxyGRPCServer.SendServiceUpdateToCluster(service.ToProtoMapping(reverseproxy.Update, "", m.proxyGRPCServer.GetOIDCValidationConfig()), service.ProxyCluster)
m.accountManager.UpdateAccountPeers(ctx, accountID)
return nil
}
func (m *managerImpl) ReloadAllReverseProxiesForAccount(ctx context.Context, accountID string) error {
proxies, err := m.store.GetAccountReverseProxies(ctx, store.LockingStrengthNone, accountID)
func (m *managerImpl) ReloadAllServicesForAccount(ctx context.Context, accountID string) error {
services, err := m.store.GetAccountServices(ctx, store.LockingStrengthNone, accountID)
if err != nil {
return fmt.Errorf("failed to get reverse proxies: %w", err)
return fmt.Errorf("failed to get services: %w", err)
}
for _, proxy := range proxies {
err = m.replaceHostByLookup(ctx, accountID, proxy)
for _, service := range services {
err = m.replaceHostByLookup(ctx, accountID, service)
if err != nil {
return fmt.Errorf("failed to replace host by lookup for proxy %s: %w", proxy.ID, err)
return fmt.Errorf("failed to replace host by lookup for service %s: %w", service.ID, err)
}
m.proxyGRPCServer.SendReverseProxyUpdateToCluster(proxy.ToProtoMapping(reverseproxy.Update, "", m.proxyGRPCServer.GetOIDCValidationConfig()), proxy.ProxyCluster)
m.proxyGRPCServer.SendServiceUpdateToCluster(service.ToProtoMapping(reverseproxy.Update, "", m.proxyGRPCServer.GetOIDCValidationConfig()), service.ProxyCluster)
}
m.accountManager.UpdateAccountPeers(ctx, accountID)
@@ -448,61 +448,61 @@ func (m *managerImpl) ReloadAllReverseProxiesForAccount(ctx context.Context, acc
return nil
}
func (m *managerImpl) GetGlobalReverseProxies(ctx context.Context) ([]*reverseproxy.ReverseProxy, error) {
proxies, err := m.store.GetReverseProxies(ctx, store.LockingStrengthNone)
func (m *managerImpl) GetGlobalServices(ctx context.Context) ([]*reverseproxy.Service, error) {
services, err := m.store.GetServices(ctx, store.LockingStrengthNone)
if err != nil {
return nil, fmt.Errorf("failed to get reverse proxies: %w", err)
return nil, fmt.Errorf("failed to get services: %w", err)
}
for _, proxy := range proxies {
err = m.replaceHostByLookup(ctx, proxy.AccountID, proxy)
for _, service := range services {
err = m.replaceHostByLookup(ctx, service.AccountID, service)
if err != nil {
return nil, fmt.Errorf("failed to replace host by lookup for proxy %s: %w", proxy.ID, err)
return nil, fmt.Errorf("failed to replace host by lookup for service %s: %w", service.ID, err)
}
}
return proxies, nil
return services, nil
}
func (m *managerImpl) GetProxyByID(ctx context.Context, accountID, reverseProxyID string) (*reverseproxy.ReverseProxy, error) {
proxy, err := m.store.GetReverseProxyByID(ctx, store.LockingStrengthNone, accountID, reverseProxyID)
func (m *managerImpl) GetServiceByID(ctx context.Context, accountID, serviceID string) (*reverseproxy.Service, error) {
service, err := m.store.GetServiceByID(ctx, store.LockingStrengthNone, accountID, serviceID)
if err != nil {
return nil, fmt.Errorf("failed to get reverse proxy: %w", err)
return nil, fmt.Errorf("failed to get service: %w", err)
}
err = m.replaceHostByLookup(ctx, accountID, proxy)
err = m.replaceHostByLookup(ctx, accountID, service)
if err != nil {
return nil, fmt.Errorf("failed to replace host by lookup for proxy %s: %w", proxy.ID, err)
return nil, fmt.Errorf("failed to replace host by lookup for service %s: %w", service.ID, err)
}
return proxy, nil
return service, nil
}
func (m *managerImpl) GetAccountReverseProxies(ctx context.Context, accountID string) ([]*reverseproxy.ReverseProxy, error) {
proxies, err := m.store.GetAccountReverseProxies(ctx, store.LockingStrengthNone, accountID)
func (m *managerImpl) GetAccountServices(ctx context.Context, accountID string) ([]*reverseproxy.Service, error) {
services, err := m.store.GetAccountServices(ctx, store.LockingStrengthNone, accountID)
if err != nil {
return nil, fmt.Errorf("failed to get reverse proxies: %w", err)
return nil, fmt.Errorf("failed to get services: %w", err)
}
for _, proxy := range proxies {
err = m.replaceHostByLookup(ctx, accountID, proxy)
for _, service := range services {
err = m.replaceHostByLookup(ctx, accountID, service)
if err != nil {
return nil, fmt.Errorf("failed to replace host by lookup for proxy %s: %w", proxy.ID, err)
return nil, fmt.Errorf("failed to replace host by lookup for service %s: %w", service.ID, err)
}
}
return proxies, nil
return services, nil
}
func (m *managerImpl) GetProxyIDByTargetID(ctx context.Context, accountID string, resourceID string) (string, error) {
target, err := m.store.GetReverseProxyTargetByTargetID(ctx, store.LockingStrengthNone, accountID, resourceID)
func (m *managerImpl) GetServiceIDByTargetID(ctx context.Context, accountID string, resourceID string) (string, error) {
target, err := m.store.GetServiceTargetByTargetID(ctx, store.LockingStrengthNone, accountID, resourceID)
if err != nil {
return "", fmt.Errorf("failed to get reverse proxy target by resource ID: %w", err)
return "", fmt.Errorf("failed to get service target by resource ID: %w", err)
}
if target == nil {
return "", nil
}
return target.ReverseProxyID, nil
return target.ServiceID, nil
}

View File

@@ -43,16 +43,16 @@ const (
)
type Target struct {
ID uint `gorm:"primaryKey" json:"-"`
AccountID string `gorm:"index:idx_target_account;not null" json:"-"`
ReverseProxyID string `gorm:"index:idx_reverse_proxy_targets;not null" json:"-"`
Path *string `json:"path,omitempty"`
Host string `json:"host"` // the Host field is only used for subnet targets, otherwise ignored
Port int `gorm:"index:idx_target_port" json:"port"`
Protocol string `gorm:"index:idx_target_protocol" json:"protocol"`
TargetId string `gorm:"index:idx_target_id" json:"target_id"`
TargetType string `gorm:"index:idx_target_type" json:"target_type"`
Enabled bool `gorm:"index:idx_target_enabled" json:"enabled"`
ID uint `gorm:"primaryKey" json:"-"`
AccountID string `gorm:"index:idx_target_account;not null" json:"-"`
ServiceID string `gorm:"index:idx_service_targets;not null" json:"-"`
Path *string `json:"path,omitempty"`
Host string `json:"host"` // the Host field is only used for subnet targets, otherwise ignored
Port int `gorm:"index:idx_target_port" json:"port"`
Protocol string `gorm:"index:idx_target_protocol" json:"protocol"`
TargetId string `gorm:"index:idx_target_id" json:"target_id"`
TargetType string `gorm:"index:idx_target_type" json:"target_type"`
Enabled bool `gorm:"index:idx_target_enabled" json:"enabled"`
}
type PasswordAuthConfig struct {
@@ -112,34 +112,34 @@ type OIDCValidationConfig struct {
MaxTokenAgeSeconds int64
}
type ReverseProxyMeta struct {
type ServiceMeta struct {
CreatedAt time.Time
CertificateIssuedAt time.Time
Status string
}
type ReverseProxy struct {
type Service struct {
ID string `gorm:"primaryKey"`
AccountID string `gorm:"index"`
Name string
Domain string `gorm:"index"`
ProxyCluster string `gorm:"index"`
Targets []*Target `gorm:"foreignKey:ReverseProxyID;constraint:OnDelete:CASCADE"`
Targets []*Target `gorm:"foreignKey:ServiceID;constraint:OnDelete:CASCADE"`
Enabled bool
PassHostHeader bool
RewriteRedirects bool
Auth AuthConfig `gorm:"serializer:json"`
Meta ReverseProxyMeta `gorm:"embedded;embeddedPrefix:meta_"`
SessionPrivateKey string `gorm:"column:session_private_key"`
SessionPublicKey string `gorm:"column:session_public_key"`
Auth AuthConfig `gorm:"serializer:json"`
Meta ServiceMeta `gorm:"embedded;embeddedPrefix:meta_"`
SessionPrivateKey string `gorm:"column:session_private_key"`
SessionPublicKey string `gorm:"column:session_public_key"`
}
func NewReverseProxy(accountID, name, domain, proxyCluster string, targets []*Target, enabled bool) *ReverseProxy {
func NewService(accountID, name, domain, proxyCluster string, targets []*Target, enabled bool) *Service {
for _, target := range targets {
target.AccountID = accountID
}
rp := &ReverseProxy{
s := &Service{
AccountID: accountID,
Name: name,
Domain: domain,
@@ -147,92 +147,92 @@ func NewReverseProxy(accountID, name, domain, proxyCluster string, targets []*Ta
Targets: targets,
Enabled: enabled,
}
rp.InitNewRecord()
return rp
s.InitNewRecord()
return s
}
// InitNewRecord generates a new unique ID and resets metadata for a newly created
// ReverseProxy record. This overwrites any existing ID and Meta fields and should
// Service record. This overwrites any existing ID and Meta fields and should
// only be called during initial creation, not for updates.
func (r *ReverseProxy) InitNewRecord() {
r.ID = xid.New().String()
r.Meta = ReverseProxyMeta{
func (s *Service) InitNewRecord() {
s.ID = xid.New().String()
s.Meta = ServiceMeta{
CreatedAt: time.Now(),
Status: string(StatusPending),
}
}
func (r *ReverseProxy) ToAPIResponse() *api.ReverseProxy {
r.Auth.ClearSecrets()
func (s *Service) ToAPIResponse() *api.Service {
s.Auth.ClearSecrets()
authConfig := api.ReverseProxyAuthConfig{}
authConfig := api.ServiceAuthConfig{}
if r.Auth.PasswordAuth != nil {
if s.Auth.PasswordAuth != nil {
authConfig.PasswordAuth = &api.PasswordAuthConfig{
Enabled: r.Auth.PasswordAuth.Enabled,
Password: r.Auth.PasswordAuth.Password,
Enabled: s.Auth.PasswordAuth.Enabled,
Password: s.Auth.PasswordAuth.Password,
}
}
if r.Auth.PinAuth != nil {
if s.Auth.PinAuth != nil {
authConfig.PinAuth = &api.PINAuthConfig{
Enabled: r.Auth.PinAuth.Enabled,
Pin: r.Auth.PinAuth.Pin,
Enabled: s.Auth.PinAuth.Enabled,
Pin: s.Auth.PinAuth.Pin,
}
}
if r.Auth.BearerAuth != nil {
if s.Auth.BearerAuth != nil {
authConfig.BearerAuth = &api.BearerAuthConfig{
Enabled: r.Auth.BearerAuth.Enabled,
DistributionGroups: &r.Auth.BearerAuth.DistributionGroups,
Enabled: s.Auth.BearerAuth.Enabled,
DistributionGroups: &s.Auth.BearerAuth.DistributionGroups,
}
}
// Convert internal targets to API targets
apiTargets := make([]api.ReverseProxyTarget, 0, len(r.Targets))
for _, target := range r.Targets {
apiTargets = append(apiTargets, api.ReverseProxyTarget{
apiTargets := make([]api.ServiceTarget, 0, len(s.Targets))
for _, target := range s.Targets {
apiTargets = append(apiTargets, api.ServiceTarget{
Path: target.Path,
Host: &target.Host,
Port: target.Port,
Protocol: api.ReverseProxyTargetProtocol(target.Protocol),
Protocol: api.ServiceTargetProtocol(target.Protocol),
TargetId: target.TargetId,
TargetType: api.ReverseProxyTargetTargetType(target.TargetType),
TargetType: api.ServiceTargetTargetType(target.TargetType),
Enabled: target.Enabled,
})
}
meta := api.ReverseProxyMeta{
CreatedAt: r.Meta.CreatedAt,
Status: api.ReverseProxyMetaStatus(r.Meta.Status),
meta := api.ServiceMeta{
CreatedAt: s.Meta.CreatedAt,
Status: api.ServiceMetaStatus(s.Meta.Status),
}
if !r.Meta.CertificateIssuedAt.IsZero() {
meta.CertificateIssuedAt = &r.Meta.CertificateIssuedAt
if !s.Meta.CertificateIssuedAt.IsZero() {
meta.CertificateIssuedAt = &s.Meta.CertificateIssuedAt
}
resp := &api.ReverseProxy{
Id: r.ID,
Name: r.Name,
Domain: r.Domain,
resp := &api.Service{
Id: s.ID,
Name: s.Name,
Domain: s.Domain,
Targets: apiTargets,
Enabled: r.Enabled,
PassHostHeader: &r.PassHostHeader,
RewriteRedirects: &r.RewriteRedirects,
Enabled: s.Enabled,
PassHostHeader: &s.PassHostHeader,
RewriteRedirects: &s.RewriteRedirects,
Auth: authConfig,
Meta: meta,
}
if r.ProxyCluster != "" {
resp.ProxyCluster = &r.ProxyCluster
if s.ProxyCluster != "" {
resp.ProxyCluster = &s.ProxyCluster
}
return resp
}
func (r *ReverseProxy) ToProtoMapping(operation Operation, authToken string, oidcConfig OIDCValidationConfig) *proto.ProxyMapping {
pathMappings := make([]*proto.PathMapping, 0, len(r.Targets))
for _, target := range r.Targets {
func (s *Service) ToProtoMapping(operation Operation, authToken string, oidcConfig OIDCValidationConfig) *proto.ProxyMapping {
pathMappings := make([]*proto.PathMapping, 0, len(s.Targets))
for _, target := range s.Targets {
if !target.Enabled {
continue
}
@@ -260,32 +260,32 @@ func (r *ReverseProxy) ToProtoMapping(operation Operation, authToken string, oid
}
auth := &proto.Authentication{
SessionKey: r.SessionPublicKey,
SessionKey: s.SessionPublicKey,
MaxSessionAgeSeconds: int64((time.Hour * 24).Seconds()),
}
if r.Auth.PasswordAuth != nil && r.Auth.PasswordAuth.Enabled {
if s.Auth.PasswordAuth != nil && s.Auth.PasswordAuth.Enabled {
auth.Password = true
}
if r.Auth.PinAuth != nil && r.Auth.PinAuth.Enabled {
if s.Auth.PinAuth != nil && s.Auth.PinAuth.Enabled {
auth.Pin = true
}
if r.Auth.BearerAuth != nil && r.Auth.BearerAuth.Enabled {
if s.Auth.BearerAuth != nil && s.Auth.BearerAuth.Enabled {
auth.Oidc = true
}
return &proto.ProxyMapping{
Type: operationToProtoType(operation),
Id: r.ID,
Domain: r.Domain,
Id: s.ID,
Domain: s.Domain,
Path: pathMappings,
AuthToken: authToken,
Auth: auth,
AccountId: r.AccountID,
PassHostHeader: r.PassHostHeader,
RewriteRedirects: r.RewriteRedirects,
AccountId: s.AccountID,
PassHostHeader: s.PassHostHeader,
RewriteRedirects: s.RewriteRedirects,
}
}
@@ -309,10 +309,10 @@ func isDefaultPort(scheme string, port int) bool {
return (scheme == "https" && port == 443) || (scheme == "http" && port == 80)
}
func (r *ReverseProxy) FromAPIRequest(req *api.ReverseProxyRequest, accountID string) {
r.Name = req.Name
r.Domain = req.Domain
r.AccountID = accountID
func (s *Service) FromAPIRequest(req *api.ServiceRequest, accountID string) {
s.Name = req.Name
s.Domain = req.Domain
s.AccountID = accountID
targets := make([]*Target, 0, len(req.Targets))
for _, apiTarget := range req.Targets {
@@ -330,27 +330,27 @@ func (r *ReverseProxy) FromAPIRequest(req *api.ReverseProxyRequest, accountID st
}
targets = append(targets, target)
}
r.Targets = targets
s.Targets = targets
r.Enabled = req.Enabled
s.Enabled = req.Enabled
if req.PassHostHeader != nil {
r.PassHostHeader = *req.PassHostHeader
s.PassHostHeader = *req.PassHostHeader
}
if req.RewriteRedirects != nil {
r.RewriteRedirects = *req.RewriteRedirects
s.RewriteRedirects = *req.RewriteRedirects
}
if req.Auth.PasswordAuth != nil {
r.Auth.PasswordAuth = &PasswordAuthConfig{
s.Auth.PasswordAuth = &PasswordAuthConfig{
Enabled: req.Auth.PasswordAuth.Enabled,
Password: req.Auth.PasswordAuth.Password,
}
}
if req.Auth.PinAuth != nil {
r.Auth.PinAuth = &PINAuthConfig{
s.Auth.PinAuth = &PINAuthConfig{
Enabled: req.Auth.PinAuth.Enabled,
Pin: req.Auth.PinAuth.Pin,
}
@@ -363,27 +363,27 @@ func (r *ReverseProxy) FromAPIRequest(req *api.ReverseProxyRequest, accountID st
if req.Auth.BearerAuth.DistributionGroups != nil {
bearerAuth.DistributionGroups = *req.Auth.BearerAuth.DistributionGroups
}
r.Auth.BearerAuth = bearerAuth
s.Auth.BearerAuth = bearerAuth
}
}
func (r *ReverseProxy) Validate() error {
if r.Name == "" {
return errors.New("reverse proxy name is required")
func (s *Service) Validate() error {
if s.Name == "" {
return errors.New("service name is required")
}
if len(r.Name) > 255 {
return errors.New("reverse proxy name exceeds maximum length of 255 characters")
if len(s.Name) > 255 {
return errors.New("service name exceeds maximum length of 255 characters")
}
if r.Domain == "" {
return errors.New("reverse proxy domain is required")
if s.Domain == "" {
return errors.New("service domain is required")
}
if len(r.Targets) == 0 {
if len(s.Targets) == 0 {
return errors.New("at least one target is required")
}
for i, target := range r.Targets {
for i, target := range s.Targets {
switch target.TargetType {
case TargetTypePeer, TargetTypeHost, TargetTypeDomain:
// host field will be ignored
@@ -402,42 +402,42 @@ func (r *ReverseProxy) Validate() error {
return nil
}
func (r *ReverseProxy) EventMeta() map[string]any {
return map[string]any{"name": r.Name, "domain": r.Domain, "proxy_cluster": r.ProxyCluster}
func (s *Service) EventMeta() map[string]any {
return map[string]any{"name": s.Name, "domain": s.Domain, "proxy_cluster": s.ProxyCluster}
}
func (r *ReverseProxy) Copy() *ReverseProxy {
targets := make([]*Target, len(r.Targets))
for i, target := range r.Targets {
func (s *Service) Copy() *Service {
targets := make([]*Target, len(s.Targets))
for i, target := range s.Targets {
targetCopy := *target
targets[i] = &targetCopy
}
return &ReverseProxy{
ID: r.ID,
AccountID: r.AccountID,
Name: r.Name,
Domain: r.Domain,
ProxyCluster: r.ProxyCluster,
return &Service{
ID: s.ID,
AccountID: s.AccountID,
Name: s.Name,
Domain: s.Domain,
ProxyCluster: s.ProxyCluster,
Targets: targets,
Enabled: r.Enabled,
PassHostHeader: r.PassHostHeader,
RewriteRedirects: r.RewriteRedirects,
Auth: r.Auth,
Meta: r.Meta,
SessionPrivateKey: r.SessionPrivateKey,
SessionPublicKey: r.SessionPublicKey,
Enabled: s.Enabled,
PassHostHeader: s.PassHostHeader,
RewriteRedirects: s.RewriteRedirects,
Auth: s.Auth,
Meta: s.Meta,
SessionPrivateKey: s.SessionPrivateKey,
SessionPublicKey: s.SessionPublicKey,
}
}
func (r *ReverseProxy) EncryptSensitiveData(enc *crypt.FieldEncrypt) error {
func (s *Service) EncryptSensitiveData(enc *crypt.FieldEncrypt) error {
if enc == nil {
return nil
}
if r.SessionPrivateKey != "" {
if s.SessionPrivateKey != "" {
var err error
r.SessionPrivateKey, err = enc.Encrypt(r.SessionPrivateKey)
s.SessionPrivateKey, err = enc.Encrypt(s.SessionPrivateKey)
if err != nil {
return err
}
@@ -446,14 +446,14 @@ func (r *ReverseProxy) EncryptSensitiveData(enc *crypt.FieldEncrypt) error {
return nil
}
func (r *ReverseProxy) DecryptSensitiveData(enc *crypt.FieldEncrypt) error {
func (s *Service) DecryptSensitiveData(enc *crypt.FieldEncrypt) error {
if enc == nil {
return nil
}
if r.SessionPrivateKey != "" {
if s.SessionPrivateKey != "" {
var err error
r.SessionPrivateKey, err = enc.Decrypt(r.SessionPrivateKey)
s.SessionPrivateKey, err = enc.Decrypt(s.SessionPrivateKey)
if err != nil {
return err
}

View File

@@ -13,8 +13,8 @@ import (
"github.com/netbirdio/netbird/shared/management/proto"
)
func validProxy() *ReverseProxy {
return &ReverseProxy{
func validProxy() *Service {
return &Service{
Name: "test",
Domain: "example.com",
Targets: []*Target{
@@ -170,7 +170,7 @@ func TestToProtoMapping_PortInTargetURL(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rp := &ReverseProxy{
rp := &Service{
ID: "test-id",
AccountID: "acc-1",
Domain: "example.com",
@@ -193,7 +193,7 @@ func TestToProtoMapping_PortInTargetURL(t *testing.T) {
}
func TestToProtoMapping_DisabledTargetSkipped(t *testing.T) {
rp := &ReverseProxy{
rp := &Service{
ID: "test-id",
AccountID: "acc-1",
Domain: "example.com",