mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-26 16:49:08 +02:00
[management,signal,proxy,relay,misc] Limit shared config loader to combined server
This commit is contained in:
@@ -1,84 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/acme"
|
||||
|
||||
"github.com/netbirdio/netbird/proxy"
|
||||
"github.com/netbirdio/netbird/trustedproxy"
|
||||
configloader "github.com/netbirdio/netbird/util/config"
|
||||
)
|
||||
|
||||
type commandConfig struct {
|
||||
proxy.Config `yaml:",inline"`
|
||||
|
||||
LogLevel string `yaml:"logLevel" env:"NB_PROXY_LOG_LEVEL" flag:"log-level"`
|
||||
PreallocatedBuffers *uint32 `yaml:"preallocatedBuffers" env:"-"`
|
||||
MaxBatchSize *uint32 `yaml:"maxBatchSize" env:"-"`
|
||||
}
|
||||
|
||||
func defaultConfig() *commandConfig {
|
||||
return &commandConfig{
|
||||
Config: proxy.Config{
|
||||
ListenAddr: ":443",
|
||||
ManagementAddress: DefaultManagementURL,
|
||||
CertificateDirectory: "./certs",
|
||||
CertificateFile: "tls.crt",
|
||||
CertificateKeyFile: "tls.key",
|
||||
ACMEChallengeAddress: ":80",
|
||||
ACMEDirectory: acme.LetsEncryptURL,
|
||||
ACMEChallengeType: "tls-alpn-01",
|
||||
CertLockMethod: "auto",
|
||||
DebugEndpointAddress: "localhost:8444",
|
||||
HealthAddr: "localhost:8080",
|
||||
TrustedProxies: trustedproxy.FromPrefixes(nil),
|
||||
ForwardedProto: "auto",
|
||||
SupportsCustomPorts: true,
|
||||
GeoDataDir: "/var/lib/netbird/geolocation",
|
||||
},
|
||||
LogLevel: "info",
|
||||
}
|
||||
}
|
||||
|
||||
func loadConfig(cmd *cobra.Command, configPath string) (*commandConfig, error) {
|
||||
cfg, err := configloader.Load(configPath, defaultConfig(), configloader.Options{
|
||||
TagName: "yaml",
|
||||
AllowMissing: configPath == "",
|
||||
FlagSet: cmd.Flags(),
|
||||
Strict: true,
|
||||
InvalidEnvironment: configloader.InvalidEnvironmentIgnore,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := applyPerformanceEnvironment(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func applyPerformanceEnvironment(cfg *commandConfig) error {
|
||||
for _, setting := range []struct {
|
||||
name string
|
||||
target **uint32
|
||||
}{
|
||||
{name: envPreallocatedBuffers, target: &cfg.PreallocatedBuffers},
|
||||
{name: envMaxBatchSize, target: &cfg.MaxBatchSize},
|
||||
} {
|
||||
raw := os.Getenv(setting.name)
|
||||
if raw == "" {
|
||||
continue
|
||||
}
|
||||
parsed, err := strconv.ParseUint(raw, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid %s %q: %w", setting.name, raw, err)
|
||||
}
|
||||
value := uint32(parsed)
|
||||
*setting.target = &value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,923 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/netbirdio/netbird/trustedproxy"
|
||||
)
|
||||
|
||||
func TestExampleConfig(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
cfg, err := loadConfig(cmd, filepath.Join("..", "..", "..", "config.example.yaml"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "proxy.example.com", cfg.ProxyURL, "Example config should load")
|
||||
}
|
||||
|
||||
func TestLoadConfigPreservesLegacyDefaults(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, legacyProxyDefaults(t), cfg, "Proxy defaults should remain unchanged")
|
||||
}
|
||||
|
||||
func TestLoadConfigPreservesLegacyEnvironmentBindings(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv("NB_PROXY_LOG_LEVEL", "debug")
|
||||
t.Setenv("NB_PROXY_ADDRESS", ":8443")
|
||||
t.Setenv("NB_PROXY_MANAGEMENT_ADDRESS", "https://management.example.com:443")
|
||||
t.Setenv("NB_PROXY_DOMAIN", "proxy.example.com")
|
||||
t.Setenv("NB_PROXY_TOKEN", "proxy-token")
|
||||
t.Setenv("NB_PROXY_CERTIFICATE_DIRECTORY", "/var/lib/proxy/certs")
|
||||
t.Setenv("NB_PROXY_CERTIFICATE_FILE", "proxy.crt")
|
||||
t.Setenv("NB_PROXY_CERTIFICATE_KEY_FILE", "proxy.key")
|
||||
t.Setenv("NB_PROXY_ACME_CERTIFICATES", "true")
|
||||
t.Setenv("NB_PROXY_ACME_ADDRESS", ":8080")
|
||||
t.Setenv("NB_PROXY_ACME_DIRECTORY", "https://acme.example.com/directory")
|
||||
t.Setenv("NB_PROXY_ACME_EAB_KID", "eab-kid")
|
||||
t.Setenv("NB_PROXY_ACME_EAB_HMAC_KEY", "eab-hmac")
|
||||
t.Setenv("NB_PROXY_ACME_CHALLENGE_TYPE", "http-01")
|
||||
t.Setenv("NB_PROXY_CERT_LOCK_METHOD", "flock")
|
||||
t.Setenv("NB_PROXY_WILDCARD_CERT_DIR", "/var/lib/proxy/wildcards")
|
||||
t.Setenv("NB_PROXY_DEBUG_ENDPOINT", "true")
|
||||
t.Setenv("NB_PROXY_DEBUG_ENDPOINT_ADDRESS", "localhost:9444")
|
||||
t.Setenv("NB_PROXY_HEALTH_ADDRESS", "localhost:9080")
|
||||
t.Setenv("NB_PROXY_FORWARDED_PROTO", "https")
|
||||
t.Setenv("NB_PROXY_TRUSTED_PROXIES", "192.0.2.0/24")
|
||||
t.Setenv("NB_PROXY_WG_PORT", "51820")
|
||||
t.Setenv("NB_PROXY_PROXY_PROTOCOL", "true")
|
||||
t.Setenv("NB_PROXY_PRESHARED_KEY", "pre-shared-key")
|
||||
t.Setenv("NB_PROXY_SUPPORTS_CUSTOM_PORTS", "false")
|
||||
t.Setenv("NB_PROXY_REQUIRE_SUBDOMAIN", "true")
|
||||
t.Setenv("NB_PROXY_PRIVATE", "true")
|
||||
t.Setenv("NB_PROXY_MAX_DIAL_TIMEOUT", "5s")
|
||||
t.Setenv("NB_PROXY_MAX_SESSION_IDLE_TIMEOUT", "10m")
|
||||
t.Setenv("NB_PROXY_MAPPING_BATCH_WATCHDOG", "30s")
|
||||
t.Setenv("NB_PROXY_GEO_DATA_DIR", "/var/lib/proxy/geo")
|
||||
t.Setenv("NB_PROXY_CROWDSEC_API_URL", "https://crowdsec.example.com")
|
||||
t.Setenv("NB_PROXY_CROWDSEC_API_KEY", "crowdsec-key")
|
||||
t.Setenv("NB_PROXY_PREALLOCATED_BUFFERS", "1024")
|
||||
t.Setenv("NB_PROXY_MAX_BATCH_SIZE", "64")
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "debug", cfg.LogLevel, "Legacy log-level environment binding should remain supported")
|
||||
assert.Equal(t, ":8443", cfg.ListenAddr, "Legacy address environment binding should remain supported")
|
||||
assert.Equal(t, "https://management.example.com:443", cfg.ManagementAddress,
|
||||
"Legacy management environment binding should remain supported")
|
||||
assert.Equal(t, "proxy.example.com", cfg.ProxyURL, "Legacy domain environment binding should remain supported")
|
||||
assert.Equal(t, "proxy-token", cfg.ProxyToken, "Legacy token environment binding should remain supported")
|
||||
assert.Equal(t, "/var/lib/proxy/certs", cfg.CertificateDirectory, "Legacy certificate directory should remain supported")
|
||||
assert.Equal(t, "proxy.crt", cfg.CertificateFile, "Legacy certificate file should remain supported")
|
||||
assert.Equal(t, "proxy.key", cfg.CertificateKeyFile, "Legacy certificate key should remain supported")
|
||||
assert.True(t, cfg.GenerateACMECertificates, "Legacy ACME toggle should remain supported")
|
||||
assert.Equal(t, ":8080", cfg.ACMEChallengeAddress, "Legacy ACME address should remain supported")
|
||||
assert.Equal(t, "https://acme.example.com/directory", cfg.ACMEDirectory, "Legacy ACME directory should remain supported")
|
||||
assert.Equal(t, "eab-kid", cfg.ACMEEABKID, "Legacy EAB KID should remain supported")
|
||||
assert.Equal(t, "eab-hmac", cfg.ACMEEABHMACKey, "Legacy EAB HMAC key should remain supported")
|
||||
assert.Equal(t, "http-01", cfg.ACMEChallengeType, "Legacy ACME challenge type should remain supported")
|
||||
assert.Equal(t, "flock", string(cfg.CertLockMethod), "Legacy certificate lock method should remain supported")
|
||||
assert.Equal(t, "/var/lib/proxy/wildcards", cfg.WildcardCertDir, "Legacy wildcard certificate directory should remain supported")
|
||||
assert.True(t, cfg.DebugEndpointEnabled, "Legacy debug endpoint toggle should remain supported")
|
||||
assert.Equal(t, "localhost:9444", cfg.DebugEndpointAddress, "Legacy debug endpoint address should remain supported")
|
||||
assert.Equal(t, "localhost:9080", cfg.HealthAddr, "Legacy health address should remain supported")
|
||||
assert.Equal(t, "https", cfg.ForwardedProto, "Legacy forwarded-proto environment binding should remain supported")
|
||||
require.NotNil(t, cfg.TrustedProxies, "Legacy trusted proxy environment binding should remain supported")
|
||||
assert.False(t, cfg.TrustedProxies.Empty(), "Legacy trusted proxy environment binding should remain populated")
|
||||
assert.Equal(t, uint16(51820), cfg.WireguardPort, "Legacy tunnel port should remain supported")
|
||||
assert.True(t, cfg.ProxyProtocol, "Legacy PROXY protocol toggle should remain supported")
|
||||
assert.Equal(t, "pre-shared-key", cfg.PreSharedKey, "Legacy pre-shared key should remain supported")
|
||||
assert.False(t, cfg.SupportsCustomPorts, "Legacy custom-port toggle should remain supported")
|
||||
assert.True(t, cfg.RequireSubdomain, "Legacy subdomain toggle should remain supported")
|
||||
assert.True(t, cfg.Private, "Legacy private toggle should remain supported")
|
||||
assert.Equal(t, 5*time.Second, cfg.MaxDialTimeout, "Legacy dial timeout should remain supported")
|
||||
assert.Equal(t, 10*time.Minute, cfg.MaxSessionIdleTimeout, "Legacy idle timeout should remain supported")
|
||||
assert.Equal(t, 30*time.Second, cfg.MappingBatchWatchdog, "Legacy mapping watchdog should remain supported")
|
||||
assert.Equal(t, "/var/lib/proxy/geo", cfg.GeoDataDir, "Legacy geodata directory should remain supported")
|
||||
assert.Equal(t, "https://crowdsec.example.com", cfg.CrowdSecAPIURL, "Legacy CrowdSec URL should remain supported")
|
||||
assert.Equal(t, "crowdsec-key", cfg.CrowdSecAPIKey, "Legacy CrowdSec key should remain supported")
|
||||
require.NotNil(t, cfg.PreallocatedBuffers, "Legacy preallocated buffer environment binding should remain supported")
|
||||
assert.Equal(t, uint32(1024), *cfg.PreallocatedBuffers, "Legacy preallocated buffer value should remain supported")
|
||||
require.NotNil(t, cfg.MaxBatchSize, "Legacy maximum batch environment binding should remain supported")
|
||||
assert.Equal(t, uint32(64), *cfg.MaxBatchSize, "Legacy maximum batch value should remain supported")
|
||||
}
|
||||
|
||||
func TestLoadConfigPreservesLegacyInvalidEnvironmentBehavior(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
envName string
|
||||
value string
|
||||
wantErr bool
|
||||
validate func(*testing.T, *commandConfig)
|
||||
}{
|
||||
{
|
||||
name: "invalid boolean uses default",
|
||||
envName: "NB_PROXY_SUPPORTS_CUSTOM_PORTS",
|
||||
value: "invalid",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.True(t, cfg.SupportsCustomPorts, "Invalid legacy booleans should retain their default")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid uint16 uses default",
|
||||
envName: "NB_PROXY_WG_PORT",
|
||||
value: "invalid",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Zero(t, cfg.WireguardPort, "Invalid legacy uint16 values should retain their default")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid duration uses default",
|
||||
envName: "NB_PROXY_MAX_DIAL_TIMEOUT",
|
||||
value: "invalid",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Zero(t, cfg.MaxDialTimeout, "Invalid legacy durations should retain their default")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid watchdog uses default",
|
||||
envName: "NB_PROXY_MAPPING_BATCH_WATCHDOG",
|
||||
value: "invalid",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Zero(t, cfg.MappingBatchWatchdog, "Invalid legacy watchdog values should retain their default")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty performance value remains absent",
|
||||
envName: "NB_PROXY_PREALLOCATED_BUFFERS",
|
||||
value: "",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Nil(t, cfg.PreallocatedBuffers, "Empty legacy performance values should remain absent")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid performance value remains fatal",
|
||||
envName: "NB_PROXY_PREALLOCATED_BUFFERS",
|
||||
value: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty maximum batch remains absent",
|
||||
envName: "NB_PROXY_MAX_BATCH_SIZE",
|
||||
value: "",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Nil(t, cfg.MaxBatchSize, "Empty legacy maximum batch values should remain absent")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid maximum batch remains fatal",
|
||||
envName: "NB_PROXY_MAX_BATCH_SIZE",
|
||||
value: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty string clears default",
|
||||
envName: "NB_PROXY_MANAGEMENT_ADDRESS",
|
||||
value: "",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Empty(t, cfg.ManagementAddress, "Empty legacy strings should continue to clear their default")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv(test.envName, test.value)
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
if test.wantErr {
|
||||
assert.Error(t, err, "Legacy-fatal environment input should remain fatal")
|
||||
return
|
||||
}
|
||||
if !assert.NoError(t, err, "Legacy fallback parsing should not abort Proxy startup") {
|
||||
return
|
||||
}
|
||||
test.validate(t, cfg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLegacyProxyFlagsRemainRegistered(t *testing.T) {
|
||||
for _, name := range []string{
|
||||
"mgmt",
|
||||
"addr",
|
||||
"domain",
|
||||
"cert-dir",
|
||||
"acme-certs",
|
||||
"acme-addr",
|
||||
"acme-dir",
|
||||
"acme-eab-kid",
|
||||
"acme-eab-hmac-key",
|
||||
"acme-challenge-type",
|
||||
"debug-endpoint",
|
||||
"debug-endpoint-addr",
|
||||
"health-addr",
|
||||
"forwarded-proto",
|
||||
"trusted-proxies",
|
||||
"cert-file",
|
||||
"cert-key-file",
|
||||
"cert-lock-method",
|
||||
"wildcard-cert-dir",
|
||||
"wg-port",
|
||||
"proxy-protocol",
|
||||
"preshared-key",
|
||||
"supports-custom-ports",
|
||||
"require-subdomain",
|
||||
"private",
|
||||
"max-dial-timeout",
|
||||
"max-session-idle-timeout",
|
||||
"geo-data-dir",
|
||||
"crowdsec-api-url",
|
||||
"crowdsec-api-key",
|
||||
} {
|
||||
assert.NotNil(t, rootCmd.Flags().Lookup(name), "Legacy flag %s should remain registered", name)
|
||||
}
|
||||
for _, name := range []string{"log-level", "debug"} {
|
||||
assert.NotNil(t, rootCmd.PersistentFlags().Lookup(name), "Legacy persistent flag %s should remain registered", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadConfigIgnoresEmptyBooleanEnvironment(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv("NB_PROXY_SUPPORTS_CUSTOM_PORTS", "")
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, cfg.SupportsCustomPorts,
|
||||
"An empty boolean environment variable should retain the previous default")
|
||||
}
|
||||
|
||||
func TestDeprecatedDebugEnvironmentOverridesLogLevelFlag(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv("NB_PROXY_TOKEN", "test-token")
|
||||
t.Setenv("NB_PROXY_DOMAIN", "invalid domain")
|
||||
t.Setenv("NB_PROXY_DEBUG_LOGS", "true")
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
logLevelFlag := cmd.Flags().Lookup("log-level")
|
||||
require.NotNil(t, logLevelFlag, "Log level flag should be registered")
|
||||
oldDebugLogs := debugLogs
|
||||
oldConfigPath := configPath
|
||||
t.Cleanup(func() {
|
||||
debugLogs = oldDebugLogs
|
||||
configPath = oldConfigPath
|
||||
})
|
||||
|
||||
require.NoError(t, logLevelFlag.Value.Set("trace"))
|
||||
logLevelFlag.Changed = true
|
||||
debugLogs = envBoolOrDefault("NB_PROXY_DEBUG_LOGS", false)
|
||||
configPath = ""
|
||||
require.True(t, debugLogs, "Deprecated debug environment variable should be enabled")
|
||||
|
||||
var runErr error
|
||||
output := captureStderr(t, func() {
|
||||
runErr = runServer(cmd, nil)
|
||||
})
|
||||
require.Error(t, runErr)
|
||||
assert.Contains(t, output, "configured log level: debug",
|
||||
"The deprecated debug environment variable should retain its previous precedence")
|
||||
}
|
||||
|
||||
func TestLoadConfigPrecedence(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
configPath := filepath.Join(t.TempDir(), "proxy.yaml")
|
||||
require.NoError(t, os.WriteFile(configPath, []byte(`
|
||||
domain: file.example.com
|
||||
trustedProxies: 192.0.2.0/24
|
||||
maxDialTimeout: 5s
|
||||
`), 0o600))
|
||||
t.Setenv("NB_PROXY_TOKEN", "environment-token")
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
domainFlag := cmd.Flags().Lookup("domain")
|
||||
require.NoError(t, domainFlag.Value.Set("flag.example.com"))
|
||||
domainFlag.Changed = true
|
||||
|
||||
cfg, err := loadConfig(cmd, configPath)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "flag.example.com", cfg.ProxyURL, "Flags should override the configuration file")
|
||||
assert.Equal(t, "environment-token", cfg.ProxyToken, "Environment should populate secrets")
|
||||
assert.Equal(t, 5*time.Second, cfg.MaxDialTimeout, "Durations should be decoded from the file")
|
||||
require.NotNil(t, cfg.TrustedProxies, "Trusted proxies should be decoded")
|
||||
assert.False(t, cfg.TrustedProxies.Empty(), "Configured trusted proxies should not be empty")
|
||||
}
|
||||
|
||||
// proxy-01: legacy read only the explicit NB_PROXY_* names; NB_<YAMLKEY> style
|
||||
// variables were never consulted.
|
||||
func TestLoadConfigIgnoresAutomaticEnvironmentAliases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
alias string
|
||||
value string
|
||||
legacy map[string]string
|
||||
validate func(*testing.T, *commandConfig)
|
||||
}{
|
||||
{
|
||||
name: "listen address alias is ignored",
|
||||
alias: "NB_LISTENADDRESS",
|
||||
value: ":9999",
|
||||
legacy: map[string]string{"NB_PROXY_ADDRESS": ":8443"},
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Equal(t, ":8443", cfg.ListenAddr,
|
||||
"Legacy only read NB_PROXY_ADDRESS; NB_LISTENADDRESS must not override it")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "log level alias is ignored",
|
||||
alias: "NB_LOGLEVEL",
|
||||
value: "trace",
|
||||
legacy: map[string]string{"NB_PROXY_LOG_LEVEL": "warn"},
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Equal(t, "warn", cfg.LogLevel,
|
||||
"Legacy only read NB_PROXY_LOG_LEVEL; NB_LOGLEVEL must not override it")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "token alias is ignored",
|
||||
alias: "NB_PROXYTOKEN",
|
||||
value: "alias-token",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Empty(t, cfg.ProxyToken,
|
||||
"Legacy only read NB_PROXY_TOKEN; NB_PROXYTOKEN must not satisfy the token requirement")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "private alias is ignored",
|
||||
alias: "NB_PRIVATE",
|
||||
value: "true",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.False(t, cfg.Private,
|
||||
"Legacy only read NB_PROXY_PRIVATE; NB_PRIVATE must not enable private mode")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "id alias is ignored",
|
||||
alias: "NB_ID",
|
||||
value: "alias-id",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Empty(t, cfg.ID, "Legacy never read NB_ID; the proxy ID must stay empty")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "management address alias is ignored",
|
||||
alias: "NB_MANAGEMENTADDRESS",
|
||||
value: "https://alias.example.com:443",
|
||||
legacy: map[string]string{"NB_PROXY_MANAGEMENT_ADDRESS": "https://management.example.com:443"},
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Equal(t, "https://management.example.com:443", cfg.ManagementAddress,
|
||||
"Legacy only read NB_PROXY_MANAGEMENT_ADDRESS; NB_MANAGEMENTADDRESS must not override it")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wireguard port alias is ignored",
|
||||
alias: "NB_WIREGUARDPORT",
|
||||
value: "51820",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Zero(t, cfg.WireguardPort,
|
||||
"Legacy only read NB_PROXY_WG_PORT; NB_WIREGUARDPORT must not set the tunnel port")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
for name, value := range test.legacy {
|
||||
t.Setenv(name, value)
|
||||
}
|
||||
t.Setenv(test.alias, test.value)
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
require.NoError(t, err)
|
||||
test.validate(t, cfg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// proxy-02: NB_DOMAIN is a common self-hosted variable; legacy ignored it and
|
||||
// only honoured NB_PROXY_DOMAIN / --domain.
|
||||
func TestLoadConfigIgnoresSharedDomainEnvironment(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
domain string
|
||||
}{
|
||||
{name: "valid shared domain is ignored", domain: "netbird.example.org"},
|
||||
{name: "invalid shared domain is ignored", domain: "bad domain"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv("NB_PROXY_DOMAIN", "proxy.example.com")
|
||||
t.Setenv("NB_DOMAIN", test.domain)
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "proxy.example.com", cfg.ProxyURL,
|
||||
"Legacy only read NB_PROXY_DOMAIN; the shared NB_DOMAIN variable must not override it")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// proxy-03: legacy had no NB_PROXY_ID binding; the ID was always generated at
|
||||
// Server.Start time.
|
||||
func TestLoadConfigIgnoresProxyIDEnvironment(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv("NB_PROXY_ID", "my-proxy")
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, cfg.ID,
|
||||
"Legacy never read NB_PROXY_ID; the proxy ID must stay empty so Server.Start generates it")
|
||||
}
|
||||
|
||||
// proxy-04: legacy strconv.ParseBool rejected yes/no/on/off/y/n, logging a
|
||||
// warning and keeping the flag default.
|
||||
func TestLoadConfigPreservesLegacyBooleanVocabulary(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
envName string
|
||||
value string
|
||||
validate func(*testing.T, *commandConfig)
|
||||
}{
|
||||
{
|
||||
name: "no keeps custom ports enabled",
|
||||
envName: "NB_PROXY_SUPPORTS_CUSTOM_PORTS",
|
||||
value: "no",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.True(t, cfg.SupportsCustomPorts, "Legacy ParseBool rejected \"no\" and kept the default true")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "off keeps custom ports enabled",
|
||||
envName: "NB_PROXY_SUPPORTS_CUSTOM_PORTS",
|
||||
value: "off",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.True(t, cfg.SupportsCustomPorts, "Legacy ParseBool rejected \"off\" and kept the default true")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "n keeps custom ports enabled",
|
||||
envName: "NB_PROXY_SUPPORTS_CUSTOM_PORTS",
|
||||
value: "n",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.True(t, cfg.SupportsCustomPorts, "Legacy ParseBool rejected \"n\" and kept the default true")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "uppercase OFF keeps custom ports enabled",
|
||||
envName: "NB_PROXY_SUPPORTS_CUSTOM_PORTS",
|
||||
value: "OFF",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.True(t, cfg.SupportsCustomPorts, "Legacy ParseBool rejected \"OFF\" and kept the default true")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "yes keeps ACME disabled",
|
||||
envName: "NB_PROXY_ACME_CERTIFICATES",
|
||||
value: "yes",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.False(t, cfg.GenerateACMECertificates, "Legacy ParseBool rejected \"yes\" and kept the default false")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "uppercase ON keeps ACME disabled",
|
||||
envName: "NB_PROXY_ACME_CERTIFICATES",
|
||||
value: "ON",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.False(t, cfg.GenerateACMECertificates, "Legacy ParseBool rejected \"ON\" and kept the default false")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "y keeps ACME disabled",
|
||||
envName: "NB_PROXY_ACME_CERTIFICATES",
|
||||
value: "y",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.False(t, cfg.GenerateACMECertificates, "Legacy ParseBool rejected \"y\" and kept the default false")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on keeps PROXY protocol disabled",
|
||||
envName: "NB_PROXY_PROXY_PROTOCOL",
|
||||
value: "on",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.False(t, cfg.ProxyProtocol, "Legacy ParseBool rejected \"on\" and kept the default false")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Y keeps subdomain requirement disabled",
|
||||
envName: "NB_PROXY_REQUIRE_SUBDOMAIN",
|
||||
value: "Y",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.False(t, cfg.RequireSubdomain, "Legacy ParseBool rejected \"Y\" and kept the default false")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "on keeps private mode disabled",
|
||||
envName: "NB_PROXY_PRIVATE",
|
||||
value: "on",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.False(t, cfg.Private, "Legacy ParseBool rejected \"on\" and kept the default false")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv(test.envName, test.value)
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
if !assert.NoError(t, err, "Legacy boolean fallback parsing should not abort Proxy startup") {
|
||||
return
|
||||
}
|
||||
test.validate(t, cfg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// proxy-05: legacy parsed NB_PROXY_WG_PORT with strconv.ParseUint(v, 10, 16).
|
||||
func TestLoadConfigPreservesLegacyWireguardPortParsing(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
want uint16
|
||||
}{
|
||||
{name: "leading zero is decimal", value: "010", want: 10},
|
||||
{name: "hex prefix falls back to default", value: "0x1F", want: 0},
|
||||
{name: "octal prefix falls back to default", value: "0o17", want: 0},
|
||||
{name: "underscore separator falls back to default", value: "1_000", want: 0},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv("NB_PROXY_WG_PORT", test.value)
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
if !assert.NoError(t, err, "Legacy uint16 fallback parsing should not abort Proxy startup") {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, test.want, cfg.WireguardPort,
|
||||
"Legacy parsed NB_PROXY_WG_PORT=%q in base 10 (parse errors fell back to 0)", test.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// proxy-07: legacy time.ParseDuration("") failed, logging a warning and
|
||||
// keeping the default of 0; startup continued.
|
||||
func TestLoadConfigPreservesLegacyEmptyDurationEnvironment(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
envName string
|
||||
validate func(*testing.T, *commandConfig)
|
||||
}{
|
||||
{
|
||||
name: "empty dial timeout uses default",
|
||||
envName: "NB_PROXY_MAX_DIAL_TIMEOUT",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Zero(t, cfg.MaxDialTimeout, "Legacy treated an empty dial timeout as the default 0")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty idle timeout uses default",
|
||||
envName: "NB_PROXY_MAX_SESSION_IDLE_TIMEOUT",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Zero(t, cfg.MaxSessionIdleTimeout, "Legacy treated an empty idle timeout as the default 0")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty watchdog uses default",
|
||||
envName: "NB_PROXY_MAPPING_BATCH_WATCHDOG",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Zero(t, cfg.MappingBatchWatchdog, "Legacy treated an empty watchdog as the default 0")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv(test.envName, "")
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
if !assert.NoError(t, err, "Legacy treated an empty duration environment value as a warning, not a fatal error") {
|
||||
return
|
||||
}
|
||||
test.validate(t, cfg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// proxy-08: legacy time.ParseDuration errors (missing unit / invalid) logged a
|
||||
// warning and kept the default of 0; startup continued.
|
||||
func TestLoadConfigPreservesLegacyDurationFallbacks(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
envName string
|
||||
value string
|
||||
validate func(*testing.T, *commandConfig)
|
||||
}{
|
||||
{
|
||||
name: "unit-less dial timeout uses default",
|
||||
envName: "NB_PROXY_MAX_DIAL_TIMEOUT",
|
||||
value: "5",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Zero(t, cfg.MaxDialTimeout, "Legacy treated a unit-less dial timeout as the default 0")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unit-less idle timeout uses default",
|
||||
envName: "NB_PROXY_MAX_SESSION_IDLE_TIMEOUT",
|
||||
value: "5",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Zero(t, cfg.MaxSessionIdleTimeout, "Legacy treated a unit-less idle timeout as the default 0")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid idle timeout uses default",
|
||||
envName: "NB_PROXY_MAX_SESSION_IDLE_TIMEOUT",
|
||||
value: "invalid",
|
||||
validate: func(t *testing.T, cfg *commandConfig) {
|
||||
assert.Zero(t, cfg.MaxSessionIdleTimeout, "Legacy treated an invalid idle timeout as the default 0")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv(test.envName, test.value)
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
if !assert.NoError(t, err, "Legacy treated an unparseable duration environment value as a warning, not a fatal error") {
|
||||
return
|
||||
}
|
||||
test.validate(t, cfg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// proxy-09: legacy parsed the performance variables with
|
||||
// strconv.ParseUint(raw, 10, 32) and aborted startup on any parse error.
|
||||
func TestLoadConfigPreservesLegacyPerformanceParsing(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
envName string
|
||||
value string
|
||||
want uint32
|
||||
wantErr bool
|
||||
}{
|
||||
{name: "preallocated buffers leading zero is decimal", envName: "NB_PROXY_PREALLOCATED_BUFFERS", value: "010", want: 10},
|
||||
{name: "preallocated buffers two leading zeros is decimal", envName: "NB_PROXY_PREALLOCATED_BUFFERS", value: "0100", want: 100},
|
||||
{name: "preallocated buffers hex remains fatal", envName: "NB_PROXY_PREALLOCATED_BUFFERS", value: "0x10", wantErr: true},
|
||||
{name: "preallocated buffers long hex remains fatal", envName: "NB_PROXY_PREALLOCATED_BUFFERS", value: "0x100", wantErr: true},
|
||||
{name: "preallocated buffers underscore remains fatal", envName: "NB_PROXY_PREALLOCATED_BUFFERS", value: "1_000", wantErr: true},
|
||||
{name: "maximum batch leading zero is decimal", envName: "NB_PROXY_MAX_BATCH_SIZE", value: "010", want: 10},
|
||||
{name: "maximum batch two leading zeros is decimal", envName: "NB_PROXY_MAX_BATCH_SIZE", value: "0100", want: 100},
|
||||
{name: "maximum batch hex remains fatal", envName: "NB_PROXY_MAX_BATCH_SIZE", value: "0x10", wantErr: true},
|
||||
{name: "maximum batch long hex remains fatal", envName: "NB_PROXY_MAX_BATCH_SIZE", value: "0x100", wantErr: true},
|
||||
{name: "maximum batch underscore remains fatal", envName: "NB_PROXY_MAX_BATCH_SIZE", value: "1_000", wantErr: true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
t.Setenv(test.envName, test.value)
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
cfg, err := loadConfig(cmd, "")
|
||||
if test.wantErr {
|
||||
assert.Error(t, err,
|
||||
"Legacy strconv.ParseUint(%q, 10, 32) failed and aborted Proxy startup", test.value)
|
||||
return
|
||||
}
|
||||
if !assert.NoError(t, err, "Legacy base-10 performance values should not abort Proxy startup") {
|
||||
return
|
||||
}
|
||||
got := cfg.PreallocatedBuffers
|
||||
if test.envName == "NB_PROXY_MAX_BATCH_SIZE" {
|
||||
got = cfg.MaxBatchSize
|
||||
}
|
||||
require.NotNil(t, got, "Legacy performance values should be present when set")
|
||||
assert.Equal(t, test.want, *got,
|
||||
"Legacy parsed %s=%q in base 10", test.envName, test.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// proxy-10 / proxy-12: legacy read the token only from NB_PROXY_TOKEN, checked
|
||||
// it before anything else, and reported a fixed message when it was missing.
|
||||
func TestRunServerPreservesLegacyTokenRequirement(t *testing.T) {
|
||||
const legacyTokenError = "proxy token is required: set NB_PROXY_TOKEN environment variable"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
env map[string]string
|
||||
fileConfig string
|
||||
}{
|
||||
{
|
||||
name: "missing token without other sources",
|
||||
},
|
||||
{
|
||||
name: "file token does not satisfy the requirement",
|
||||
fileConfig: "proxyToken: file-token\ndomain: \"invalid domain\"\n",
|
||||
},
|
||||
{
|
||||
name: "token alias does not satisfy the requirement",
|
||||
env: map[string]string{"NB_PROXYTOKEN": "alias-token", "NB_PROXY_DOMAIN": "invalid domain"},
|
||||
},
|
||||
{
|
||||
name: "empty token with file token remains fatal",
|
||||
env: map[string]string{"NB_PROXY_TOKEN": ""},
|
||||
fileConfig: "proxyToken: file-token\ndomain: \"invalid domain\"\n",
|
||||
},
|
||||
{
|
||||
name: "missing token is reported before performance parsing",
|
||||
env: map[string]string{"NB_PROXY_PREALLOCATED_BUFFERS": "invalid"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
clearProxyConfigEnvironment(t)
|
||||
for name, value := range test.env {
|
||||
t.Setenv(name, value)
|
||||
}
|
||||
cmd := newLegacyProxyCommand(t)
|
||||
|
||||
oldDebugLogs := debugLogs
|
||||
oldConfigPath := configPath
|
||||
t.Cleanup(func() {
|
||||
debugLogs = oldDebugLogs
|
||||
configPath = oldConfigPath
|
||||
})
|
||||
debugLogs = false
|
||||
configPath = ""
|
||||
if test.fileConfig != "" {
|
||||
configPath = filepath.Join(t.TempDir(), "proxy.yaml")
|
||||
require.NoError(t, os.WriteFile(configPath, []byte(test.fileConfig), 0o600))
|
||||
}
|
||||
|
||||
var runErr error
|
||||
captureStderr(t, func() {
|
||||
runErr = runServer(cmd, nil)
|
||||
})
|
||||
require.Error(t, runErr)
|
||||
assert.EqualError(t, runErr, legacyTokenError,
|
||||
"Legacy accepted the token only from NB_PROXY_TOKEN and reported it before any other startup step")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newLegacyProxyCommand(t *testing.T) *cobra.Command {
|
||||
t.Helper()
|
||||
|
||||
defaults := legacyProxyDefaults(t)
|
||||
cmd := &cobra.Command{Use: "proxy-test"}
|
||||
flags := cmd.Flags()
|
||||
flags.String("log-level", defaults.LogLevel, "")
|
||||
flags.Bool("debug", false, "")
|
||||
flags.String("mgmt", defaults.ManagementAddress, "")
|
||||
flags.String("addr", defaults.ListenAddr, "")
|
||||
flags.String("domain", defaults.ProxyURL, "")
|
||||
flags.String("cert-dir", defaults.CertificateDirectory, "")
|
||||
flags.Bool("acme-certs", defaults.GenerateACMECertificates, "")
|
||||
flags.String("acme-addr", defaults.ACMEChallengeAddress, "")
|
||||
flags.String("acme-dir", defaults.ACMEDirectory, "")
|
||||
flags.String("acme-eab-kid", defaults.ACMEEABKID, "")
|
||||
flags.String("acme-eab-hmac-key", defaults.ACMEEABHMACKey, "")
|
||||
flags.String("acme-challenge-type", defaults.ACMEChallengeType, "")
|
||||
flags.Bool("debug-endpoint", defaults.DebugEndpointEnabled, "")
|
||||
flags.String("debug-endpoint-addr", defaults.DebugEndpointAddress, "")
|
||||
flags.String("health-addr", defaults.HealthAddr, "")
|
||||
flags.String("forwarded-proto", defaults.ForwardedProto, "")
|
||||
flags.String("trusted-proxies", "", "")
|
||||
flags.String("cert-file", defaults.CertificateFile, "")
|
||||
flags.String("cert-key-file", defaults.CertificateKeyFile, "")
|
||||
flags.String("cert-lock-method", string(defaults.CertLockMethod), "")
|
||||
flags.String("wildcard-cert-dir", defaults.WildcardCertDir, "")
|
||||
flags.Uint16("wg-port", defaults.WireguardPort, "")
|
||||
flags.Bool("proxy-protocol", defaults.ProxyProtocol, "")
|
||||
flags.String("preshared-key", defaults.PreSharedKey, "")
|
||||
flags.Bool("supports-custom-ports", defaults.SupportsCustomPorts, "")
|
||||
flags.Bool("require-subdomain", defaults.RequireSubdomain, "")
|
||||
flags.Bool("private", defaults.Private, "")
|
||||
flags.Duration("max-dial-timeout", defaults.MaxDialTimeout, "")
|
||||
flags.Duration("max-session-idle-timeout", defaults.MaxSessionIdleTimeout, "")
|
||||
flags.String("geo-data-dir", defaults.GeoDataDir, "")
|
||||
flags.String("crowdsec-api-url", defaults.CrowdSecAPIURL, "")
|
||||
flags.String("crowdsec-api-key", defaults.CrowdSecAPIKey, "")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func legacyProxyDefaults(t *testing.T) *commandConfig {
|
||||
t.Helper()
|
||||
|
||||
trustedProxies, err := trustedproxy.Parse("")
|
||||
require.NoError(t, err)
|
||||
cfg := &commandConfig{LogLevel: "info"}
|
||||
cfg.ListenAddr = ":443"
|
||||
cfg.ManagementAddress = DefaultManagementURL
|
||||
cfg.CertificateDirectory = "./certs"
|
||||
cfg.CertificateFile = "tls.crt"
|
||||
cfg.CertificateKeyFile = "tls.key"
|
||||
cfg.ACMEChallengeAddress = ":80"
|
||||
cfg.ACMEDirectory = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
cfg.ACMEChallengeType = "tls-alpn-01"
|
||||
cfg.CertLockMethod = "auto"
|
||||
cfg.DebugEndpointAddress = "localhost:8444"
|
||||
cfg.HealthAddr = "localhost:8080"
|
||||
cfg.ForwardedProto = "auto"
|
||||
cfg.TrustedProxies = trustedProxies
|
||||
cfg.SupportsCustomPorts = true
|
||||
cfg.GeoDataDir = "/var/lib/netbird/geolocation"
|
||||
return cfg
|
||||
}
|
||||
|
||||
func clearProxyConfigEnvironment(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
for _, name := range []string{
|
||||
"NB_PROXY_LOG_LEVEL",
|
||||
"NB_PROXY_DEBUG_LOGS",
|
||||
"NB_PROXY_MANAGEMENT_ADDRESS",
|
||||
"NB_PROXY_ADDRESS",
|
||||
"NB_PROXY_DOMAIN",
|
||||
"NB_PROXY_TOKEN",
|
||||
"NB_PROXY_CERTIFICATE_DIRECTORY",
|
||||
"NB_PROXY_CERTIFICATE_FILE",
|
||||
"NB_PROXY_CERTIFICATE_KEY_FILE",
|
||||
"NB_PROXY_ACME_CERTIFICATES",
|
||||
"NB_PROXY_ACME_ADDRESS",
|
||||
"NB_PROXY_ACME_DIRECTORY",
|
||||
"NB_PROXY_ACME_EAB_KID",
|
||||
"NB_PROXY_ACME_EAB_HMAC_KEY",
|
||||
"NB_PROXY_ACME_CHALLENGE_TYPE",
|
||||
"NB_PROXY_CERT_LOCK_METHOD",
|
||||
"NB_PROXY_WILDCARD_CERT_DIR",
|
||||
"NB_PROXY_DEBUG_ENDPOINT",
|
||||
"NB_PROXY_DEBUG_ENDPOINT_ADDRESS",
|
||||
"NB_PROXY_HEALTH_ADDRESS",
|
||||
"NB_PROXY_FORWARDED_PROTO",
|
||||
"NB_PROXY_TRUSTED_PROXIES",
|
||||
"NB_PROXY_WG_PORT",
|
||||
"NB_PROXY_PROXY_PROTOCOL",
|
||||
"NB_PROXY_PRESHARED_KEY",
|
||||
"NB_PROXY_SUPPORTS_CUSTOM_PORTS",
|
||||
"NB_PROXY_REQUIRE_SUBDOMAIN",
|
||||
"NB_PROXY_PRIVATE",
|
||||
"NB_PROXY_MAX_DIAL_TIMEOUT",
|
||||
"NB_PROXY_MAX_SESSION_IDLE_TIMEOUT",
|
||||
"NB_PROXY_MAPPING_BATCH_WATCHDOG",
|
||||
"NB_PROXY_GEO_DATA_DIR",
|
||||
"NB_PROXY_CROWDSEC_API_URL",
|
||||
"NB_PROXY_CROWDSEC_API_KEY",
|
||||
"NB_PROXY_PREALLOCATED_BUFFERS",
|
||||
"NB_PROXY_MAX_BATCH_SIZE",
|
||||
} {
|
||||
t.Setenv(name, "")
|
||||
require.NoError(t, os.Unsetenv(name))
|
||||
}
|
||||
}
|
||||
|
||||
func captureStderr(t *testing.T, fn func()) string {
|
||||
t.Helper()
|
||||
|
||||
reader, writer, err := os.Pipe()
|
||||
require.NoError(t, err)
|
||||
oldStderr := os.Stderr
|
||||
os.Stderr = writer
|
||||
defer func() {
|
||||
os.Stderr = oldStderr
|
||||
}()
|
||||
fn()
|
||||
os.Stderr = oldStderr
|
||||
require.NoError(t, writer.Close())
|
||||
|
||||
output, err := io.ReadAll(reader)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, reader.Close())
|
||||
return string(output)
|
||||
}
|
||||
+99
-48
@@ -15,7 +15,10 @@ import (
|
||||
|
||||
"github.com/netbirdio/netbird/shared/management/domain"
|
||||
|
||||
"github.com/netbirdio/netbird/client/embed"
|
||||
"github.com/netbirdio/netbird/proxy"
|
||||
nbacme "github.com/netbirdio/netbird/proxy/internal/acme"
|
||||
"github.com/netbirdio/netbird/trustedproxy"
|
||||
"github.com/netbirdio/netbird/util"
|
||||
)
|
||||
|
||||
@@ -23,7 +26,10 @@ const (
|
||||
// envPreallocatedBuffers caps the per-tunnel buffer pool. Zero (unset)
|
||||
// keeps the upstream uncapped default.
|
||||
envPreallocatedBuffers = "NB_PROXY_PREALLOCATED_BUFFERS"
|
||||
envMaxBatchSize = "NB_PROXY_MAX_BATCH_SIZE"
|
||||
// envMaxBatchSize overrides the per-tunnel batch size, which controls
|
||||
// how many buffers each receive/TUN worker eagerly allocates. Zero
|
||||
// (unset) keeps the platform default.
|
||||
envMaxBatchSize = "NB_PROXY_MAX_BATCH_SIZE"
|
||||
)
|
||||
|
||||
const DefaultManagementURL = "https://api.netbird.io:443"
|
||||
@@ -73,7 +79,6 @@ var (
|
||||
geoDataDir string
|
||||
crowdsecAPIURL string
|
||||
crowdsecAPIKey string
|
||||
configPath string
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
@@ -86,7 +91,6 @@ var rootCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.Flags().StringVar(&configPath, "config", "", "path to configuration file")
|
||||
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", envStringOrDefault("NB_PROXY_LOG_LEVEL", "info"), "Log level: panic, fatal, error, warn, info, debug, trace")
|
||||
rootCmd.PersistentFlags().BoolVar(&debugLogs, "debug", envBoolOrDefault("NB_PROXY_DEBUG_LOGS", false), "Enable debug logs")
|
||||
_ = rootCmd.PersistentFlags().MarkDeprecated("debug", "use --log-level instead")
|
||||
@@ -146,66 +150,113 @@ func runServer(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("proxy token is required: set %s environment variable", envProxyToken)
|
||||
}
|
||||
|
||||
cfg, err := loadConfig(cmd, configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("load config: %w", err)
|
||||
}
|
||||
cfg.ProxyToken = proxyToken
|
||||
|
||||
level := cfg.LogLevel
|
||||
level := logLevel
|
||||
if debugLogs {
|
||||
level = "debug"
|
||||
}
|
||||
logger := log.New()
|
||||
|
||||
_ = util.InitLogger(logger, level, util.LogConsole)
|
||||
|
||||
logger.Infof("configured log level: %s", level)
|
||||
|
||||
proxyConfig := cfg.Config
|
||||
proxyConfig.Logger = logger
|
||||
proxyConfig.Version = Version
|
||||
applyPerformanceConfig(&proxyConfig, cfg, logger)
|
||||
|
||||
switch proxyConfig.ForwardedProto {
|
||||
case "auto", "http", "https":
|
||||
default:
|
||||
return fmt.Errorf("invalid --forwarded-proto value %q: must be auto, http, or https", proxyConfig.ForwardedProto)
|
||||
var wgPool, wgBatch uint64
|
||||
var perf embed.Performance
|
||||
if raw := os.Getenv(envPreallocatedBuffers); raw != "" {
|
||||
n, err := strconv.ParseUint(raw, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid %s %q: %w", envPreallocatedBuffers, raw, err)
|
||||
}
|
||||
wgPool = n
|
||||
v := uint32(n)
|
||||
perf.PreallocatedBuffersPerPool = &v
|
||||
logger.Infof("tunnel preallocated buffers per pool: %d", n)
|
||||
}
|
||||
if raw := os.Getenv(envMaxBatchSize); raw != "" {
|
||||
n, err := strconv.ParseUint(raw, 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid %s %q: %w", envMaxBatchSize, raw, err)
|
||||
}
|
||||
wgBatch = n
|
||||
v := uint32(n)
|
||||
perf.MaxBatchSize = &v
|
||||
logger.Infof("tunnel max batch size override: %d", n)
|
||||
}
|
||||
if wgPool > 0 {
|
||||
// Each bind recv goroutine (IPv4 + IPv6 + ICE relay) plus
|
||||
// RoutineReadFromTUN eagerly reserves `batch` message buffers for
|
||||
// the lifetime of the Device. A pool cap below that floor blocks
|
||||
// the receive pipeline at startup.
|
||||
batch := wgBatch
|
||||
if batch == 0 {
|
||||
batch = 128
|
||||
}
|
||||
const recvGoroutines = 4
|
||||
floor := batch * recvGoroutines
|
||||
if wgPool < floor {
|
||||
logger.Warnf("%s=%d is below the eager-allocation floor (~%d for batch=%d); startup may deadlock",
|
||||
envPreallocatedBuffers, wgPool, floor, batch)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := domain.ValidateDomains([]string{proxyConfig.ProxyURL}); err != nil {
|
||||
return fmt.Errorf("invalid domain value %q: %w", proxyConfig.ProxyURL, err)
|
||||
switch forwardedProto {
|
||||
case "auto", "http", "https":
|
||||
default:
|
||||
return fmt.Errorf("invalid --forwarded-proto value %q: must be auto, http, or https", forwardedProto)
|
||||
}
|
||||
|
||||
_, err := domain.ValidateDomains([]string{proxyDomain})
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid domain value %q: %w", proxyDomain, err)
|
||||
}
|
||||
|
||||
parsedTrustedProxies, err := trustedproxy.Parse(trustedProxies)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid --trusted-proxies: %w", err)
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)
|
||||
defer stop()
|
||||
|
||||
srv := proxy.New(ctx, proxyConfig)
|
||||
return srv.ListenAndServe(ctx, proxyConfig.ListenAddr)
|
||||
}
|
||||
srv := proxy.New(ctx, proxy.Config{
|
||||
ListenAddr: addr,
|
||||
Logger: logger,
|
||||
Version: Version,
|
||||
ManagementAddress: mgmtAddr,
|
||||
ProxyURL: proxyDomain,
|
||||
ProxyToken: proxyToken,
|
||||
CertificateDirectory: certDir,
|
||||
CertificateFile: certFile,
|
||||
CertificateKeyFile: certKeyFile,
|
||||
GenerateACMECertificates: acmeCerts,
|
||||
ACMEChallengeAddress: acmeAddr,
|
||||
ACMEDirectory: acmeDir,
|
||||
ACMEEABKID: acmeEABKID,
|
||||
ACMEEABHMACKey: acmeEABHMACKey,
|
||||
ACMEChallengeType: acmeChallengeType,
|
||||
DebugEndpointEnabled: debugEndpoint,
|
||||
DebugEndpointAddress: debugEndpointAddr,
|
||||
HealthAddr: healthAddr,
|
||||
ForwardedProto: forwardedProto,
|
||||
TrustedProxies: parsedTrustedProxies,
|
||||
CertLockMethod: nbacme.CertLockMethod(certLockMethod),
|
||||
WildcardCertDir: wildcardCertDir,
|
||||
WireguardPort: wgPort,
|
||||
Performance: perf,
|
||||
ProxyProtocol: proxyProtocol,
|
||||
PreSharedKey: preSharedKey,
|
||||
SupportsCustomPorts: supportsCustomPorts,
|
||||
RequireSubdomain: requireSubdomain,
|
||||
Private: private,
|
||||
MaxDialTimeout: maxDialTimeout,
|
||||
MaxSessionIdleTimeout: maxSessionIdleTimeout,
|
||||
MappingBatchWatchdog: envDurationOrDefault("NB_PROXY_MAPPING_BATCH_WATCHDOG", 0),
|
||||
GeoDataDir: geoDataDir,
|
||||
CrowdSecAPIURL: crowdsecAPIURL,
|
||||
CrowdSecAPIKey: crowdsecAPIKey,
|
||||
})
|
||||
|
||||
func applyPerformanceConfig(proxyConfig *proxy.Config, cfg *commandConfig, logger *log.Logger) {
|
||||
if cfg.PreallocatedBuffers != nil {
|
||||
proxyConfig.Performance.PreallocatedBuffersPerPool = cfg.PreallocatedBuffers
|
||||
logger.Infof("tunnel preallocated buffers per pool: %d", *cfg.PreallocatedBuffers)
|
||||
}
|
||||
if cfg.MaxBatchSize != nil {
|
||||
proxyConfig.Performance.MaxBatchSize = cfg.MaxBatchSize
|
||||
logger.Infof("tunnel max batch size override: %d", *cfg.MaxBatchSize)
|
||||
}
|
||||
if cfg.PreallocatedBuffers == nil || *cfg.PreallocatedBuffers == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
batch := uint64(128)
|
||||
if cfg.MaxBatchSize != nil && *cfg.MaxBatchSize > 0 {
|
||||
batch = uint64(*cfg.MaxBatchSize)
|
||||
}
|
||||
const recvGoroutines = 4
|
||||
floor := batch * recvGoroutines
|
||||
pool := uint64(*cfg.PreallocatedBuffers)
|
||||
if pool < floor {
|
||||
logger.Warnf("%s=%d is below the eager-allocation floor (~%d for batch=%d); startup may deadlock",
|
||||
envPreallocatedBuffers, pool, floor, batch)
|
||||
}
|
||||
return srv.ListenAndServe(ctx, addr)
|
||||
}
|
||||
|
||||
func envBoolOrDefault(key string, def bool) bool {
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# Command-line flags override environment variables, which override this file.
|
||||
listenAddress: ":443"
|
||||
id: "proxy-1"
|
||||
domain: "proxy.example.com"
|
||||
managementAddress: "https://api.netbird.io:443"
|
||||
proxyToken: "replace-with-a-proxy-token"
|
||||
|
||||
certificateDirectory: "/var/lib/netbird/certs"
|
||||
certificateFile: "tls.crt"
|
||||
certificateKeyFile: "tls.key"
|
||||
generateACMECertificates: true
|
||||
acmeChallengeAddress: ":80"
|
||||
acmeDirectory: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
acmeEABKID: ""
|
||||
acmeEABHMACKey: ""
|
||||
acmeChallengeType: "tls-alpn-01"
|
||||
certLockMethod: "auto"
|
||||
wildcardCertDir: ""
|
||||
|
||||
debugEndpointEnabled: false
|
||||
debugEndpointAddress: "localhost:8444"
|
||||
healthAddress: "localhost:8080"
|
||||
forwardedProto: "auto"
|
||||
trustedProxies: ""
|
||||
wireguardPort: 0
|
||||
proxyProtocol: false
|
||||
preSharedKey: ""
|
||||
supportsCustomPorts: true
|
||||
requireSubdomain: false
|
||||
private: false
|
||||
maxDialTimeout: "0s"
|
||||
maxSessionIdleTimeout: "0s"
|
||||
mappingBatchWatchdog: "0s"
|
||||
geoDataDir: "/var/lib/netbird/geolocation"
|
||||
crowdSecAPIURL: ""
|
||||
crowdSecAPIKey: ""
|
||||
logLevel: "info"
|
||||
|
||||
# Optional tunnel memory and throughput tuning.
|
||||
# preallocatedBuffers: 4096
|
||||
# maxBatchSize: 128
|
||||
+36
-36
@@ -20,112 +20,112 @@ import (
|
||||
// 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 `yaml:"listenAddress" env:"NB_PROXY_ADDRESS" flag:"addr"`
|
||||
ListenAddr string
|
||||
// ID identifies this proxy instance to management. Empty values are
|
||||
// replaced with a timestamped default at Server.Start time (see
|
||||
// initDefaults), not in New.
|
||||
ID string `yaml:"id" env:"-"`
|
||||
ID string
|
||||
// Logger is the logrus logger used everywhere. Empty values fall
|
||||
// back to log.StandardLogger() at Server.Start time (see
|
||||
// initDefaults), not in New.
|
||||
Logger *log.Logger `yaml:"-" env:"-" flag:"-"`
|
||||
Logger *log.Logger
|
||||
// Version is the build version string reported to management. Empty
|
||||
// values are replaced with "dev" at Server.Start time (see
|
||||
// initDefaults), not in New.
|
||||
Version string `yaml:"-" env:"-" flag:"-"`
|
||||
Version string
|
||||
// ProxyURL is the public address operators use to reach this proxy.
|
||||
ProxyURL string `yaml:"domain" env:"NB_PROXY_DOMAIN" flag:"domain"`
|
||||
ProxyURL string
|
||||
// ManagementAddress is the gRPC URL of the management server.
|
||||
ManagementAddress string `yaml:"managementAddress" env:"NB_PROXY_MANAGEMENT_ADDRESS" flag:"mgmt"`
|
||||
ManagementAddress string
|
||||
// ProxyToken authenticates this proxy with the management server.
|
||||
ProxyToken string `yaml:"proxyToken" env:"NB_PROXY_TOKEN"`
|
||||
ProxyToken string
|
||||
|
||||
// CertificateDirectory is the directory holding TLS certificate
|
||||
// material (static or ACME-provisioned).
|
||||
CertificateDirectory string `yaml:"certificateDirectory" env:"NB_PROXY_CERTIFICATE_DIRECTORY" flag:"cert-dir"`
|
||||
CertificateDirectory string
|
||||
// CertificateFile is the certificate filename within
|
||||
// CertificateDirectory.
|
||||
CertificateFile string `yaml:"certificateFile" env:"NB_PROXY_CERTIFICATE_FILE" flag:"cert-file"`
|
||||
CertificateFile string
|
||||
// CertificateKeyFile is the private key filename within
|
||||
// CertificateDirectory.
|
||||
CertificateKeyFile string `yaml:"certificateKeyFile" env:"NB_PROXY_CERTIFICATE_KEY_FILE" flag:"cert-key-file"`
|
||||
CertificateKeyFile string
|
||||
// GenerateACMECertificates toggles ACME certificate provisioning.
|
||||
GenerateACMECertificates bool `yaml:"generateACMECertificates" env:"NB_PROXY_ACME_CERTIFICATES" flag:"acme-certs"`
|
||||
GenerateACMECertificates bool
|
||||
// ACMEChallengeAddress is the listen address for HTTP-01 challenges.
|
||||
ACMEChallengeAddress string `yaml:"acmeChallengeAddress" env:"NB_PROXY_ACME_ADDRESS" flag:"acme-addr"`
|
||||
ACMEChallengeAddress string
|
||||
// ACMEDirectory is the ACME directory URL (Let's Encrypt by default).
|
||||
ACMEDirectory string `yaml:"acmeDirectory" env:"NB_PROXY_ACME_DIRECTORY" flag:"acme-dir"`
|
||||
ACMEDirectory string
|
||||
// ACMEEABKID is the External Account Binding Key ID for CAs that
|
||||
// require EAB (e.g. ZeroSSL).
|
||||
ACMEEABKID string `yaml:"acmeEABKID" env:"NB_PROXY_ACME_EAB_KID" flag:"acme-eab-kid"`
|
||||
ACMEEABKID string
|
||||
// ACMEEABHMACKey is the External Account Binding HMAC key for CAs
|
||||
// that require EAB.
|
||||
ACMEEABHMACKey string `yaml:"acmeEABHMACKey" env:"NB_PROXY_ACME_EAB_HMAC_KEY" flag:"acme-eab-hmac-key"`
|
||||
ACMEEABHMACKey string
|
||||
// ACMEChallengeType is the ACME challenge type ("tls-alpn-01" or
|
||||
// "http-01"). Empty defaults to "tls-alpn-01".
|
||||
ACMEChallengeType string `yaml:"acmeChallengeType" env:"NB_PROXY_ACME_CHALLENGE_TYPE" flag:"acme-challenge-type"`
|
||||
ACMEChallengeType string
|
||||
// CertLockMethod controls how ACME certificate locks are coordinated
|
||||
// across replicas.
|
||||
CertLockMethod acme.CertLockMethod `yaml:"certLockMethod" env:"NB_PROXY_CERT_LOCK_METHOD" flag:"cert-lock-method"`
|
||||
CertLockMethod acme.CertLockMethod
|
||||
// WildcardCertDir is an optional directory containing static wildcard
|
||||
// certificates that override ACME for matching domains.
|
||||
WildcardCertDir string `yaml:"wildcardCertDir" env:"NB_PROXY_WILDCARD_CERT_DIR" flag:"wildcard-cert-dir"`
|
||||
WildcardCertDir string
|
||||
|
||||
// DebugEndpointEnabled toggles the debug HTTP endpoint.
|
||||
DebugEndpointEnabled bool `yaml:"debugEndpointEnabled" env:"NB_PROXY_DEBUG_ENDPOINT" flag:"debug-endpoint"`
|
||||
DebugEndpointEnabled bool
|
||||
// DebugEndpointAddress is the bind address for the debug endpoint.
|
||||
DebugEndpointAddress string `yaml:"debugEndpointAddress" env:"NB_PROXY_DEBUG_ENDPOINT_ADDRESS" flag:"debug-endpoint-addr"`
|
||||
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 `yaml:"healthAddress" env:"NB_PROXY_HEALTH_ADDRESS" flag:"health-addr"`
|
||||
HealthAddr string
|
||||
|
||||
// ForwardedProto overrides the X-Forwarded-Proto value sent to
|
||||
// backends. Valid values: "auto", "http", "https".
|
||||
ForwardedProto string `yaml:"forwardedProto" env:"NB_PROXY_FORWARDED_PROTO" flag:"forwarded-proto"`
|
||||
ForwardedProto string
|
||||
// TrustedProxies is the set of trusted upstream proxies that may set
|
||||
// forwarding headers.
|
||||
TrustedProxies *trustedproxy.List `yaml:"trustedProxies" env:"NB_PROXY_TRUSTED_PROXIES" flag:"trusted-proxies"`
|
||||
TrustedProxies *trustedproxy.List
|
||||
// WireguardPort is the UDP port for the embedded NetBird tunnel.
|
||||
// Zero asks the OS for a random port.
|
||||
WireguardPort uint16 `yaml:"wireguardPort" env:"NB_PROXY_WG_PORT" flag:"wg-port"`
|
||||
WireguardPort uint16
|
||||
// ProxyProtocol enables PROXY protocol (v1/v2) on TCP listeners.
|
||||
ProxyProtocol bool `yaml:"proxyProtocol" env:"NB_PROXY_PROXY_PROTOCOL" flag:"proxy-protocol"`
|
||||
ProxyProtocol bool
|
||||
// PreSharedKey is the WireGuard pre-shared key used between the
|
||||
// proxy's embedded clients and peers.
|
||||
PreSharedKey string `yaml:"preSharedKey" env:"NB_PROXY_PRESHARED_KEY" flag:"preshared-key"`
|
||||
PreSharedKey string
|
||||
// Performance configures the tunnel pool/batch sizes for every
|
||||
// embedded client this proxy creates. Zero values fall back to
|
||||
// upstream defaults.
|
||||
Performance embed.Performance `yaml:"performance" env:"-" flag:"-"`
|
||||
Performance embed.Performance
|
||||
|
||||
// SupportsCustomPorts indicates whether the proxy can bind arbitrary
|
||||
// ports for TCP/UDP/TLS services.
|
||||
SupportsCustomPorts bool `yaml:"supportsCustomPorts" env:"NB_PROXY_SUPPORTS_CUSTOM_PORTS" flag:"supports-custom-ports"`
|
||||
SupportsCustomPorts bool
|
||||
// RequireSubdomain forces accounts to use a subdomain in front of
|
||||
// the proxy's cluster domain.
|
||||
RequireSubdomain bool `yaml:"requireSubdomain" env:"NB_PROXY_REQUIRE_SUBDOMAIN" flag:"require-subdomain"`
|
||||
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 `yaml:"private" env:"NB_PROXY_PRIVATE" flag:"private"`
|
||||
Private bool
|
||||
|
||||
// MaxDialTimeout caps the per-service backend dial timeout.
|
||||
MaxDialTimeout time.Duration `yaml:"maxDialTimeout" env:"NB_PROXY_MAX_DIAL_TIMEOUT" flag:"max-dial-timeout"`
|
||||
MaxDialTimeout time.Duration
|
||||
// MaxSessionIdleTimeout caps the per-service session idle timeout.
|
||||
MaxSessionIdleTimeout time.Duration `yaml:"maxSessionIdleTimeout" env:"NB_PROXY_MAX_SESSION_IDLE_TIMEOUT" flag:"max-session-idle-timeout"`
|
||||
MaxSessionIdleTimeout time.Duration
|
||||
// MappingBatchWatchdog bounds how long a single mapping batch may spend
|
||||
// being applied before the receive loop reconnects to resync. Zero falls
|
||||
// back to the internal default.
|
||||
MappingBatchWatchdog time.Duration `yaml:"mappingBatchWatchdog" env:"NB_PROXY_MAPPING_BATCH_WATCHDOG"`
|
||||
MappingBatchWatchdog time.Duration
|
||||
|
||||
// GeoDataDir is the directory containing GeoLite2 MMDB files.
|
||||
GeoDataDir string `yaml:"geoDataDir" env:"NB_PROXY_GEO_DATA_DIR" flag:"geo-data-dir"`
|
||||
GeoDataDir string
|
||||
// CrowdSecAPIURL is the CrowdSec LAPI URL. Empty disables CrowdSec.
|
||||
CrowdSecAPIURL string `yaml:"crowdSecAPIURL" env:"NB_PROXY_CROWDSEC_API_URL" flag:"crowdsec-api-url"`
|
||||
CrowdSecAPIURL string
|
||||
// CrowdSecAPIKey is the CrowdSec bouncer API key. Empty disables
|
||||
// CrowdSec.
|
||||
CrowdSecAPIKey string `yaml:"crowdSecAPIKey" env:"NB_PROXY_CROWDSEC_API_KEY" flag:"crowdsec-api-key"`
|
||||
CrowdSecAPIKey string
|
||||
}
|
||||
|
||||
// New builds a Server from cfg without performing any I/O. No goroutines
|
||||
|
||||
Reference in New Issue
Block a user