0.10.1: 安全与业务逻辑加固、新品牌与部署加固

三轮审查修复(60+ 项),相对远端 main(b536672)的关键变更:
- 安全: 数据面 SSRF 拨号防护(防 DNS rebinding)/上游凭据剥离/登录防枚举
  与锁定态统一/可信代理(X-Forwarded-For)限流加固/会话版本失效机制/
  撤销即时传播/弱密钥拒绝启动/脱敏字节级重写(保签名契约)
- 业务逻辑: 裸 body 上传 panic/bootstrap 审计管线卡死/定价通配符优先级/
  全局工具可见性/调度器停机补跑/TOTP 挑战令牌消费顺序/熔断探针语义/
  >4MB 响应 token 计量/管理员重置密码作废会话 等
- 前端: 新 logo(语枢 AI 网关主题)/Provider 凭据异常警示/删除入口/
  后端错误消息透传/localStorage 敏感数据收敛
- 部署: CREDENTIAL_MASTER_KEY 持久化与弱值拒绝/Provider DELETE 接口/
  nginx 安全头/worker 内存限制
- 新增迁移 000029(key_hash 索引)/000030(usage_daily 币种维度)
This commit is contained in:
2026-08-13 10:50:51 +08:00
parent b536672000
commit 9501751792
136 changed files with 8024 additions and 1476 deletions
+37 -2
View File
@@ -10,6 +10,7 @@ import (
"syscall"
"time"
"aigateway.local/core/internal/agentnode"
"aigateway.local/core/internal/apikey"
"aigateway.local/core/internal/audit"
"aigateway.local/core/internal/contentpolicy"
@@ -23,15 +24,17 @@ import (
"aigateway.local/core/internal/platform/cryptox"
"aigateway.local/core/internal/platform/database"
"aigateway.local/core/internal/platform/health"
"aigateway.local/core/internal/platform/storage"
"aigateway.local/core/internal/platform/httpserver"
"aigateway.local/core/internal/platform/storage"
"aigateway.local/core/internal/portal"
"aigateway.local/core/internal/pricing"
"aigateway.local/core/internal/provider"
providercontrolplane "aigateway.local/core/internal/provider/controlplane"
provideropenai "aigateway.local/core/internal/provider/openai"
providerruntime "aigateway.local/core/internal/provider/runtime"
"aigateway.local/core/internal/scheduler"
"aigateway.local/core/internal/shadow"
"aigateway.local/core/internal/trace"
"aigateway.local/core/internal/workbench"
)
@@ -129,6 +132,7 @@ func main() {
apiKeyAuthenticator := apikey.NewAuthenticator(apiKeyRepository, criticalRedis, bootstrapAPIKey)
apiKeyAuthenticator.SetLogger(logger)
proxy := gateway.NewDynamicProxy(providerResolver, apiKeyAuthenticator, cfg.Server.MaxBodyBytes, logger)
proxy.SetAllowPrivateProviderURLs(cfg.Credentials.AllowPrivateProviderURL)
proxy.SetAdmissionController(gateway.NewRedisAdmissionController(criticalRedis))
proxy.SetTokenQuotaController(gateway.NewRedisTokenQuotaController(criticalRedis))
proxy.SetResiliencePolicy(gateway.ResiliencePolicy{
@@ -160,7 +164,7 @@ func main() {
proxy.SetPricingService(pricingService)
identityRepository := identity.NewRepository(db)
sessionStore := identity.NewSessionStore(criticalRedis, cfg.Auth.SessionTTL)
loginLimiter := identity.NewLoginLimiter(criticalRedis, cfg.Auth.LoginRateLimitMax, cfg.Auth.LoginRateLimitWindow)
loginLimiter := identity.NewLoginLimiter(criticalRedis, cfg.Auth.LoginRateLimitMax, cfg.Auth.LoginRateLimitWindow, cfg.Auth.TrustedProxies)
totpCipher, err := cryptox.NewKeyring(
cfg.Credentials.MasterKey, cfg.Credentials.KEKVersion, cfg.Credentials.KEKKeyring, "totp-secret",
)
@@ -268,6 +272,24 @@ func main() {
fileService := workbench.NewFileService(workbenchService, objectStore)
filesAdminHandler := workbench.NewFilesAdminHTTPHandler(fileService, identityService)
filesPortalHandler := workbench.NewFilesPortalHTTPHandler(fileService, identityService)
// M8 P4:站内消息。未读数以 PostgreSQL 为权威源,inbox service 仅用 Redis PUBLISH
// 提醒订阅方;通知 worker 在同一消费循环内物化事件(见 gateway-notification-worker)。
inboxService := workbench.NewInboxService(workbenchService, criticalRedis, cfg.Inbox.Channel)
inboxAdminHandler := workbench.NewInboxAdminHTTPHandler(inboxService, identityService)
inboxPortalHandler := workbench.NewInboxPortalHTTPHandler(inboxService, identityService)
schedulerCipher, err := cryptox.NewKeyring(
cfg.Credentials.MasterKey, cfg.Credentials.KEKVersion, cfg.Credentials.KEKKeyring, "scheduled-task-api-key",
)
if err != nil {
logger.Error("scheduled task encryption initialization failed", "error", err)
os.Exit(1)
}
schedulerService := scheduler.NewService(db, schedulerCipher)
schedulerHandler := scheduler.NewAdminHTTPHandler(schedulerService, identityService)
traceStore := trace.NewStore(db)
traceHandler := trace.NewAdminHTTPHandler(traceStore, identityService)
agentNodeStore := agentnode.NewStore(db)
agentNodeHandler := agentnode.NewHTTPHandler(agentNodeStore, identityService)
applicationKeyCipher, err := cryptox.NewKeyring(
cfg.Credentials.MasterKey, cfg.Credentials.KEKVersion, cfg.Credentials.KEKKeyring, "application-runtime-key",
)
@@ -285,6 +307,7 @@ func main() {
MCPClient: mcpClient,
})
workbenchRuntime.SetLogger(logger)
workbenchRuntime.SetTraceStore(traceStore)
// Wire the fact-check engine: the admin fact-check settings/policies UI now
// actually governs application answers instead of being inert configuration.
factCheckEngine := factcheck.NewEngine(db, workbench.NewFactCheckRetriever(workbench.NewRetriever(workbenchService, workbenchService.Embedder())), logger)
@@ -348,6 +371,18 @@ func main() {
controlMux.Handle("/api/v1/admin/files/", filesAdminHandler)
controlMux.Handle("/api/v1/portal/files", filesPortalHandler)
controlMux.Handle("/api/v1/portal/files/", filesPortalHandler)
controlMux.Handle("/api/v1/admin/inbox", inboxAdminHandler)
controlMux.Handle("/api/v1/admin/inbox/", inboxAdminHandler)
controlMux.Handle("/api/v1/portal/inbox", inboxPortalHandler)
controlMux.Handle("/api/v1/portal/inbox/", inboxPortalHandler)
controlMux.Handle("/api/v1/admin/scheduled-tasks", schedulerHandler)
controlMux.Handle("/api/v1/admin/scheduled-tasks/", schedulerHandler)
controlMux.Handle("/api/v1/admin/traces", traceHandler)
controlMux.Handle("/api/v1/admin/traces/", traceHandler)
controlMux.Handle("/api/v1/admin/agent-sessions", traceHandler)
controlMux.Handle("/api/v1/admin/agent-nodes", agentNodeHandler)
controlMux.Handle("/api/v1/admin/agent-nodes/", agentNodeHandler)
controlMux.Handle("/api/v1/agent/nodes/", agentNodeHandler)
controlMux.Handle("/api/v1/admin/reload", operationsHandler)
controlMux.Handle("/api/v1/admin/identities/", identityManagementHandler)
controlMux.Handle("/api/v1/admin/departments", identityManagementHandler)