mirror of
https://github.com/netbirdio/netbird.git
synced 2026-05-21 08:09:55 +00:00
feat(private-service): expose NetBird-only services over tunnel peers
Adds a new "private" service mode for the reverse proxy: services reachable exclusively over the embedded WireGuard tunnel, gated by per-peer group membership instead of operator auth schemes. Wire contract - ProxyMapping.private (field 13): the proxy MUST call ValidateTunnelPeer and fail closed; operator schemes are bypassed. - ProxyCapabilities.private (4) + supports_private_service (5): capability gate. Management never streams private mappings to proxies that don't claim the capability; the broadcast path applies the same filter via filterMappingsForProxy. - ValidateTunnelPeer RPC: resolves an inbound tunnel IP to a peer, checks the peer's groups against service.AccessGroups, and mints a session JWT on success. checkPeerGroupAccess fails closed when a private service has empty AccessGroups. - ValidateSession/ValidateTunnelPeer responses now carry peer_group_ids + peer_group_names so the proxy can authorise policy-aware middlewares without an extra management round-trip. - ProxyInboundListener + SendStatusUpdate.inbound_listener: per-account inbound listener state surfaced to dashboards. - PathTargetOptions.direct_upstream (11): bypass the embedded NetBird client and dial the target via the proxy host's network stack for upstreams reachable without WireGuard. Data model - Service.Private (bool) + Service.AccessGroups ([]string, JSON- serialised). Validate() rejects bearer auth on private services. Copy() deep-copies AccessGroups. pgx getServices loads the columns. - DomainConfig.Private threaded into the proxy auth middleware. Request handler routes private services through forwardWithTunnelPeer and returns 403 on validation failure. - Account-level SynthesizePrivateServiceZones (synthetic DNS) and injectPrivateServicePolicies (synthetic ACL) gate on len(svc.AccessGroups) > 0. Proxy - /netbird proxy --private (embedded mode) flag; Config.Private in proxy/lifecycle.go. - Per-account inbound listener (proxy/inbound.go) binding HTTP/HTTPS on the embedded NetBird client's WireGuard tunnel netstack. - proxy/internal/auth/tunnel_cache: ValidateTunnelPeer response cache with single-flight de-duplication and per-account eviction. - Local peerstore short-circuit: when the inbound IP isn't in the account roster, deny fast without an RPC. - proxy/server.go reports SupportsPrivateService=true and redacts the full ProxyMapping JSON from info logs (auth_token + header-auth hashed values now only at debug level). Identity forwarding - ValidateSessionJWT returns user_id, email, method, groups, group_names. sessionkey.Claims carries Email + Groups + GroupNames so the proxy can stamp identity onto upstream requests without an extra management round-trip on every cookie-bearing request. - CapturedData carries userEmail / userGroups / userGroupNames; the proxy stamps X-NetBird-User and X-NetBird-Groups on r.Out from the authenticated identity (strips client-supplied values first to prevent spoofing). - AccessLog.UserGroups: access-log enrichment captures the user's group memberships at write time so the dashboard can render group context without reverse-resolving stale memberships. OpenAPI/dashboard surface - ReverseProxyService gains private + access_groups; ReverseProxyCluster gains private + supports_private. ReverseProxyTarget target_type enum gains "cluster". ServiceTargetOptions gains direct_upstream. ProxyAccessLog gains user_groups.
This commit is contained in:
160
proxy/lifecycle.go
Normal file
160
proxy/lifecycle.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/proxy/internal/acme"
|
||||
)
|
||||
|
||||
// Config bundles every knob the proxy reads at construction time. It mirrors
|
||||
// the public fields on Server so library callers don't have to learn the
|
||||
// internal struct layout. Zero values mean "feature off" or "fall back to the
|
||||
// internal default" depending on the field — see the per-field doc.
|
||||
//
|
||||
// The standalone binary continues to populate Server fields directly, so
|
||||
// adding fields here must not change the zero-value behaviour of Server.
|
||||
type Config struct {
|
||||
// ListenAddr is the TCP address the main listener binds. Required.
|
||||
ListenAddr string
|
||||
// ID identifies this proxy instance to management. Empty value lets
|
||||
// New generate a timestamped default.
|
||||
ID string
|
||||
// Logger is the logrus logger used everywhere. Empty value falls back
|
||||
// to log.StandardLogger().
|
||||
Logger *log.Logger
|
||||
// Version is the build version string reported to management. Empty
|
||||
// becomes "dev".
|
||||
Version string
|
||||
// ProxyURL is the public address operators use to reach this proxy.
|
||||
ProxyURL string
|
||||
// ManagementAddress is the gRPC URL of the management server.
|
||||
ManagementAddress string
|
||||
// ProxyToken authenticates this proxy with the management server.
|
||||
ProxyToken string
|
||||
|
||||
// CertificateDirectory is the directory holding TLS certificate
|
||||
// material (static or ACME-provisioned).
|
||||
CertificateDirectory string
|
||||
// CertificateFile is the certificate filename within
|
||||
// CertificateDirectory.
|
||||
CertificateFile string
|
||||
// CertificateKeyFile is the private key filename within
|
||||
// CertificateDirectory.
|
||||
CertificateKeyFile string
|
||||
// GenerateACMECertificates toggles ACME certificate provisioning.
|
||||
GenerateACMECertificates bool
|
||||
// ACMEChallengeAddress is the listen address for HTTP-01 challenges.
|
||||
ACMEChallengeAddress string
|
||||
// ACMEDirectory is the ACME directory URL (Let's Encrypt by default).
|
||||
ACMEDirectory string
|
||||
// ACMEEABKID is the External Account Binding Key ID for CAs that
|
||||
// require EAB (e.g. ZeroSSL).
|
||||
ACMEEABKID string
|
||||
// ACMEEABHMACKey is the External Account Binding HMAC key for CAs
|
||||
// that require EAB.
|
||||
ACMEEABHMACKey string
|
||||
// ACMEChallengeType is the ACME challenge type ("tls-alpn-01" or
|
||||
// "http-01"). Empty defaults to "tls-alpn-01".
|
||||
ACMEChallengeType string
|
||||
// CertLockMethod controls how ACME certificate locks are coordinated
|
||||
// across replicas.
|
||||
CertLockMethod acme.CertLockMethod
|
||||
// WildcardCertDir is an optional directory containing static wildcard
|
||||
// certificates that override ACME for matching domains.
|
||||
WildcardCertDir string
|
||||
|
||||
// DebugEndpointEnabled toggles the debug HTTP endpoint.
|
||||
DebugEndpointEnabled bool
|
||||
// DebugEndpointAddress is the bind address for the debug endpoint.
|
||||
DebugEndpointAddress string
|
||||
// HealthAddr is the bind address for the health probe and metrics
|
||||
// surface. Empty disables the health probe entirely (library callers
|
||||
// can attach their own).
|
||||
HealthAddr string
|
||||
|
||||
// ForwardedProto overrides the X-Forwarded-Proto value sent to
|
||||
// backends. Valid values: "auto", "http", "https".
|
||||
ForwardedProto string
|
||||
// TrustedProxies is a list of IP prefixes for trusted upstream
|
||||
// proxies that may set forwarding headers.
|
||||
TrustedProxies []netip.Prefix
|
||||
// WireguardPort is the UDP port for the embedded NetBird tunnel.
|
||||
// Zero asks the OS for a random port.
|
||||
WireguardPort uint16
|
||||
// ProxyProtocol enables PROXY protocol (v1/v2) on TCP listeners.
|
||||
ProxyProtocol bool
|
||||
// PreSharedKey is the WireGuard pre-shared key used between the
|
||||
// proxy's embedded clients and peers.
|
||||
PreSharedKey string
|
||||
|
||||
// SupportsCustomPorts indicates whether the proxy can bind arbitrary
|
||||
// ports for TCP/UDP/TLS services.
|
||||
SupportsCustomPorts bool
|
||||
// RequireSubdomain forces accounts to use a subdomain in front of
|
||||
// the proxy's cluster domain.
|
||||
RequireSubdomain bool
|
||||
// Private flags this proxy as embedded in a netbird client and
|
||||
// serving exclusively over the WireGuard tunnel. Also enables
|
||||
// per-account inbound listeners on each embedded client's netstack.
|
||||
Private bool
|
||||
|
||||
// MaxDialTimeout caps the per-service backend dial timeout.
|
||||
MaxDialTimeout time.Duration
|
||||
// MaxSessionIdleTimeout caps the per-service session idle timeout.
|
||||
MaxSessionIdleTimeout time.Duration
|
||||
|
||||
// GeoDataDir is the directory containing GeoLite2 MMDB files.
|
||||
GeoDataDir string
|
||||
// CrowdSecAPIURL is the CrowdSec LAPI URL. Empty disables CrowdSec.
|
||||
CrowdSecAPIURL string
|
||||
// CrowdSecAPIKey is the CrowdSec bouncer API key. Empty disables
|
||||
// CrowdSec.
|
||||
CrowdSecAPIKey string
|
||||
}
|
||||
|
||||
// New builds a Server from cfg without performing any I/O. No goroutines
|
||||
// are spawned, no network connections are dialed, and no listeners are
|
||||
// bound — call Start to bring the proxy up. Returning a fully-formed
|
||||
// Server keeps the standalone code path (which still constructs Server
|
||||
// directly) byte-for-byte equivalent.
|
||||
func New(cfg Config) *Server {
|
||||
return &Server{
|
||||
ListenAddr: cfg.ListenAddr,
|
||||
ID: cfg.ID,
|
||||
Logger: cfg.Logger,
|
||||
Version: cfg.Version,
|
||||
ProxyURL: cfg.ProxyURL,
|
||||
ManagementAddress: cfg.ManagementAddress,
|
||||
ProxyToken: cfg.ProxyToken,
|
||||
CertificateDirectory: cfg.CertificateDirectory,
|
||||
CertificateFile: cfg.CertificateFile,
|
||||
CertificateKeyFile: cfg.CertificateKeyFile,
|
||||
GenerateACMECertificates: cfg.GenerateACMECertificates,
|
||||
ACMEChallengeAddress: cfg.ACMEChallengeAddress,
|
||||
ACMEDirectory: cfg.ACMEDirectory,
|
||||
ACMEEABKID: cfg.ACMEEABKID,
|
||||
ACMEEABHMACKey: cfg.ACMEEABHMACKey,
|
||||
ACMEChallengeType: cfg.ACMEChallengeType,
|
||||
CertLockMethod: cfg.CertLockMethod,
|
||||
WildcardCertDir: cfg.WildcardCertDir,
|
||||
DebugEndpointEnabled: cfg.DebugEndpointEnabled,
|
||||
DebugEndpointAddress: cfg.DebugEndpointAddress,
|
||||
HealthAddress: cfg.HealthAddr,
|
||||
ForwardedProto: cfg.ForwardedProto,
|
||||
TrustedProxies: cfg.TrustedProxies,
|
||||
WireguardPort: cfg.WireguardPort,
|
||||
ProxyProtocol: cfg.ProxyProtocol,
|
||||
PreSharedKey: cfg.PreSharedKey,
|
||||
SupportsCustomPorts: cfg.SupportsCustomPorts,
|
||||
RequireSubdomain: cfg.RequireSubdomain,
|
||||
Private: cfg.Private,
|
||||
MaxDialTimeout: cfg.MaxDialTimeout,
|
||||
MaxSessionIdleTimeout: cfg.MaxSessionIdleTimeout,
|
||||
GeoDataDir: cfg.GeoDataDir,
|
||||
CrowdSecAPIURL: cfg.CrowdSecAPIURL,
|
||||
CrowdSecAPIKey: cfg.CrowdSecAPIKey,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user