mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-29 15:59:10 +01:00
fix cluster logic for domains and reverse proxy
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
||||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||||
"github.com/netbirdio/netbird/shared/management/http/util"
|
"github.com/netbirdio/netbird/shared/management/http/util"
|
||||||
@@ -40,12 +39,16 @@ func domainTypeToApi(t domainType) api.ReverseProxyDomainType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func domainToApi(d *Domain) api.ReverseProxyDomain {
|
func domainToApi(d *Domain) api.ReverseProxyDomain {
|
||||||
return api.ReverseProxyDomain{
|
resp := api.ReverseProxyDomain{
|
||||||
Domain: d.Domain,
|
Domain: d.Domain,
|
||||||
Id: d.ID,
|
Id: d.ID,
|
||||||
Type: domainTypeToApi(d.Type),
|
Type: domainTypeToApi(d.Type),
|
||||||
Validated: d.Validated,
|
Validated: d.Validated,
|
||||||
}
|
}
|
||||||
|
if d.TargetCluster != "" {
|
||||||
|
resp.TargetCluster = &d.TargetCluster
|
||||||
|
}
|
||||||
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *handler) getAllDomains(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) getAllDomains(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -82,7 +85,7 @@ func (h *handler) createCustomDomain(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
domain, err := h.manager.CreateDomain(r.Context(), userAuth.AccountId, req.Domain)
|
domain, err := h.manager.CreateDomain(r.Context(), userAuth.AccountId, req.Domain, req.TargetCluster)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
util.WriteError(r.Context(), err, w)
|
util.WriteError(r.Context(), err, w)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -19,11 +19,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Domain struct {
|
type Domain struct {
|
||||||
ID string `gorm:"unique;primaryKey;autoIncrement"`
|
ID string `gorm:"unique;primaryKey;autoIncrement"`
|
||||||
Domain string `gorm:"unique"` // Domain records must be unique, this avoids domain reuse across accounts.
|
Domain string `gorm:"unique"` // Domain records must be unique, this avoids domain reuse across accounts.
|
||||||
AccountID string `gorm:"index"`
|
AccountID string `gorm:"index"`
|
||||||
Type domainType `gorm:"-"`
|
TargetCluster string // The proxy cluster this domain should be validated against
|
||||||
Validated bool
|
Type domainType `gorm:"-"`
|
||||||
|
Validated bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type store interface {
|
type store interface {
|
||||||
@@ -32,7 +33,7 @@ type store interface {
|
|||||||
GetCustomDomain(ctx context.Context, accountID string, domainID string) (*Domain, error)
|
GetCustomDomain(ctx context.Context, accountID string, domainID string) (*Domain, error)
|
||||||
ListFreeDomains(ctx context.Context, accountID string) ([]string, error)
|
ListFreeDomains(ctx context.Context, accountID string) ([]string, error)
|
||||||
ListCustomDomains(ctx context.Context, accountID string) ([]*Domain, error)
|
ListCustomDomains(ctx context.Context, accountID string) ([]*Domain, error)
|
||||||
CreateCustomDomain(ctx context.Context, accountID string, domainName string, validated bool) (*Domain, error)
|
CreateCustomDomain(ctx context.Context, accountID string, domainName string, targetCluster string, validated bool) (*Domain, error)
|
||||||
UpdateCustomDomain(ctx context.Context, accountID string, d *Domain) (*Domain, error)
|
UpdateCustomDomain(ctx context.Context, accountID string, d *Domain) (*Domain, error)
|
||||||
DeleteCustomDomain(ctx context.Context, accountID string, domainID string) error
|
DeleteCustomDomain(ctx context.Context, accountID string, domainID string) error
|
||||||
}
|
}
|
||||||
@@ -85,31 +86,43 @@ func (m Manager) GetDomains(ctx context.Context, accountID string) ([]*Domain, e
|
|||||||
// Add custom domains.
|
// Add custom domains.
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
ret = append(ret, &Domain{
|
ret = append(ret, &Domain{
|
||||||
ID: domain.ID,
|
ID: domain.ID,
|
||||||
Domain: domain.Domain,
|
Domain: domain.Domain,
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
Type: TypeCustom,
|
TargetCluster: domain.TargetCluster,
|
||||||
Validated: domain.Validated,
|
Type: TypeCustom,
|
||||||
|
Validated: domain.Validated,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) CreateDomain(ctx context.Context, accountID, domainName string) (*Domain, error) {
|
func (m Manager) CreateDomain(ctx context.Context, accountID, domainName, targetCluster string) (*Domain, error) {
|
||||||
// Attempt an initial validation; however, a failure is still acceptable for creation
|
|
||||||
// because the user may not yet have configured their DNS records, or the DNS update
|
// Verify the target cluster is in the available clusters
|
||||||
// has not yet reached the servers that are queried by the validation resolver.
|
allowList := m.proxyURLAllowList()
|
||||||
|
clusterValid := false
|
||||||
|
for _, cluster := range allowList {
|
||||||
|
if cluster == targetCluster {
|
||||||
|
clusterValid = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !clusterValid {
|
||||||
|
return nil, fmt.Errorf("target cluster %s is not available", targetCluster)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt an initial validation against the specified cluster only
|
||||||
var validated bool
|
var validated bool
|
||||||
if m.validator.IsValid(ctx, domainName, m.proxyURLAllowList()) {
|
if m.validator.IsValid(ctx, domainName, []string{targetCluster}) {
|
||||||
validated = true
|
validated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := m.store.CreateCustomDomain(ctx, accountID, domainName, validated)
|
d, err := m.store.CreateCustomDomain(ctx, accountID, domainName, targetCluster, validated)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d, fmt.Errorf("create domain in store: %w", err)
|
return d, fmt.Errorf("create domain in store: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,15 +149,25 @@ func (m Manager) ValidateDomain(accountID, domainID string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
allowList := m.proxyURLAllowList()
|
// Validate only against the domain's target cluster
|
||||||
log.WithFields(log.Fields{
|
targetCluster := d.TargetCluster
|
||||||
"accountID": accountID,
|
if targetCluster == "" {
|
||||||
"domainID": domainID,
|
log.WithFields(log.Fields{
|
||||||
"domain": d.Domain,
|
"accountID": accountID,
|
||||||
"proxyAllowList": allowList,
|
"domainID": domainID,
|
||||||
}).Info("validating domain against proxy allow list")
|
"domain": d.Domain,
|
||||||
|
}).Warn("domain has no target cluster set, skipping validation")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if m.validator.IsValid(context.Background(), d.Domain, allowList) {
|
log.WithFields(log.Fields{
|
||||||
|
"accountID": accountID,
|
||||||
|
"domainID": domainID,
|
||||||
|
"domain": d.Domain,
|
||||||
|
"targetCluster": targetCluster,
|
||||||
|
}).Info("validating domain against target cluster")
|
||||||
|
|
||||||
|
if m.validator.IsValid(context.Background(), d.Domain, []string{targetCluster}) {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"accountID": accountID,
|
"accountID": accountID,
|
||||||
"domainID": domainID,
|
"domainID": domainID,
|
||||||
@@ -161,11 +184,11 @@ func (m Manager) ValidateDomain(accountID, domainID string) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
"accountID": accountID,
|
"accountID": accountID,
|
||||||
"domainID": domainID,
|
"domainID": domainID,
|
||||||
"domain": d.Domain,
|
"domain": d.Domain,
|
||||||
"proxyAllowList": allowList,
|
"targetCluster": targetCluster,
|
||||||
}).Warn("domain validation failed - CNAME does not match any connected proxy")
|
}).Warn("domain validation failed - CNAME does not match target cluster")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4845,13 +4845,14 @@ func (s *SqlStore) ListCustomDomains(ctx context.Context, accountID string) ([]*
|
|||||||
return domains, nil
|
return domains, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqlStore) CreateCustomDomain(ctx context.Context, accountID string, domainName string, validated bool) (*domain.Domain, error) {
|
func (s *SqlStore) CreateCustomDomain(ctx context.Context, accountID string, domainName string, targetCluster string, validated bool) (*domain.Domain, error) {
|
||||||
newDomain := &domain.Domain{
|
newDomain := &domain.Domain{
|
||||||
ID: xid.New().String(), // Generate our own ID because gorm doesn't always configure the database to handle this for us.
|
ID: xid.New().String(), // Generate our own ID because gorm doesn't always configure the database to handle this for us.
|
||||||
Domain: domainName,
|
Domain: domainName,
|
||||||
AccountID: accountID,
|
AccountID: accountID,
|
||||||
Type: domain.TypeCustom,
|
TargetCluster: targetCluster,
|
||||||
Validated: validated,
|
Type: domain.TypeCustom,
|
||||||
|
Validated: validated,
|
||||||
}
|
}
|
||||||
result := s.db.Create(newDomain)
|
result := s.db.Create(newDomain)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ type Store interface {
|
|||||||
GetCustomDomain(ctx context.Context, accountID string, domainID string) (*domain.Domain, error)
|
GetCustomDomain(ctx context.Context, accountID string, domainID string) (*domain.Domain, error)
|
||||||
ListFreeDomains(ctx context.Context, accountID string) ([]string, error)
|
ListFreeDomains(ctx context.Context, accountID string) ([]string, error)
|
||||||
ListCustomDomains(ctx context.Context, accountID string) ([]*domain.Domain, error)
|
ListCustomDomains(ctx context.Context, accountID string) ([]*domain.Domain, error)
|
||||||
CreateCustomDomain(ctx context.Context, accountID string, domainName string, validated bool) (*domain.Domain, error)
|
CreateCustomDomain(ctx context.Context, accountID string, domainName string, targetCluster string, validated bool) (*domain.Domain, error)
|
||||||
UpdateCustomDomain(ctx context.Context, accountID string, d *domain.Domain) (*domain.Domain, error)
|
UpdateCustomDomain(ctx context.Context, accountID string, d *domain.Domain) (*domain.Domain, error)
|
||||||
DeleteCustomDomain(ctx context.Context, accountID string, domainID string) error
|
DeleteCustomDomain(ctx context.Context, accountID string, domainID string) error
|
||||||
|
|
||||||
|
|||||||
@@ -3047,6 +3047,9 @@ components:
|
|||||||
description: Whether the domain has been validated
|
description: Whether the domain has been validated
|
||||||
type:
|
type:
|
||||||
$ref: '#/components/schemas/ReverseProxyDomainType'
|
$ref: '#/components/schemas/ReverseProxyDomainType'
|
||||||
|
target_cluster:
|
||||||
|
type: string
|
||||||
|
description: The proxy cluster this domain is validated against (only for custom domains)
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- domain
|
- domain
|
||||||
@@ -3058,8 +3061,12 @@ components:
|
|||||||
domain:
|
domain:
|
||||||
type: string
|
type: string
|
||||||
description: Domain name
|
description: Domain name
|
||||||
|
target_cluster:
|
||||||
|
type: string
|
||||||
|
description: The proxy cluster this domain should be validated against
|
||||||
required:
|
required:
|
||||||
- domain
|
- domain
|
||||||
|
- target_cluster
|
||||||
InstanceStatus:
|
InstanceStatus:
|
||||||
type: object
|
type: object
|
||||||
description: Instance status information
|
description: Instance status information
|
||||||
|
|||||||
@@ -2006,6 +2006,9 @@ type ReverseProxyDomain struct {
|
|||||||
// Id Domain ID
|
// Id Domain ID
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
|
|
||||||
|
// TargetCluster The proxy cluster this domain is validated against (only for custom domains)
|
||||||
|
TargetCluster *string `json:"target_cluster,omitempty"`
|
||||||
|
|
||||||
// Type Type of Reverse Proxy Domain
|
// Type Type of Reverse Proxy Domain
|
||||||
Type ReverseProxyDomainType `json:"type"`
|
Type ReverseProxyDomainType `json:"type"`
|
||||||
|
|
||||||
@@ -2017,6 +2020,9 @@ type ReverseProxyDomain struct {
|
|||||||
type ReverseProxyDomainRequest struct {
|
type ReverseProxyDomainRequest struct {
|
||||||
// Domain Domain name
|
// Domain Domain name
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
|
|
||||||
|
// TargetCluster The proxy cluster this domain should be validated against
|
||||||
|
TargetCluster string `json:"target_cluster"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReverseProxyDomainType Type of Reverse Proxy Domain
|
// ReverseProxyDomainType Type of Reverse Proxy Domain
|
||||||
|
|||||||
Reference in New Issue
Block a user