feat: add support for CIDR and IP address lists in TRUST_PROXY

This commit is contained in:
Elias Schneider
2026-07-13 09:22:30 +02:00
parent d9ead47d19
commit 187cd8ddcd
3 changed files with 73 additions and 17 deletions

View File

@@ -68,7 +68,10 @@ func initEngine() (*gin.Engine, error) {
r := gin.New() r := gin.New()
initLogger(r) initLogger(r)
configureEngine(r) err := configureEngine(r)
if err != nil {
return nil, err
}
registerGlobalMiddleware(r) registerGlobalMiddleware(r)
return r, nil return r, nil
@@ -86,9 +89,10 @@ func setGinMode() {
} }
} }
func configureEngine(r *gin.Engine) { func configureEngine(r *gin.Engine) error {
if !common.EnvConfig.TrustProxy { err := r.SetTrustedProxies(common.EnvConfig.TrustProxy)
_ = r.SetTrustedProxies(nil) if err != nil {
return fmt.Errorf("failed to configure trusted proxies: %w", err)
} }
if common.EnvConfig.TrustedPlatform != "" { if common.EnvConfig.TrustedPlatform != "" {
@@ -99,6 +103,8 @@ func configureEngine(r *gin.Engine) {
common.Name, common.Name,
otelgin.WithFilter(shouldTraceRequest)), otelgin.WithFilter(shouldTraceRequest)),
) )
return nil
} }
// shouldTraceRequest reports whether an incoming request should be traced. // shouldTraceRequest reports whether an incoming request should be traced.

View File

