diff --git a/cmd/s3-balance/main.go b/cmd/s3-balance/main.go index 6c34bfa..5d811e6 100644 --- a/cmd/s3-balance/main.go +++ b/cmd/s3-balance/main.go @@ -92,6 +92,7 @@ func main() { cfg.S3API.ProxyMode, cfg.S3API.AuthRequired, cfg.S3API.VirtualHost, + cfg.S3API.Host, ) // 注册配置热更新回调 diff --git a/config/config.example.yaml b/config/config.example.yaml index 72f8dda..0b79d91 100644 --- a/config/config.example.yaml +++ b/config/config.example.yaml @@ -138,3 +138,9 @@ s3api: # 是否需要认证(开启后使用 Basic Auth,凭据来自 access_key/secret_key) auth_required: true + + # 用于签名验证的Host(可选) + # 当服务前有 nginx 等反向代理时,可以设置此项为客户端实际访问的域名 + # 留空则使用请求中的 Host 头 + # 示例: "s3.example.com" 或 "s3.example.com:8080" + host: "" diff --git a/deploy/docker/config.docker.yaml b/deploy/docker/config.docker.yaml index 6bf1fe5..91e4953 100644 --- a/deploy/docker/config.docker.yaml +++ b/deploy/docker/config.docker.yaml @@ -56,4 +56,5 @@ s3api: secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" virtual_host: false proxy_mode: true - auth_required: true \ No newline at end of file + auth_required: true + host: "" \ No newline at end of file diff --git a/internal/api/s3_handler.go b/internal/api/s3_handler.go index c9cc7d9..32b2e10 100644 --- a/internal/api/s3_handler.go +++ b/internal/api/s3_handler.go @@ -29,6 +29,7 @@ type handlerSettings struct { proxyMode bool authRequired bool virtualHost bool + signatureHost string } // NewS3Handler 创建新的S3兼容API处理器 @@ -43,6 +44,7 @@ func NewS3Handler( proxyMode bool, authRequired bool, virtualHost bool, + signatureHost string, ) *S3Handler { handler := &S3Handler{ @@ -52,17 +54,18 @@ func NewS3Handler( storage: storage, metrics: metrics, } - handler.initSettings(accessKey, secretKey, proxyMode, authRequired, virtualHost) + handler.initSettings(accessKey, secretKey, proxyMode, authRequired, virtualHost, signatureHost) return handler } -func (h *S3Handler) initSettings(accessKey, secretKey string, proxyMode, authRequired, virtualHost bool) { +func (h *S3Handler) initSettings(accessKey, secretKey string, proxyMode, authRequired, virtualHost bool, signatureHost string) { h.settings.Store(handlerSettings{ - accessKey: accessKey, - secretKey: secretKey, - proxyMode: proxyMode, - authRequired: authRequired, - virtualHost: virtualHost, + accessKey: accessKey, + secretKey: secretKey, + proxyMode: proxyMode, + authRequired: authRequired, + virtualHost: virtualHost, + signatureHost: signatureHost, }) } @@ -99,10 +102,12 @@ func (h *S3Handler) RegisterS3Routes(router *mux.Router) { }, })) protected.Use(middleware.S3Signature(middleware.S3SignatureConfig{ - Required: h.authRequired, - Credentials: h.credentials, - OnError: h.sendS3Error, + Required: h.authRequired, + Credentials: h.credentials, + OnError: h.sendS3Error, + SignatureHost: h.signatureHost, })) + protected.Use(h.accessLogMiddleware) } func (h *S3Handler) loadSettings() handlerSettings { @@ -129,15 +134,20 @@ func (h *S3Handler) proxyModeEnabled() bool { return h.loadSettings().proxyMode } +func (h *S3Handler) signatureHost() string { + return h.loadSettings().signatureHost +} + func (h *S3Handler) UpdateS3APIConfig(cfg *config.S3APIConfig) { if cfg == nil { return } h.settings.Store(handlerSettings{ - accessKey: cfg.AccessKey, - secretKey: cfg.SecretKey, - proxyMode: cfg.ProxyMode, - authRequired: cfg.AuthRequired, - virtualHost: cfg.VirtualHost, + accessKey: cfg.AccessKey, + secretKey: cfg.SecretKey, + proxyMode: cfg.ProxyMode, + authRequired: cfg.AuthRequired, + virtualHost: cfg.VirtualHost, + signatureHost: cfg.Host, }) } diff --git a/internal/config/config.go b/internal/config/config.go index 2bb95b3..8224518 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -65,6 +65,7 @@ type S3APIConfig struct { VirtualHost bool `yaml:"virtual_host"` // 是否使用虚拟主机模式 ProxyMode bool `yaml:"proxy_mode"` // 是否使用代理模式(而非重定向) AuthRequired bool `yaml:"auth_required"` // 是否需要认证 + Host string `yaml:"host"` // 用于签名验证的Host(为空则使用请求的Host) } // DatabaseConfig 数据库配置 diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index efd5643..e4f9e24 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -10,9 +10,10 @@ import ( // S3SignatureConfig controls S3 signature validation. type S3SignatureConfig struct { - Required func() bool - Credentials func() (string, string) - OnError func(http.ResponseWriter, string, string, string) + Required func() bool + Credentials func() (string, string) + OnError func(http.ResponseWriter, string, string, string) + SignatureHost func() string // 用于签名验证的Host(为空则使用请求的Host) } // credentialsProvider implements s3validate.CredentialsProvider interface. @@ -47,6 +48,13 @@ func S3Signature(cfg S3SignatureConfig) func(http.Handler) http.Handler { return } + // 如果配置了签名验证的Host,覆盖请求的Host + if cfg.SignatureHost != nil { + if signatureHost := cfg.SignatureHost(); signatureHost != "" { + r.Host = signatureHost + } + } + result, err := verifier.Verify(r.Context(), r) if err != nil { invokeOnError(w, cfg, "SignatureDoesNotMatch", err.Error())