fix: 安全初始化 Gin 可信代理

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Jlypx
2026-07-19 21:40:32 +08:00
parent 6e2ba0e4cd
commit 39107ca457
2 changed files with 70 additions and 12 deletions
+20 -12
View File
@@ -47,18 +47,7 @@ func ProvideRouter(
r := gin.New()
r.Use(middleware2.Recovery())
if len(cfg.Server.TrustedProxies) > 0 {
if err := r.SetTrustedProxies(cfg.Server.TrustedProxies); err != nil {
log.Printf("Failed to set trusted proxies: %v", err)
}
} else {
if err := r.SetTrustedProxies(nil); err != nil {
log.Printf("Failed to disable trusted proxies: %v", err)
}
if cfg.Server.Mode == "release" {
log.Printf("Warning: server.trusted_proxies is empty in release mode; client IP trust chain is disabled")
}
}
configureTrustedProxies(r, cfg.Server)
// Wire up websearch Manager builder so it initializes on startup and rebuilds on config save.
settingService.SetWebSearchManagerBuilder(context.Background(), func(cfg *service.WebSearchEmulationConfig, proxyURLs map[int64]string) {
@@ -99,6 +88,25 @@ func ProvideRouter(
return SetupRouter(r, handlers, jwtAuth, adminAuth, apiKeyAuth, auditLog, stepUpAuth, apiKeyService, subscriptionService, opsService, settingService, cfg, redisClient)
}
func configureTrustedProxies(r *gin.Engine, cfg config.ServerConfig) {
if cfg.TrustedProxiesConfigured {
if err := r.SetTrustedProxies(cfg.TrustedProxies); err != nil {
log.Printf("Failed to set trusted proxies: %v", err)
_ = r.SetTrustedProxies(nil)
}
if len(cfg.TrustedProxies) == 0 && cfg.Mode == "release" {
log.Printf("Warning: server.trusted_proxies is explicitly empty; forwarded client IP trust is disabled")
}
} else {
if err := r.SetTrustedProxies(nil); err != nil {
log.Printf("Failed to disable trusted proxies: %v", err)
}
if cfg.Mode == "release" {
log.Printf("Warning: server.trusted_proxies is not configured; disabling the forwarded-IP compatibility switch will use direct peer addresses only")
}
}
}
// ProvideHTTPServer 提供 HTTP 服务器
func ProvideHTTPServer(cfg *config.Config, router *gin.Engine) *http.Server {
httpHandler := http.Handler(router)
@@ -55,6 +55,56 @@ func TestProvideHTTPServerEnablesBoundedH2C(t *testing.T) {
require.True(t, srv.Protocols.HTTP1())
}
func TestConfigureTrustedProxies(t *testing.T) {
gin.SetMode(gin.TestMode)
tests := []struct {
name string
cfg config.ServerConfig
want string
}{
{
name: "configured proxy resolves forwarded client",
cfg: config.ServerConfig{
TrustedProxies: []string{"9.9.9.9/32"},
TrustedProxiesConfigured: true,
},
want: "1.2.3.4",
},
{
name: "explicit empty list ignores forwarded client",
cfg: config.ServerConfig{
TrustedProxiesConfigured: true,
},
want: "9.9.9.9",
},
{
name: "invalid proxy list fails closed",
cfg: config.ServerConfig{
TrustedProxies: []string{"not-a-cidr"},
TrustedProxiesConfigured: true,
},
want: "9.9.9.9",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
r := gin.New()
configureTrustedProxies(r, tc.cfg)
r.GET("/t", func(c *gin.Context) { c.String(http.StatusOK, c.ClientIP()) })
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/t", nil)
req.RemoteAddr = "9.9.9.9:12345"
req.Header.Set("X-Forwarded-For", "1.2.3.4")
r.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
require.Equal(t, tc.want, w.Body.String())
})
}
}
func TestHTTPServerRejectsOversizedHTTP1Header(t *testing.T) {
r := gin.New()
r.GET("/", func(c *gin.Context) { c.Status(http.StatusOK) })