@@ -8,6 +8,7 @@ import (
"net/url" "net/url"
"os" "os"
"reflect" "reflect"
"strconv"
"strings" "strings"
"github.com/caarlos0/env/v11" "github.com/caarlos0/env/v11"
@@ -17,6 +18,7 @@ import (
type AppEnv string type AppEnv string
type DbProvider string type DbProvider string
type TrustProxyConfig []string
const ( const (
// TracerName should be passed to otel.Tracer, trace.SpanFromContext when creating custom spans. // TracerName should be passed to otel.Tracer, trace.SpanFromContext when creating custom spans.
@@ -42,18 +44,18 @@ type EnvConfigSchema struct {
EncryptionKey []byte `env:"ENCRYPTION_KEY" options:"file"` EncryptionKey []byte `env:"ENCRYPTION_KEY" options:"file"`
AppURL string `env:"APP_URL" options:"toLower,trimTrailingSlash"` AppURL string `env:"APP_URL" options:"toLower,trimTrailingSlash"`
DbProvider DbProvider DbProvider DbProvider
DbConnectionString string `env:"DB_CONNECTION_STRING" options:"file"` DbConnectionString string `env:"DB_CONNECTION_STRING" options:"file"`
TrustProxy bool `env:"TRUST_PROXY"` TrustProxy TrustProxyConfig `env:"TRUST_PROXY"`
TrustedPlatform string `env:"TRUSTED_PLATFORM"` TrustedPlatform string `env:"TRUSTED_PLATFORM"`
AuditLogRetentionDays int `env:"AUDIT_LOG_RETENTION_DAYS"` AuditLogRetentionDays int `env:"AUDIT_LOG_RETENTION_DAYS"`
AnalyticsDisabled bool `env:"ANALYTICS_DISABLED"` AnalyticsDisabled bool `env:"ANALYTICS_DISABLED"`
AllowDowngrade bool `env:"ALLOW_DOWNGRADE"` AllowDowngrade bool `env:"ALLOW_DOWNGRADE"`
AllowInsecureCallbackURLs bool `env:"ALLOW_INSECURE_CALLBACK_URLS"` AllowInsecureCallbackURLs bool `env:"ALLOW_INSECURE_CALLBACK_URLS"`
InternalAppURL string `env:"INTERNAL_APP_URL"` InternalAppURL string `env:"INTERNAL_APP_URL"`
UiConfigDisabled bool `env:"UI_CONFIG_DISABLED"` UiConfigDisabled bool `env:"UI_CONFIG_DISABLED"`
DisableRateLimiting bool `env:"DISABLE_RATE_LIMITING"` DisableRateLimiting bool `env:"DISABLE_RATE_LIMITING"`
VersionCheckDisabled bool `env:"VERSION_CHECK_DISABLED"` VersionCheckDisabled bool `env:"VERSION_CHECK_DISABLED"`
StaticApiKey string `env:"STATIC_API_KEY" options:"file"` StaticApiKey string `env:"STATIC_API_KEY" options:"file"`
FileBackend string `env:"FILE_BACKEND" options:"toLower"` FileBackend string `env:"FILE_BACKEND" options:"toLower"`
UploadPath string `env:"UPLOAD_PATH"` UploadPath string `env:"UPLOAD_PATH"`
@@ -392,3 +394,29 @@ func (a AppEnv) IsProduction() bool {
func (a AppEnv) IsTest() bool { func (a AppEnv) IsTest() bool {
return a == AppEnvTest return a == AppEnvTest
} }
func (config *TrustProxyConfig) UnmarshalText(text []byte) error {
value := strings.TrimSpace(string(text))
// Support boolean values for completely enabling or disabling trust proxy
enabled, err := strconv.ParseBool(value)
if err == nil {
if enabled {
*config = TrustProxyConfig{"0.0.0.0/0", "::/0"}
} else {
*config = nil
}
return nil
}
// Normalize and validate each explicit proxy before the server starts
proxies := strings.Split(value, ",")
for i, proxy := range proxies {
proxy = strings.TrimSpace(proxy)
proxies[i] = proxy
}
*config = proxies
return nil
}

View File

@@ -124,11 +124,33 @@ func TestParseEnvConfig(t *testing.T) {
err := parseAndValidateEnvConfig(t) err := parseAndValidateEnvConfig(t)
require.NoError(t, err) require.NoError(t, err)
assert.True(t, EnvConfig.UiConfigDisabled) assert.True(t, EnvConfig.UiConfigDisabled)
assert.True(t, EnvConfig.TrustProxy) assert.Equal(t, TrustProxyConfig{"0.0.0.0/0", "::/0"}, EnvConfig.TrustProxy)
assert.False(t, EnvConfig.AnalyticsDisabled) assert.False(t, EnvConfig.AnalyticsDisabled)
assert.False(t, EnvConfig.AllowInsecureCallbackURLs) assert.False(t, EnvConfig.AllowInsecureCallbackURLs)
}) })
t.Run("should parse trusted proxy IP addresses and CIDR ranges", func(t *testing.T) {
EnvConfig = defaultConfig()
t.Setenv("TRUST_PROXY", "10.0.0.0/8, 192.168.1.10, ::1/128")
err := parseAndValidateEnvConfig(t)
require.NoError(t, err)
assert.Equal(t, TrustProxyConfig{"10.0.0.0/8", "192.168.1.10", "::1/128"}, EnvConfig.TrustProxy)
})
t.Run("should disable trusted proxies when set to false", func(t *testing.T) {
EnvConfig = defaultConfig()
t.Setenv("TRUST_PROXY", "false")
err := parseAndValidateEnvConfig(t)
require.NoError(t, err)
assert.Nil(t, EnvConfig.TrustProxy)
})
t.Run("should allow insecure callback URLs by default", func(t *testing.T) {
assert.True(t, defaultConfig().AllowInsecureCallbackURLs)
})
t.Run("should default audit log retention days to 90", func(t *testing.T) { t.Run("should default audit log retention days to 90", func(t *testing.T) {
EnvConfig = defaultConfig() EnvConfig = defaultConfig()
t.Setenv("DB_PROVIDER", "sqlite") t.Setenv("DB_PROVIDER", "sqlite")