Support custom host

This commit is contained in:
DullJZ
2025-10-04 15:19:54 +08:00
parent 34a905defd
commit 4b44dc1a1b
6 changed files with 46 additions and 19 deletions
+1
View File
@@ -92,6 +92,7 @@ func main() {
cfg.S3API.ProxyMode, cfg.S3API.ProxyMode,
cfg.S3API.AuthRequired, cfg.S3API.AuthRequired,
cfg.S3API.VirtualHost, cfg.S3API.VirtualHost,
cfg.S3API.Host,
) )
// 注册配置热更新回调 // 注册配置热更新回调
+6
View File
@@ -138,3 +138,9 @@ s3api:
# 是否需要认证(开启后使用 Basic Auth,凭据来自 access_key/secret_key # 是否需要认证(开启后使用 Basic Auth,凭据来自 access_key/secret_key
auth_required: true auth_required: true
# 用于签名验证的Host(可选)
# 当服务前有 nginx 等反向代理时,可以设置此项为客户端实际访问的域名
# 留空则使用请求中的 Host 头
# 示例: "s3.example.com" 或 "s3.example.com:8080"
host: ""
+2 -1
View File
@@ -56,4 +56,5 @@ s3api:
secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
virtual_host: false virtual_host: false
proxy_mode: true proxy_mode: true
auth_required: true auth_required: true
host: ""
+25 -15
View File
@@ -29,6 +29,7 @@ type handlerSettings struct {
proxyMode bool proxyMode bool
authRequired bool authRequired bool
virtualHost bool virtualHost bool
signatureHost string
} }
// NewS3Handler 创建新的S3兼容API处理器 // NewS3Handler 创建新的S3兼容API处理器
@@ -43,6 +44,7 @@ func NewS3Handler(
proxyMode bool, proxyMode bool,
authRequired bool, authRequired bool,
virtualHost bool, virtualHost bool,
signatureHost string,
) *S3Handler { ) *S3Handler {
handler := &S3Handler{ handler := &S3Handler{
@@ -52,17 +54,18 @@ func NewS3Handler(
storage: storage, storage: storage,
metrics: metrics, metrics: metrics,
} }
handler.initSettings(accessKey, secretKey, proxyMode, authRequired, virtualHost) handler.initSettings(accessKey, secretKey, proxyMode, authRequired, virtualHost, signatureHost)
return handler 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{ h.settings.Store(handlerSettings{
accessKey: accessKey, accessKey: accessKey,
secretKey: secretKey, secretKey: secretKey,
proxyMode: proxyMode, proxyMode: proxyMode,
authRequired: authRequired, authRequired: authRequired,
virtualHost: virtualHost, virtualHost: virtualHost,
signatureHost: signatureHost,
}) })
} }
@@ -99,10 +102,12 @@ func (h *S3Handler) RegisterS3Routes(router *mux.Router) {
}, },
})) }))
protected.Use(middleware.S3Signature(middleware.S3SignatureConfig{ protected.Use(middleware.S3Signature(middleware.S3SignatureConfig{
Required: h.authRequired, Required: h.authRequired,
Credentials: h.credentials, Credentials: h.credentials,
OnError: h.sendS3Error, OnError: h.sendS3Error,
SignatureHost: h.signatureHost,
})) }))
protected.Use(h.accessLogMiddleware)
} }
func (h *S3Handler) loadSettings() handlerSettings { func (h *S3Handler) loadSettings() handlerSettings {
@@ -129,15 +134,20 @@ func (h *S3Handler) proxyModeEnabled() bool {
return h.loadSettings().proxyMode return h.loadSettings().proxyMode
} }
func (h *S3Handler) signatureHost() string {
return h.loadSettings().signatureHost
}
func (h *S3Handler) UpdateS3APIConfig(cfg *config.S3APIConfig) { func (h *S3Handler) UpdateS3APIConfig(cfg *config.S3APIConfig) {
if cfg == nil { if cfg == nil {
return return
} }
h.settings.Store(handlerSettings{ h.settings.Store(handlerSettings{
accessKey: cfg.AccessKey, accessKey: cfg.AccessKey,
secretKey: cfg.SecretKey, secretKey: cfg.SecretKey,
proxyMode: cfg.ProxyMode, proxyMode: cfg.ProxyMode,
authRequired: cfg.AuthRequired, authRequired: cfg.AuthRequired,
virtualHost: cfg.VirtualHost, virtualHost: cfg.VirtualHost,
signatureHost: cfg.Host,
}) })
} }
+1
View File
@@ -65,6 +65,7 @@ type S3APIConfig struct {
VirtualHost bool `yaml:"virtual_host"` // 是否使用虚拟主机模式 VirtualHost bool `yaml:"virtual_host"` // 是否使用虚拟主机模式
ProxyMode bool `yaml:"proxy_mode"` // 是否使用代理模式(而非重定向) ProxyMode bool `yaml:"proxy_mode"` // 是否使用代理模式(而非重定向)
AuthRequired bool `yaml:"auth_required"` // 是否需要认证 AuthRequired bool `yaml:"auth_required"` // 是否需要认证
Host string `yaml:"host"` // 用于签名验证的Host(为空则使用请求的Host)
} }
// DatabaseConfig 数据库配置 // DatabaseConfig 数据库配置
+11 -3
View File
@@ -10,9 +10,10 @@ import (
// S3SignatureConfig controls S3 signature validation. // S3SignatureConfig controls S3 signature validation.
type S3SignatureConfig struct { type S3SignatureConfig struct {
Required func() bool Required func() bool
Credentials func() (string, string) Credentials func() (string, string)
OnError func(http.ResponseWriter, string, string, string) OnError func(http.ResponseWriter, string, string, string)
SignatureHost func() string // 用于签名验证的Host(为空则使用请求的Host)
} }
// credentialsProvider implements s3validate.CredentialsProvider interface. // credentialsProvider implements s3validate.CredentialsProvider interface.
@@ -47,6 +48,13 @@ func S3Signature(cfg S3SignatureConfig) func(http.Handler) http.Handler {
return return
} }
// 如果配置了签名验证的Host,覆盖请求的Host
if cfg.SignatureHost != nil {
if signatureHost := cfg.SignatureHost(); signatureHost != "" {
r.Host = signatureHost
}
}
result, err := verifier.Verify(r.Context(), r) result, err := verifier.Verify(r.Context(), r)
if err != nil { if err != nil {
invokeOnError(w, cfg, "SignatureDoesNotMatch", err.Error()) invokeOnError(w, cfg, "SignatureDoesNotMatch", err.Error())