mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-19 08:46:38 +00:00
cleanup
This commit is contained in:
@@ -39,7 +39,7 @@ func NewLetsEncrypt(config LetsEncryptConfig) *LetsEncryptManager {
|
||||
HostPolicy: m.hostPolicy,
|
||||
Cache: autocert.DirCache(config.CertCacheDir),
|
||||
Email: config.Email,
|
||||
RenewBefore: 0, // Use default
|
||||
RenewBefore: 0, // Use default 30 days prior to expiration
|
||||
}
|
||||
|
||||
log.Info("Let's Encrypt certificate manager initialized")
|
||||
@@ -71,8 +71,6 @@ func (m *LetsEncryptManager) RemoveDomain(domain string) {
|
||||
func (m *LetsEncryptManager) IssueCertificate(ctx context.Context, domain string) error {
|
||||
log.Infof("Issuing Let's Encrypt certificate for domain: %s", domain)
|
||||
|
||||
// Use GetCertificate to trigger certificate issuance
|
||||
// This will go through the ACME challenge flow
|
||||
hello := &tls.ClientHelloInfo{
|
||||
ServerName: domain,
|
||||
}
|
||||
|
||||
@@ -54,19 +54,16 @@ func (m *SelfSignedManager) IssueCertificate(ctx context.Context, domain string)
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// Check if we already have a certificate for this domain
|
||||
if _, exists := m.certificates[domain]; exists {
|
||||
log.Debugf("Self-signed certificate already exists for domain: %s", domain)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate self-signed certificate
|
||||
cert, err := m.generateCertificate(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Cache the certificate
|
||||
m.certificates[domain] = cert
|
||||
|
||||
return nil
|
||||
@@ -94,7 +91,6 @@ func (m *SelfSignedManager) getCertificate(hello *tls.ClientHelloInfo) (*tls.Cer
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// Generate certificate on-demand if not cached
|
||||
log.Infof("Generating self-signed certificate on-demand for: %s", hello.ServerName)
|
||||
|
||||
newCert, err := m.generateCertificate(hello.ServerName)
|
||||
@@ -102,7 +98,6 @@ func (m *SelfSignedManager) getCertificate(hello *tls.ClientHelloInfo) (*tls.Cer
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Cache it
|
||||
m.mu.Lock()
|
||||
m.certificates[hello.ServerName] = newCert
|
||||
m.mu.Unlock()
|
||||
@@ -112,13 +107,11 @@ func (m *SelfSignedManager) getCertificate(hello *tls.ClientHelloInfo) (*tls.Cer
|
||||
|
||||
// generateCertificate generates a self-signed certificate for a domain
|
||||
func (m *SelfSignedManager) generateCertificate(domain string) (*tls.Certificate, error) {
|
||||
// Generate private key
|
||||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate private key: %w", err)
|
||||
}
|
||||
|
||||
// Create certificate template
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(365 * 24 * time.Hour) // Valid for 1 year
|
||||
|
||||
@@ -141,13 +134,11 @@ func (m *SelfSignedManager) generateCertificate(domain string) (*tls.Certificate
|
||||
DNSNames: []string{domain},
|
||||
}
|
||||
|
||||
// Create self-signed certificate
|
||||
certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create certificate: %w", err)
|
||||
}
|
||||
|
||||
// Parse certificate
|
||||
cert, err := x509.ParseCertificate(certDER)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate: %w", err)
|
||||
|
||||
@@ -53,18 +53,13 @@ func (p *Proxy) handleProxyRequest(w http.ResponseWriter, r *http.Request) {
|
||||
host = host[:idx]
|
||||
}
|
||||
|
||||
// Get auth info from headers set by auth middleware
|
||||
// TODO: extract logging data
|
||||
authMechanism := r.Header.Get("X-Auth-Method")
|
||||
if authMechanism == "" {
|
||||
authMechanism = "none"
|
||||
}
|
||||
|
||||
userID := r.Header.Get("X-Auth-User-ID")
|
||||
|
||||
// Determine auth success based on status code
|
||||
authSuccess := rw.statusCode != http.StatusUnauthorized && rw.statusCode != http.StatusForbidden
|
||||
|
||||
// Extract source IP directly
|
||||
sourceIP := extractSourceIP(r)
|
||||
|
||||
data := RequestData{
|
||||
@@ -89,29 +84,22 @@ func (p *Proxy) findRoute(host, path string) *routeEntry {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
// Strip port from host
|
||||
if idx := strings.LastIndex(host, ":"); idx != -1 {
|
||||
host = host[:idx]
|
||||
}
|
||||
|
||||
// O(1) lookup by host
|
||||
routeConfig, exists := p.routes[host]
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build list of route entries sorted by path specificity
|
||||
var entries []*routeEntry
|
||||
|
||||
// Create entries for each path mapping
|
||||
for routePath, target := range routeConfig.PathMappings {
|
||||
proxy := p.createProxy(routeConfig, target)
|
||||
|
||||
// ALWAYS wrap proxy with auth middleware (even if no auth configured)
|
||||
// This ensures consistent auth handling and logging
|
||||
handler := auth.Wrap(proxy, routeConfig.AuthConfig, routeConfig.ID, routeConfig.AuthRejectResponse, p.oidcHandler)
|
||||
|
||||
// Log auth configuration
|
||||
if routeConfig.AuthConfig != nil && !routeConfig.AuthConfig.IsEmpty() {
|
||||
var authType string
|
||||
if routeConfig.AuthConfig.BasicAuth != nil {
|
||||
@@ -169,11 +157,9 @@ func (p *Proxy) findRoute(host, path string) *routeEntry {
|
||||
|
||||
// createProxy creates a reverse proxy for a target with the route's connection
|
||||
func (p *Proxy) createProxy(routeConfig *RouteConfig, target string) *httputil.ReverseProxy {
|
||||
// Parse target URL
|
||||
targetURL, err := url.Parse("http://" + target)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to parse target URL %s: %v", target, err)
|
||||
// Return a proxy that returns 502
|
||||
return &httputil.ReverseProxy{
|
||||
Director: func(req *http.Request) {},
|
||||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
@@ -182,21 +168,18 @@ func (p *Proxy) createProxy(routeConfig *RouteConfig, target string) *httputil.R
|
||||
}
|
||||
}
|
||||
|
||||
// Create reverse proxy
|
||||
proxy := httputil.NewSingleHostReverseProxy(targetURL)
|
||||
|
||||
// Configure transport to use the provided connection (WireGuard, etc.)
|
||||
proxy.Transport = &http.Transport{
|
||||
DialContext: routeConfig.nbClient.DialContext,
|
||||
MaxIdleConns: 1,
|
||||
MaxIdleConnsPerHost: 1,
|
||||
IdleConnTimeout: 0, // Keep alive indefinitely
|
||||
IdleConnTimeout: 0,
|
||||
DisableKeepAlives: false,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
|
||||
// Custom error handler
|
||||
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
log.Errorf("Proxy error for %s%s: %v", r.Host, r.URL.Path, err)
|
||||
http.Error(w, "Bad Gateway", http.StatusBadGateway)
|
||||
@@ -207,14 +190,12 @@ func (p *Proxy) createProxy(routeConfig *RouteConfig, target string) *httputil.R
|
||||
|
||||
// handleOIDCCallback handles the global /auth/callback endpoint for all routes
|
||||
func (p *Proxy) handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
|
||||
// Check if OIDC handler is available
|
||||
if p.oidcHandler == nil {
|
||||
log.Error("OIDC callback received but no OIDC handler configured")
|
||||
http.Error(w, "Authentication not configured", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Use the OIDC handler's callback method
|
||||
handler := p.oidcHandler.HandleCallback()
|
||||
handler(w, r)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ type Proxy struct {
|
||||
|
||||
// New creates a new reverse proxy
|
||||
func New(config Config) (*Proxy, error) {
|
||||
// Set defaults
|
||||
if config.ListenAddress == "" {
|
||||
config.ListenAddress = ":443"
|
||||
}
|
||||
@@ -38,22 +37,18 @@ func New(config Config) (*Proxy, error) {
|
||||
config.CertCacheDir = "./certs"
|
||||
}
|
||||
|
||||
// Set default cert mode
|
||||
if config.CertMode == "" {
|
||||
config.CertMode = "letsencrypt"
|
||||
}
|
||||
|
||||
// Validate config based on cert mode
|
||||
if config.CertMode == "letsencrypt" && config.TLSEmail == "" {
|
||||
return nil, fmt.Errorf("TLSEmail is required for letsencrypt mode")
|
||||
}
|
||||
|
||||
// Set default OIDC session cookie name if not provided
|
||||
if config.OIDCConfig != nil && config.OIDCConfig.SessionCookieName == "" {
|
||||
config.OIDCConfig.SessionCookieName = "auth_session"
|
||||
}
|
||||
|
||||
// Initialize certificate manager based on mode
|
||||
var certMgr certmanager.Manager
|
||||
if config.CertMode == "selfsigned" {
|
||||
// HTTPS with self-signed certificates (for local testing)
|
||||
@@ -73,8 +68,6 @@ func New(config Config) (*Proxy, error) {
|
||||
isRunning: false,
|
||||
}
|
||||
|
||||
// Initialize OIDC handler if OIDC is configured
|
||||
// The handler internally creates and manages its own state store
|
||||
if config.OIDCConfig != nil {
|
||||
stateStore := oidc.NewStateStore()
|
||||
p.oidcHandler = oidc.NewHandler(config.OIDCConfig, stateStore)
|
||||
@@ -93,28 +86,25 @@ func (p *Proxy) Start() error {
|
||||
p.isRunning = true
|
||||
p.mu.Unlock()
|
||||
|
||||
// Build the main HTTP handler
|
||||
handler := p.buildHandler()
|
||||
|
||||
return p.startHTTPS(handler)
|
||||
}
|
||||
|
||||
// startHTTPS starts the proxy with HTTPS (non-blocking)
|
||||
// startHTTPS starts the proxy with HTTPS
|
||||
func (p *Proxy) startHTTPS(handler http.Handler) error {
|
||||
// Start HTTP server for ACME challenges (Let's Encrypt HTTP-01)
|
||||
p.httpServer = &http.Server{
|
||||
Addr: p.config.HTTPListenAddress,
|
||||
Handler: p.certManager.HTTPHandler(nil),
|
||||
}
|
||||
|
||||
go func() {
|
||||
log.Infof("Starting HTTP server for ACME challenges on %s", p.config.HTTPListenAddress)
|
||||
log.Infof("Starting HTTP server on %s", p.config.HTTPListenAddress)
|
||||
if err := p.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Errorf("HTTP server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Start HTTPS server in background
|
||||
p.server = &http.Server{
|
||||
Addr: p.config.ListenAddress,
|
||||
Handler: handler,
|
||||
@@ -143,14 +133,12 @@ func (p *Proxy) Stop(ctx context.Context) error {
|
||||
|
||||
log.Info("Stopping reverse proxy server...")
|
||||
|
||||
// Stop HTTP server (for ACME challenges)
|
||||
if p.httpServer != nil {
|
||||
if err := p.httpServer.Shutdown(ctx); err != nil {
|
||||
log.Errorf("Error shutting down HTTP server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Stop main server
|
||||
if p.server != nil {
|
||||
if err := p.server.Shutdown(ctx); err != nil {
|
||||
return fmt.Errorf("error shutting down server: %w", err)
|
||||
|
||||
@@ -36,7 +36,6 @@ func (p *Proxy) AddRoute(route *RouteConfig) error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Check if route already exists for this domain
|
||||
if _, exists := p.routes[route.Domain]; exists {
|
||||
return fmt.Errorf("route for domain %s already exists", route.Domain)
|
||||
}
|
||||
@@ -54,10 +53,8 @@ func (p *Proxy) AddRoute(route *RouteConfig) error {
|
||||
|
||||
route.nbClient = client
|
||||
|
||||
// Add route with domain as key
|
||||
p.routes[route.Domain] = route
|
||||
|
||||
// Register domain with certificate manager
|
||||
p.certManager.AddDomain(route.Domain)
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
@@ -66,13 +63,13 @@ func (p *Proxy) AddRoute(route *RouteConfig) error {
|
||||
"paths": len(route.PathMappings),
|
||||
}).Info("Added route")
|
||||
|
||||
// Eagerly issue certificate in background
|
||||
go func(domain string) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
if err := p.certManager.IssueCertificate(ctx, domain); err != nil {
|
||||
log.Errorf("Failed to issue certificate: %v", err)
|
||||
// TODO: Better error feedback mechanism
|
||||
}
|
||||
}(route.Domain)
|
||||
|
||||
@@ -84,15 +81,12 @@ func (p *Proxy) RemoveRoute(domain string) error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Check if route exists
|
||||
if _, exists := p.routes[domain]; !exists {
|
||||
return fmt.Errorf("route for domain %s not found", domain)
|
||||
}
|
||||
|
||||
// Remove route
|
||||
delete(p.routes, domain)
|
||||
|
||||
// Unregister domain from certificate manager
|
||||
p.certManager.RemoveDomain(domain)
|
||||
|
||||
log.Infof("Removed route for domain: %s", domain)
|
||||
@@ -114,12 +108,10 @@ func (p *Proxy) UpdateRoute(route *RouteConfig) error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Check if route exists for this domain
|
||||
if _, exists := p.routes[route.Domain]; !exists {
|
||||
return fmt.Errorf("route for domain %s not found", route.Domain)
|
||||
}
|
||||
|
||||
// Update route using domain as key
|
||||
p.routes[route.Domain] = route
|
||||
|
||||
log.WithFields(log.Fields{
|
||||
|
||||
Reference in New Issue
Block a user