diff --git a/cmd/s3-balance/main.go b/cmd/s3-balance/main.go index 7018974..6c34bfa 100644 --- a/cmd/s3-balance/main.go +++ b/cmd/s3-balance/main.go @@ -108,6 +108,9 @@ func main() { log.Printf("Failed to update load balancer strategy: %v", err) } + // 更新S3 API设置 + s3Handler.UpdateS3APIConfig(&newConfig.S3API) + log.Println("Components updated successfully") }) @@ -254,7 +257,7 @@ func startSessionCleaner(ctx context.Context, storageService *storage.Service) { } // cleanupS3MultipartUploads 清理S3存储桶中过期的分片上传 -func cleanupS3MultipartUploads(ctx context.Context, storageService *storage.Service) { +func cleanupS3MultipartUploads(_ context.Context, storageService *storage.Service) { // 获取所有过期的会话 sessions, err := storageService.GetPendingUploadSessions("", "", "", 0) if err != nil { diff --git a/go.mod b/go.mod index 585c66b..72f743b 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.31.1 github.com/aws/aws-sdk-go-v2/credentials v1.18.5 github.com/aws/aws-sdk-go-v2/service/s3 v1.87.0 + github.com/aws/smithy-go v1.22.5 github.com/fsnotify/fsnotify v1.9.0 github.com/gorilla/mux v1.8.1 github.com/prometheus/client_golang v1.23.2 @@ -33,7 +34,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.28.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.1 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.37.1 // indirect - github.com/aws/smithy-go v1.22.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect diff --git a/internal/api/object_handler.go b/internal/api/object_handler.go index df51325..9b8a2ff 100644 --- a/internal/api/object_handler.go +++ b/internal/api/object_handler.go @@ -84,7 +84,7 @@ func (h *S3Handler) handleGetObject(w http.ResponseWriter, r *http.Request, buck } // 根据配置决定使用代理模式还是重定向模式 - if h.proxyMode { + if h.proxyModeEnabled() { // 代理模式:流式传输内容给客户端 resp, err := http.Get(downloadInfo.URL) if err != nil { diff --git a/internal/api/s3_handler.go b/internal/api/s3_handler.go index 7859d52..3b913c1 100644 --- a/internal/api/s3_handler.go +++ b/internal/api/s3_handler.go @@ -1,8 +1,11 @@ package api import ( + "sync/atomic" + "github.com/DullJZ/s3-balance/internal/balancer" "github.com/DullJZ/s3-balance/internal/bucket" + "github.com/DullJZ/s3-balance/internal/config" "github.com/DullJZ/s3-balance/internal/metrics" "github.com/DullJZ/s3-balance/internal/middleware" "github.com/DullJZ/s3-balance/internal/storage" @@ -16,12 +19,16 @@ type S3Handler struct { balancer *balancer.Balancer presigner *presigner.Presigner storage *storage.Service - accessKey string - secretKey string metrics *metrics.Metrics - proxyMode bool - authRequired bool - virtualHost bool + settings atomic.Value +} + +type handlerSettings struct { + accessKey string + secretKey string + proxyMode bool + authRequired bool + virtualHost bool } // NewS3Handler 创建新的S3兼容API处理器 @@ -36,19 +43,27 @@ func NewS3Handler( proxyMode bool, authRequired bool, virtualHost bool, + ) *S3Handler { - return &S3Handler{ + handler := &S3Handler{ bucketManager: bucketManager, balancer: balancer, presigner: presigner, storage: storage, - accessKey: accessKey, - secretKey: secretKey, metrics: metrics, - proxyMode: proxyMode, - authRequired: authRequired, - virtualHost: virtualHost, } + handler.initSettings(accessKey, secretKey, proxyMode, authRequired, virtualHost) + return handler +} + +func (h *S3Handler) initSettings(accessKey, secretKey string, proxyMode, authRequired, virtualHost bool) { + h.settings.Store(handlerSettings{ + accessKey: accessKey, + secretKey: secretKey, + proxyMode: proxyMode, + authRequired: authRequired, + virtualHost: virtualHost, + }) } // RegisterS3Routes 注册S3兼容的路由 @@ -78,16 +93,52 @@ func (h *S3Handler) RegisterS3Routes(router *mux.Router) { // 添加中间件 router.Use(middleware.VirtualHost(middleware.VirtualHostConfig{ - Enabled: h.virtualHost, + Enabled: h.virtualHostEnabled, BucketExists: func(name string) bool { _, ok := h.bucketManager.GetBucket(name) return ok }, })) router.Use(middleware.BasicAuth(middleware.AuthConfig{ - Required: h.authRequired, - AccessKey: h.accessKey, - SecretKey: h.secretKey, - OnError: h.sendS3Error, + Required: h.authRequired, + Credentials: h.credentials, + OnError: h.sendS3Error, })) } + +func (h *S3Handler) loadSettings() handlerSettings { + if v := h.settings.Load(); v != nil { + return v.(handlerSettings) + } + return handlerSettings{} +} + +func (h *S3Handler) virtualHostEnabled() bool { + return h.loadSettings().virtualHost +} + +func (h *S3Handler) authRequired() bool { + return h.loadSettings().authRequired +} + +func (h *S3Handler) credentials() (string, string) { + s := h.loadSettings() + return s.accessKey, s.secretKey +} + +func (h *S3Handler) proxyModeEnabled() bool { + return h.loadSettings().proxyMode +} + +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, + }) +} diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index 94c68ff..ff4e8ea 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -8,17 +8,20 @@ import ( // AuthConfig controls Basic Auth validation. type AuthConfig struct { - Required bool - AccessKey string - SecretKey string - OnError func(http.ResponseWriter, string, string, string) + Required func() bool + Credentials func() (string, string) + OnError func(http.ResponseWriter, string, string, string) } -// BasicAuth enforces static access/secret key authentication when Required is true. +// BasicAuth enforces static access/secret key authentication when required. func BasicAuth(cfg AuthConfig) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !cfg.Required { + required := false + if cfg.Required != nil { + required = cfg.Required() + } + if !required { next.ServeHTTP(w, r) return } @@ -42,11 +45,16 @@ func BasicAuth(cfg AuthConfig) func(http.Handler) http.Handler { return } - if parts[0] != cfg.AccessKey { + accessKey, secretKey := "", "" + if cfg.Credentials != nil { + accessKey, secretKey = cfg.Credentials() + } + + if parts[0] != accessKey { invokeOnError(w, cfg, "InvalidAccessKeyId", "The AWS Access Key Id you provided does not match the configured key.") return } - if parts[1] != cfg.SecretKey { + if parts[1] != secretKey { invokeOnError(w, cfg, "SignatureDoesNotMatch", "The request signature we calculated does not match the signature you provided.") return } diff --git a/internal/middleware/virtual_host.go b/internal/middleware/virtual_host.go index 464372a..426cb3a 100644 --- a/internal/middleware/virtual_host.go +++ b/internal/middleware/virtual_host.go @@ -8,7 +8,7 @@ import ( // VirtualHostConfig controls host-style bucket resolution. type VirtualHostConfig struct { - Enabled bool + Enabled func() bool BucketExists func(string) bool } @@ -16,7 +16,11 @@ type VirtualHostConfig struct { func VirtualHost(cfg VirtualHostConfig) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !cfg.Enabled { + enabled := false + if cfg.Enabled != nil { + enabled = cfg.Enabled() + } + if !enabled { next.ServeHTTP(w, r) return }