feat: 实例级隔离 — salt + 指纹版本可配置
- 新增 gateway.instance_salt: 不同 sub2api 实例对相同输入产生不同 hash 影响 user_id 重写和 session hash,防止跨实例指纹关联 - 新增 gateway.fingerprint_defaults: CLI 版本号/SDK 版本/OS/Arch 可配置 每个实例可设不同值,与其他 sub2api 部署区分 - constants.go + identity_service.go 支持启动时覆盖默认指纹 - wire_gen.go 启动时读取配置并应用覆盖
This commit is contained in:
parent
43506e4f78
commit
73bbbb415c
@ -12,6 +12,7 @@ import (
|
||||
"github.com/Wei-Shaw/sub2api/internal/config"
|
||||
"github.com/Wei-Shaw/sub2api/internal/handler"
|
||||
"github.com/Wei-Shaw/sub2api/internal/handler/admin"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/claude"
|
||||
"github.com/Wei-Shaw/sub2api/internal/repository"
|
||||
"github.com/Wei-Shaw/sub2api/internal/server"
|
||||
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
|
||||
@ -35,6 +36,10 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 应用实例级指纹覆盖(不同 sub2api 实例可设不同的默认版本号)
|
||||
fpd := configConfig.Gateway.FingerprintDefaults
|
||||
claude.ApplyFingerprintOverrides(fpd.ClaudeCLIVersion, fpd.StainlessPackageVersion, fpd.StainlessRuntimeVersion, fpd.StainlessOS, fpd.StainlessArch)
|
||||
service.ApplyDefaultFingerprintOverrides(fpd.ClaudeCLIVersion, fpd.StainlessPackageVersion, fpd.StainlessRuntimeVersion, fpd.StainlessOS, fpd.StainlessArch)
|
||||
client, err := repository.ProvideEnt(configConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -166,7 +171,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
|
||||
return nil, err
|
||||
}
|
||||
billingService := service.NewBillingService(configConfig, pricingService)
|
||||
identityService := service.NewIdentityService(identityCache)
|
||||
identityService := service.NewIdentityServiceWithSalt(identityCache, configConfig.Gateway.InstanceSalt)
|
||||
deferredService := service.ProvideDeferredService(accountRepository, timingWheelService)
|
||||
claudeTokenProvider := service.ProvideClaudeTokenProvider(accountRepository, geminiTokenCache, oAuthService, oauthRefreshAPI)
|
||||
digestSessionStore := service.NewDigestSessionStore()
|
||||
|
||||
@ -461,6 +461,18 @@ type GatewayConfig struct {
|
||||
// 实现天然 JA3/JA4 指纹匹配(无需 uTLS 模拟)
|
||||
NodeTLSProxy NodeTLSProxyConfig `mapstructure:"node_tls_proxy"`
|
||||
|
||||
// InstanceSalt: 实例级隔离盐值
|
||||
// 用于 user_id 重写和 session hash 的种子混淆,
|
||||
// 不同 sub2api 实例设置不同的 salt,确保相同输入产生不同输出。
|
||||
// 为空时使用默认行为(无 salt),建议生产环境必须配置。
|
||||
// 生成方法: openssl rand -hex 32
|
||||
InstanceSalt string `mapstructure:"instance_salt"`
|
||||
|
||||
// FingerprintDefaults: 指纹默认值覆盖
|
||||
// 允许每个实例配置不同的 Claude CLI 版本号,与其他 sub2api 实例区分。
|
||||
// 为空时使用代码内置默认值。
|
||||
FingerprintDefaults FingerprintDefaultsConfig `mapstructure:"fingerprint_defaults"`
|
||||
|
||||
// UsageRecord: 使用量记录异步队列配置(有界队列 + 固定 worker)
|
||||
UsageRecord GatewayUsageRecordConfig `mapstructure:"usage_record"`
|
||||
|
||||
@ -696,6 +708,23 @@ type NodeTLSProxyConfig struct {
|
||||
ProxyHosts []string `mapstructure:"proxy_hosts"`
|
||||
}
|
||||
|
||||
// FingerprintDefaultsConfig 指纹默认值配置
|
||||
// 允许每个 sub2api 实例设置不同的默认指纹值,与其他实例区分。
|
||||
// 所有字段为空时使用代码内置默认值。
|
||||
type FingerprintDefaultsConfig struct {
|
||||
// ClaudeCLIVersion: Claude CLI 版本号(如 "2.1.81"),
|
||||
// 最终 User-Agent 为 "claude-cli/{version} (external, cli)"
|
||||
ClaudeCLIVersion string `mapstructure:"claude_cli_version"`
|
||||
// StainlessPackageVersion: @anthropic-ai/sdk 版本(如 "0.80.0")
|
||||
StainlessPackageVersion string `mapstructure:"stainless_package_version"`
|
||||
// StainlessRuntimeVersion: Node.js 版本(如 "v24.13.0")
|
||||
StainlessRuntimeVersion string `mapstructure:"stainless_runtime_version"`
|
||||
// StainlessOS: 操作系统(如 "Linux", "Darwin")
|
||||
StainlessOS string `mapstructure:"stainless_os"`
|
||||
// StainlessArch: 架构(如 "arm64", "x64")
|
||||
StainlessArch string `mapstructure:"stainless_arch"`
|
||||
}
|
||||
|
||||
// GatewaySchedulingConfig accounts scheduling configuration.
|
||||
type GatewaySchedulingConfig struct {
|
||||
// 粘性会话排队配置
|
||||
|
||||
@ -61,6 +61,30 @@ var DefaultHeaders = map[string]string{
|
||||
"Anthropic-Dangerous-Direct-Browser-Access": "true",
|
||||
}
|
||||
|
||||
// ApplyFingerprintOverrides 用配置覆盖默认指纹值(每个实例可设不同值)
|
||||
// cliVersion: Claude CLI 版本(如 "2.1.81")
|
||||
// pkgVersion: SDK 版本(如 "0.80.0")
|
||||
// runtimeVersion: Node.js 版本(如 "v24.13.0")
|
||||
// os_: 操作系统(如 "Linux")
|
||||
// arch: 架构(如 "arm64")
|
||||
func ApplyFingerprintOverrides(cliVersion, pkgVersion, runtimeVersion, os_, arch string) {
|
||||
if cliVersion != "" {
|
||||
DefaultHeaders["User-Agent"] = "claude-cli/" + cliVersion + " (external, cli)"
|
||||
}
|
||||
if pkgVersion != "" {
|
||||
DefaultHeaders["X-Stainless-Package-Version"] = pkgVersion
|
||||
}
|
||||
if runtimeVersion != "" {
|
||||
DefaultHeaders["X-Stainless-Runtime-Version"] = runtimeVersion
|
||||
}
|
||||
if os_ != "" {
|
||||
DefaultHeaders["X-Stainless-OS"] = os_
|
||||
}
|
||||
if arch != "" {
|
||||
DefaultHeaders["X-Stainless-Arch"] = arch
|
||||
}
|
||||
}
|
||||
|
||||
// Model 表示一个 Claude 模型
|
||||
type Model struct {
|
||||
ID string `json:"id"`
|
||||
|
||||
@ -35,6 +35,25 @@ var defaultFingerprint = Fingerprint{
|
||||
StainlessRuntimeVersion: "v24.13.0",
|
||||
}
|
||||
|
||||
// ApplyDefaultFingerprintOverrides 用配置覆盖 identity_service 的默认指纹
|
||||
func ApplyDefaultFingerprintOverrides(cliVersion, pkgVersion, runtimeVersion, os_, arch string) {
|
||||
if cliVersion != "" {
|
||||
defaultFingerprint.UserAgent = "claude-cli/" + cliVersion + " (external, cli)"
|
||||
}
|
||||
if pkgVersion != "" {
|
||||
defaultFingerprint.StainlessPackageVersion = pkgVersion
|
||||
}
|
||||
if runtimeVersion != "" {
|
||||
defaultFingerprint.StainlessRuntimeVersion = runtimeVersion
|
||||
}
|
||||
if os_ != "" {
|
||||
defaultFingerprint.StainlessOS = os_
|
||||
}
|
||||
if arch != "" {
|
||||
defaultFingerprint.StainlessArch = arch
|
||||
}
|
||||
}
|
||||
|
||||
// Fingerprint represents account fingerprint data
|
||||
type Fingerprint struct {
|
||||
ClientID string
|
||||
@ -64,6 +83,7 @@ type IdentityCache interface {
|
||||
// IdentityService 管理OAuth账号的请求身份指纹
|
||||
type IdentityService struct {
|
||||
cache IdentityCache
|
||||
instanceSalt string // 实例级隔离盐值,不同 sub2api 实例产生不同的 hash 输出
|
||||
}
|
||||
|
||||
// NewIdentityService 创建新的IdentityService
|
||||
@ -71,6 +91,11 @@ func NewIdentityService(cache IdentityCache) *IdentityService {
|
||||
return &IdentityService{cache: cache}
|
||||
}
|
||||
|
||||
// NewIdentityServiceWithSalt 创建带实例盐值的 IdentityService
|
||||
func NewIdentityServiceWithSalt(cache IdentityCache, salt string) *IdentityService {
|
||||
return &IdentityService{cache: cache, instanceSalt: salt}
|
||||
}
|
||||
|
||||
// GetOrCreateFingerprint 获取或创建账号的指纹
|
||||
// 如果缓存存在,检测user-agent版本,新版本则更新
|
||||
// 如果缓存不存在,生成随机ClientID并从请求头创建指纹,然后缓存
|
||||
@ -241,8 +266,9 @@ func (s *IdentityService) RewriteUserID(body []byte, accountID int64, accountUUI
|
||||
|
||||
sessionTail := parsed.SessionID // 原始session UUID
|
||||
|
||||
// 生成新的session hash: SHA256(accountID::sessionTail) -> UUID格式
|
||||
seed := fmt.Sprintf("%d::%s", accountID, sessionTail)
|
||||
// 生成新的session hash: SHA256(salt::accountID::sessionTail) -> UUID格式
|
||||
// instanceSalt 使不同 sub2api 实例对相同输入产生不同的 hash
|
||||
seed := fmt.Sprintf("%s::%d::%s", s.instanceSalt, accountID, sessionTail)
|
||||
newSessionHash := generateUUIDFromSeed(seed)
|
||||
|
||||
// 根据客户端版本选择输出格式
|
||||
|
||||
@ -397,6 +397,24 @@ gateway:
|
||||
# Upstream target host / 上游目标主机
|
||||
upstream_host: "api.anthropic.com"
|
||||
|
||||
# Instance isolation salt / 实例隔离盐值
|
||||
# IMPORTANT: Each sub2api deployment MUST set a unique salt to prevent
|
||||
# cross-instance fingerprint correlation. Generate: openssl rand -hex 32
|
||||
# 重要:每个 sub2api 实例必须设置唯一的 salt,防止不同实例之间的指纹关联。
|
||||
# 生成方法: openssl rand -hex 32
|
||||
instance_salt: ""
|
||||
|
||||
# Fingerprint defaults override / 指纹默认值覆盖
|
||||
# Each instance can set different version numbers to differentiate from
|
||||
# other sub2api deployments. Empty values use built-in defaults.
|
||||
# 每个实例可设置不同的版本号,与其他 sub2api 部署区分。空值使用内置默认值。
|
||||
fingerprint_defaults:
|
||||
# claude_cli_version: "2.1.81"
|
||||
# stainless_package_version: "0.80.0"
|
||||
# stainless_runtime_version: "v24.13.0"
|
||||
# stainless_os: "Linux" # Linux / Darwin
|
||||
# stainless_arch: "arm64" # arm64 / x64
|
||||
|
||||
# =============================================================================
|
||||
# Logging Configuration
|
||||
# 日志配置
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user