feat: add trusted proxy identity and overload stats

This commit is contained in:
2026-09-22 18:19:39 +08:00
parent 2259606f72
commit 95dc4568f6
17 changed files with 346 additions and 42 deletions

View File

@@ -4,6 +4,7 @@ import (
"strings"
"time"
"git.yuntuo.hk/k2safe/go-omni/httpx"
"git.yuntuo.hk/k2safe/go-omni/logx"
)
@@ -14,6 +15,7 @@ type Config struct {
Address string
RequestIDHeader string
EnableLogger bool
TrustedProxies []string
ShutdownTimeout time.Duration
Log logx.Config
}
@@ -38,6 +40,9 @@ func (c Config) Validate() error {
if c.ShutdownTimeout <= 0 {
return &ConfigError{Field: "shutdown_timeout", Message: "must be positive"}
}
if _, err := httpx.NewClientIPResolver(c.TrustedProxies...); err != nil {
return &ConfigError{Field: "trusted_proxies", Message: err.Error()}
}
return nil
}
@@ -66,10 +71,22 @@ func WithLogger(enabled bool) Option {
return func(cfg *Config) { cfg.EnableLogger = enabled }
}
// WithTrustedProxies allows only these IPs/CIDRs to supply X-Forwarded-For.
// An empty list disables forwarding-header trust. Invalid entries are rejected
// by Config.Validate and panic when passed directly to App.New.
func WithTrustedProxies(proxies ...string) Option {
proxies = append([]string(nil), proxies...)
return func(cfg *Config) { cfg.TrustedProxies = append([]string(nil), proxies...) }
}
// WithConfig applies a complete configuration loaded from a file or
// environment. Later options can still override individual fields.
func WithConfig(settings Config) Option {
return func(cfg *Config) { *cfg = settings }
settings.TrustedProxies = append([]string(nil), settings.TrustedProxies...)
return func(cfg *Config) {
*cfg = settings
cfg.TrustedProxies = append([]string(nil), settings.TrustedProxies...)
}
}
// WithLogConfig configures the logger used by the application runtime.

View File

@@ -9,6 +9,7 @@ import (
func TestLoadFromEnvUsesDefaults(t *testing.T) {
for _, key := range []string{
"GO_OMNI_HTTP_ADDR", "GO_OMNI_REQUEST_ID_HEADER", "GO_OMNI_ENABLE_LOGGER",
"GO_OMNI_TRUSTED_PROXIES",
"GO_OMNI_LOG_LEVEL", "GO_OMNI_LOG_FORMAT", "GO_OMNI_LOG_OUTPUT",
"GO_OMNI_LOG_FILE", "GO_OMNI_SERVICE", "GO_OMNI_ENVIRONMENT",
"GO_OMNI_LOG_ADD_SOURCE", "GO_OMNI_SHUTDOWN_TIMEOUT",
@@ -31,6 +32,7 @@ func TestLoadFromEnvUsesDefaults(t *testing.T) {
func TestLoadFromEnvOverridesSettings(t *testing.T) {
t.Setenv("GO_OMNI_HTTP_ADDR", "127.0.0.1:9090")
t.Setenv("GO_OMNI_REQUEST_ID_HEADER", "X-Correlation-ID")
t.Setenv("GO_OMNI_TRUSTED_PROXIES", "127.0.0.1, 10.0.0.0/8")
t.Setenv("GO_OMNI_ENABLE_LOGGER", "true")
t.Setenv("GO_OMNI_LOG_LEVEL", "debug")
t.Setenv("GO_OMNI_LOG_FORMAT", "text")
@@ -44,7 +46,7 @@ func TestLoadFromEnvOverridesSettings(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if cfg.Address != "127.0.0.1:9090" || cfg.RequestIDHeader != "X-Correlation-ID" || !cfg.EnableLogger {
if cfg.Address != "127.0.0.1:9090" || cfg.RequestIDHeader != "X-Correlation-ID" || !cfg.EnableLogger || len(cfg.TrustedProxies) != 2 || cfg.TrustedProxies[1] != "10.0.0.0/8" {
t.Fatalf("unexpected application settings: %+v", cfg)
}
if cfg.ShutdownTimeout != 3*time.Second || cfg.Log.Level != "debug" || cfg.Log.Format != "text" || cfg.Log.Output != "stdout" || cfg.Log.Service != "billing" || cfg.Log.Environment != "test" || !cfg.Log.AddSource {
@@ -65,6 +67,11 @@ func TestLoadFromEnvRejectsInvalidValues(t *testing.T) {
}
t.Setenv("GO_OMNI_SHUTDOWN_TIMEOUT", "1s")
t.Setenv("GO_OMNI_TRUSTED_PROXIES", "proxy.example.com")
if _, err := LoadFromEnv(""); err == nil || !strings.Contains(err.Error(), "trusted_proxies") {
t.Fatalf("expected invalid trusted proxy error, got %v", err)
}
t.Setenv("GO_OMNI_TRUSTED_PROXIES", "")
cfg := Default()
cfg.Address = ""
if err := cfg.Validate(); err == nil || !strings.Contains(err.Error(), "address") {
@@ -80,11 +87,16 @@ func TestLoadFromEnvRejectsInvalidValues(t *testing.T) {
func TestWithConfigAndWithLogConfig(t *testing.T) {
settings := Default()
settings.Log.Service = "orders"
settings.TrustedProxies = []string{"127.0.0.1"}
cfg := Default()
WithConfig(settings)(&cfg)
if cfg.Log.Service != "orders" {
if cfg.Log.Service != "orders" || len(cfg.TrustedProxies) != 1 {
t.Fatalf("complete config was not applied: %+v", cfg)
}
settings.TrustedProxies[0] = "10.0.0.0/8"
if cfg.TrustedProxies[0] != "127.0.0.1" {
t.Fatal("WithConfig should copy trusted proxy settings")
}
WithLogConfig(Default().Log)(&cfg)
if cfg.Log.Service != "" {

View File

@@ -20,6 +20,12 @@ func LoadFromEnv(prefix string) (Config, error) {
cfg := Default()
cfg.Address = envString(prefix+"HTTP_ADDR", cfg.Address)
cfg.RequestIDHeader = envString(prefix+"REQUEST_ID_HEADER", cfg.RequestIDHeader)
if value := strings.TrimSpace(os.Getenv(prefix + "TRUSTED_PROXIES")); value != "" {
cfg.TrustedProxies = strings.Split(value, ",")
for i := range cfg.TrustedProxies {
cfg.TrustedProxies[i] = strings.TrimSpace(cfg.TrustedProxies[i])
}
}
cfg.Log.Level = envString(prefix+"LOG_LEVEL", cfg.Log.Level)
cfg.Log.Format = envString(prefix+"LOG_FORMAT", cfg.Log.Format)
cfg.Log.Output = envString(prefix+"LOG_OUTPUT", cfg.Log.Output)