From 1d2445ff52f00badf2f0dd90668b31b21b6dccf7 Mon Sep 17 00:00:00 2001 From: lyen1688 Date: Wed, 20 May 2026 23:51:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20API=20Key=20ACL=20?= =?UTF-8?q?=E5=BC=80=E5=85=B3=E7=9A=84=20CI=20=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/config/config.go | 26 +++++++++++++++++-- backend/internal/server/api_contract_test.go | 2 ++ .../server/middleware/api_key_auth.go | 2 +- .../server/middleware/api_key_auth_test.go | 3 +-- backend/internal/service/setting_service.go | 8 +++--- .../service/setting_service_update_test.go | 2 +- 6 files changed, 32 insertions(+), 11 deletions(-) diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 7210b258..5048e791 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -580,7 +580,29 @@ type SecurityConfig struct { ProxyFallback ProxyFallbackConfig `mapstructure:"proxy_fallback"` ProxyProbe ProxyProbeConfig `mapstructure:"proxy_probe"` TrustForwardedIPForAPIKeyACL bool `mapstructure:"trust_forwarded_ip_for_api_key_acl"` - TrustForwardedIPForAPIKeyACLLive atomic.Bool `mapstructure:"-"` + trustForwardedIPForAPIKeyACLLive *atomic.Bool `mapstructure:"-"` +} + +func (c *Config) TrustForwardedIPForAPIKeyACL() bool { + if c == nil { + return false + } + live := c.Security.trustForwardedIPForAPIKeyACLLive + if live == nil { + return c.Security.TrustForwardedIPForAPIKeyACL + } + return live.Load() +} + +func (c *Config) SetTrustForwardedIPForAPIKeyACL(enabled bool) { + if c == nil { + return + } + c.Security.TrustForwardedIPForAPIKeyACL = enabled + if c.Security.trustForwardedIPForAPIKeyACLLive == nil { + c.Security.trustForwardedIPForAPIKeyACLLive = &atomic.Bool{} + } + c.Security.trustForwardedIPForAPIKeyACLLive.Store(enabled) } type URLAllowlistConfig struct { @@ -1366,7 +1388,7 @@ func load(allowMissingJWTSecret bool) (*Config, error) { cfg.Security.ResponseHeaders.AdditionalAllowed = normalizeStringSlice(cfg.Security.ResponseHeaders.AdditionalAllowed) cfg.Security.ResponseHeaders.ForceRemove = normalizeStringSlice(cfg.Security.ResponseHeaders.ForceRemove) cfg.Security.CSP.Policy = strings.TrimSpace(cfg.Security.CSP.Policy) - cfg.Security.TrustForwardedIPForAPIKeyACLLive.Store(cfg.Security.TrustForwardedIPForAPIKeyACL) + cfg.SetTrustForwardedIPForAPIKeyACL(cfg.Security.TrustForwardedIPForAPIKeyACL) cfg.Log.Level = strings.ToLower(strings.TrimSpace(cfg.Log.Level)) cfg.Log.Format = strings.ToLower(strings.TrimSpace(cfg.Log.Format)) cfg.Log.ServiceName = strings.TrimSpace(cfg.Log.ServiceName) diff --git a/backend/internal/server/api_contract_test.go b/backend/internal/server/api_contract_test.go index 10404276..eaf3bcfb 100644 --- a/backend/internal/server/api_contract_test.go +++ b/backend/internal/server/api_contract_test.go @@ -757,6 +757,7 @@ func TestAPIContracts(t *testing.T) { "site_logo": "", "site_subtitle": "Subtitle", "api_base_url": "https://api.example.com", + "api_key_acl_trust_forwarded_ip": false, "contact_info": "support", "doc_url": "https://docs.example.com", "auth_source_default_email_balance": 0, @@ -1014,6 +1015,7 @@ func TestAPIContracts(t *testing.T) { "site_logo": "", "site_subtitle": "Subscription to API Conversion Platform", "api_base_url": "", + "api_key_acl_trust_forwarded_ip": false, "contact_info": "", "doc_url": "", "home_content": "", diff --git a/backend/internal/server/middleware/api_key_auth.go b/backend/internal/server/middleware/api_key_auth.go index f6ec53ab..7b9a1ee0 100644 --- a/backend/internal/server/middleware/api_key_auth.go +++ b/backend/internal/server/middleware/api_key_auth.go @@ -90,7 +90,7 @@ func apiKeyAuthWithSubscription(apiKeyService *service.APIKeyService, subscripti // 注意:错误信息故意模糊,避免暴露具体的 IP 限制机制 if len(apiKey.IPWhitelist) > 0 || len(apiKey.IPBlacklist) > 0 { clientIP := ip.GetTrustedClientIP(c) - if cfg != nil && cfg.Security.TrustForwardedIPForAPIKeyACLLive.Load() { + if cfg.TrustForwardedIPForAPIKeyACL() { clientIP = ip.GetClientIP(c) } allowed, _ := ip.CheckIPRestrictionWithCompiledRules(clientIP, apiKey.CompiledIPWhitelist, apiKey.CompiledIPBlacklist) diff --git a/backend/internal/server/middleware/api_key_auth_test.go b/backend/internal/server/middleware/api_key_auth_test.go index 19d1919b..57e69f10 100644 --- a/backend/internal/server/middleware/api_key_auth_test.go +++ b/backend/internal/server/middleware/api_key_auth_test.go @@ -490,8 +490,7 @@ func TestAPIKeyAuthIPRestrictionCanTrustForwardedClientIPForReverseProxy(t *test } cfg := &config.Config{RunMode: config.RunModeSimple} - cfg.Security.TrustForwardedIPForAPIKeyACL = true - cfg.Security.TrustForwardedIPForAPIKeyACLLive.Store(true) + cfg.SetTrustForwardedIPForAPIKeyACL(true) apiKeyService := service.NewAPIKeyService(apiKeyRepo, nil, nil, nil, nil, nil, cfg) router := gin.New() require.NoError(t, router.SetTrustedProxies(nil)) diff --git a/backend/internal/service/setting_service.go b/backend/internal/service/setting_service.go index acdb2804..5616c353 100644 --- a/backend/internal/service/setting_service.go +++ b/backend/internal/service/setting_service.go @@ -604,14 +604,13 @@ func (s *SettingService) LoadAPIKeyACLTrustForwardedIPSetting(ctx context.Contex value, err := s.settingRepo.GetValue(ctx, SettingKeyAPIKeyACLTrustForwardedIP) if err != nil { if errors.Is(err, ErrSettingNotFound) { - s.cfg.Security.TrustForwardedIPForAPIKeyACLLive.Store(s.cfg.Security.TrustForwardedIPForAPIKeyACL) + s.cfg.SetTrustForwardedIPForAPIKeyACL(s.cfg.Security.TrustForwardedIPForAPIKeyACL) return nil } return fmt.Errorf("get api key acl forwarded ip setting: %w", err) } enabled := value == "true" - s.cfg.Security.TrustForwardedIPForAPIKeyACL = enabled - s.cfg.Security.TrustForwardedIPForAPIKeyACLLive.Store(enabled) + s.cfg.SetTrustForwardedIPForAPIKeyACL(enabled) return nil } @@ -1888,8 +1887,7 @@ func (s *SettingService) refreshCachedSettings(settings *SystemSettings) { expiresAt: time.Now().Add(openAIAdvancedSchedulerSettingCacheTTL).UnixNano(), }) if s.cfg != nil { - s.cfg.Security.TrustForwardedIPForAPIKeyACL = settings.APIKeyACLTrustForwardedIP - s.cfg.Security.TrustForwardedIPForAPIKeyACLLive.Store(settings.APIKeyACLTrustForwardedIP) + s.cfg.SetTrustForwardedIPForAPIKeyACL(settings.APIKeyACLTrustForwardedIP) } if s.onUpdate != nil { s.onUpdate() // Invalidate cache after settings update diff --git a/backend/internal/service/setting_service_update_test.go b/backend/internal/service/setting_service_update_test.go index 863edb7e..07add066 100644 --- a/backend/internal/service/setting_service_update_test.go +++ b/backend/internal/service/setting_service_update_test.go @@ -301,7 +301,7 @@ func TestSettingService_UpdateSettings_APIKeyACLTrustForwardedIPRefreshesConfig( require.NoError(t, err) require.Equal(t, "true", repo.updates[SettingKeyAPIKeyACLTrustForwardedIP]) require.True(t, cfg.Security.TrustForwardedIPForAPIKeyACL) - require.True(t, cfg.Security.TrustForwardedIPForAPIKeyACLLive.Load()) + require.True(t, cfg.TrustForwardedIPForAPIKeyACL()) } func TestSettingService_ParseSettings_APIKeyACLTrustForwardedIPFallsBackToConfigWhenMissing(t *testing.T) {