mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-23 02:36:42 +00:00
Merge prototype/reverse-proxy with proxy clustering support
Combines token-based authentication from upstream with proxy clustering: - Session keys for JWT signing (SessionPrivateKey/SessionPublicKey) - One-time token store for proxy authentication - Cluster-targeted updates via SendReverseProxyUpdateToCluster - ProxyCluster field derived from domain - OIDC validation config support Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/netbirdio/netbird/util/crypt"
|
||||
"github.com/rs/xid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
@@ -31,6 +32,9 @@ const (
|
||||
StatusCertificatePending ProxyStatus = "certificate_pending"
|
||||
StatusCertificateFailed ProxyStatus = "certificate_failed"
|
||||
StatusError ProxyStatus = "error"
|
||||
|
||||
TargetTypePeer = "peer"
|
||||
TargetTypeResource = "resource"
|
||||
)
|
||||
|
||||
type Target struct {
|
||||
@@ -58,15 +62,17 @@ type BearerAuthConfig struct {
|
||||
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 OIDCValidationConfig struct {
|
||||
Issuer string
|
||||
Audiences []string
|
||||
KeysLocation string
|
||||
MaxTokenAgeSeconds int64
|
||||
}
|
||||
|
||||
type ReverseProxyMeta struct {
|
||||
@@ -76,15 +82,17 @@ type ReverseProxyMeta struct {
|
||||
}
|
||||
|
||||
type ReverseProxy struct {
|
||||
ID string `gorm:"primaryKey"`
|
||||
AccountID string `gorm:"index"`
|
||||
Name string
|
||||
Domain string `gorm:"index"`
|
||||
ProxyCluster string `gorm:"index"`
|
||||
Targets []Target `gorm:"serializer:json"`
|
||||
Enabled bool
|
||||
Auth AuthConfig `gorm:"serializer:json"`
|
||||
Meta ReverseProxyMeta `gorm:"embedded;embeddedPrefix:meta_"`
|
||||
ID string `gorm:"primaryKey"`
|
||||
AccountID string `gorm:"index"`
|
||||
Name string
|
||||
Domain string `gorm:"index"`
|
||||
ProxyCluster string `gorm:"index"`
|
||||
Targets []Target `gorm:"serializer:json"`
|
||||
Enabled 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"`
|
||||
}
|
||||
|
||||
func NewReverseProxy(accountID, name, domain, proxyCluster string, targets []Target, enabled bool) *ReverseProxy {
|
||||
@@ -127,12 +135,6 @@ func (r *ReverseProxy) ToAPIResponse() *api.ReverseProxy {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -173,7 +175,7 @@ func (r *ReverseProxy) ToAPIResponse() *api.ReverseProxy {
|
||||
return resp
|
||||
}
|
||||
|
||||
func (r *ReverseProxy) ToProtoMapping(operation Operation, setupKey string) *proto.ProxyMapping {
|
||||
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 {
|
||||
if !target.Enabled {
|
||||
@@ -200,7 +202,10 @@ func (r *ReverseProxy) ToProtoMapping(operation Operation, setupKey string) *pro
|
||||
})
|
||||
}
|
||||
|
||||
auth := &proto.Authentication{}
|
||||
auth := &proto.Authentication{
|
||||
SessionKey: r.SessionPublicKey,
|
||||
MaxSessionAgeSeconds: int64((time.Hour * 24).Seconds()),
|
||||
}
|
||||
|
||||
if r.Auth.PasswordAuth != nil && r.Auth.PasswordAuth.Enabled {
|
||||
auth.Password = true
|
||||
@@ -211,13 +216,7 @@ func (r *ReverseProxy) ToProtoMapping(operation Operation, setupKey string) *pro
|
||||
}
|
||||
|
||||
if r.Auth.BearerAuth != nil && r.Auth.BearerAuth.Enabled {
|
||||
auth.Oidc = &proto.OIDC{
|
||||
DistributionGroups: r.Auth.BearerAuth.DistributionGroups,
|
||||
}
|
||||
}
|
||||
|
||||
if r.Auth.LinkAuth != nil && r.Auth.LinkAuth.Enabled {
|
||||
auth.Link = true
|
||||
auth.Oidc = true
|
||||
}
|
||||
|
||||
return &proto.ProxyMapping{
|
||||
@@ -225,7 +224,7 @@ func (r *ReverseProxy) ToProtoMapping(operation Operation, setupKey string) *pro
|
||||
Id: r.ID,
|
||||
Domain: r.Domain,
|
||||
Path: pathMappings,
|
||||
SetupKey: setupKey,
|
||||
AuthToken: authToken,
|
||||
Auth: auth,
|
||||
AccountId: r.AccountID,
|
||||
}
|
||||
@@ -289,13 +288,6 @@ func (r *ReverseProxy) FromAPIRequest(req *api.ReverseProxyRequest, accountID st
|
||||
}
|
||||
r.Auth.BearerAuth = bearerAuth
|
||||
}
|
||||
|
||||
if req.Auth.LinkAuth != nil {
|
||||
r.Auth.LinkAuth = &LinkAuthConfig{
|
||||
Enabled: req.Auth.LinkAuth.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (r *ReverseProxy) Validate() error {
|
||||
@@ -320,3 +312,54 @@ func (r *ReverseProxy) Validate() error {
|
||||
func (r *ReverseProxy) EventMeta() map[string]any {
|
||||
return map[string]any{"name": r.Name, "domain": r.Domain, "proxy_cluster": r.ProxyCluster}
|
||||
}
|
||||
|
||||
func (r *ReverseProxy) Copy() *ReverseProxy {
|
||||
targets := make([]Target, len(r.Targets))
|
||||
copy(targets, r.Targets)
|
||||
|
||||
return &ReverseProxy{
|
||||
ID: r.ID,
|
||||
AccountID: r.AccountID,
|
||||
Name: r.Name,
|
||||
Domain: r.Domain,
|
||||
ProxyCluster: r.ProxyCluster,
|
||||
Targets: targets,
|
||||
Enabled: r.Enabled,
|
||||
Auth: r.Auth,
|
||||
Meta: r.Meta,
|
||||
SessionPrivateKey: r.SessionPrivateKey,
|
||||
SessionPublicKey: r.SessionPublicKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ReverseProxy) EncryptSensitiveData(enc *crypt.FieldEncrypt) error {
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.SessionPrivateKey != "" {
|
||||
var err error
|
||||
r.SessionPrivateKey, err = enc.Encrypt(r.SessionPrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ReverseProxy) DecryptSensitiveData(enc *crypt.FieldEncrypt) error {
|
||||
if enc == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.SessionPrivateKey != "" {
|
||||
var err error
|
||||
r.SessionPrivateKey, err = enc.Decrypt(r.SessionPrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user