| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- package s3api
- import (
- "encoding/xml"
- "fmt"
- "io"
- "net/http"
- "github.com/seaweedfs/seaweedfs/weed/glog"
- "github.com/seaweedfs/seaweedfs/weed/pb/s3_pb"
- "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
- "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
- )
- // ServerSideEncryptionConfiguration represents the bucket encryption configuration
- type ServerSideEncryptionConfiguration struct {
- XMLName xml.Name `xml:"ServerSideEncryptionConfiguration"`
- Rules []ServerSideEncryptionRule `xml:"Rule"`
- }
- // ServerSideEncryptionRule represents a single encryption rule
- type ServerSideEncryptionRule struct {
- ApplyServerSideEncryptionByDefault ApplyServerSideEncryptionByDefault `xml:"ApplyServerSideEncryptionByDefault"`
- BucketKeyEnabled *bool `xml:"BucketKeyEnabled,omitempty"`
- }
- // ApplyServerSideEncryptionByDefault specifies the default encryption settings
- type ApplyServerSideEncryptionByDefault struct {
- SSEAlgorithm string `xml:"SSEAlgorithm"`
- KMSMasterKeyID string `xml:"KMSMasterKeyID,omitempty"`
- }
- // encryptionConfigToProto converts EncryptionConfiguration to protobuf format
- func encryptionConfigToProto(config *s3_pb.EncryptionConfiguration) *s3_pb.EncryptionConfiguration {
- if config == nil {
- return nil
- }
- return &s3_pb.EncryptionConfiguration{
- SseAlgorithm: config.SseAlgorithm,
- KmsKeyId: config.KmsKeyId,
- BucketKeyEnabled: config.BucketKeyEnabled,
- }
- }
- // encryptionConfigFromXML converts XML ServerSideEncryptionConfiguration to protobuf
- func encryptionConfigFromXML(xmlConfig *ServerSideEncryptionConfiguration) *s3_pb.EncryptionConfiguration {
- if xmlConfig == nil || len(xmlConfig.Rules) == 0 {
- return nil
- }
- rule := xmlConfig.Rules[0] // AWS S3 supports only one rule
- return &s3_pb.EncryptionConfiguration{
- SseAlgorithm: rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm,
- KmsKeyId: rule.ApplyServerSideEncryptionByDefault.KMSMasterKeyID,
- BucketKeyEnabled: rule.BucketKeyEnabled != nil && *rule.BucketKeyEnabled,
- }
- }
- // encryptionConfigToXML converts protobuf EncryptionConfiguration to XML
- func encryptionConfigToXML(config *s3_pb.EncryptionConfiguration) *ServerSideEncryptionConfiguration {
- if config == nil {
- return nil
- }
- return &ServerSideEncryptionConfiguration{
- Rules: []ServerSideEncryptionRule{
- {
- ApplyServerSideEncryptionByDefault: ApplyServerSideEncryptionByDefault{
- SSEAlgorithm: config.SseAlgorithm,
- KMSMasterKeyID: config.KmsKeyId,
- },
- BucketKeyEnabled: &config.BucketKeyEnabled,
- },
- },
- }
- }
- // Default encryption algorithms
- const (
- EncryptionTypeAES256 = "AES256"
- EncryptionTypeKMS = "aws:kms"
- )
- // GetBucketEncryptionHandler handles GET bucket encryption requests
- func (s3a *S3ApiServer) GetBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
- bucket, _ := s3_constants.GetBucketAndObject(r)
- // Load bucket encryption configuration
- config, errCode := s3a.getEncryptionConfiguration(bucket)
- if errCode != s3err.ErrNone {
- if errCode == s3err.ErrNoSuchBucketEncryptionConfiguration {
- s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucketEncryptionConfiguration)
- return
- }
- s3err.WriteErrorResponse(w, r, errCode)
- return
- }
- // Convert protobuf config to S3 XML response
- response := encryptionConfigToXML(config)
- if response == nil {
- s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucketEncryptionConfiguration)
- return
- }
- w.Header().Set("Content-Type", "application/xml")
- if err := xml.NewEncoder(w).Encode(response); err != nil {
- glog.Errorf("Failed to encode bucket encryption response: %v", err)
- s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
- return
- }
- }
- // PutBucketEncryptionHandler handles PUT bucket encryption requests
- func (s3a *S3ApiServer) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
- bucket, _ := s3_constants.GetBucketAndObject(r)
- // Read and parse the request body
- body, err := io.ReadAll(r.Body)
- if err != nil {
- glog.Errorf("Failed to read request body: %v", err)
- s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
- return
- }
- defer r.Body.Close()
- var xmlConfig ServerSideEncryptionConfiguration
- if err := xml.Unmarshal(body, &xmlConfig); err != nil {
- glog.Errorf("Failed to parse bucket encryption configuration: %v", err)
- s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML)
- return
- }
- // Validate the configuration
- if len(xmlConfig.Rules) == 0 {
- s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML)
- return
- }
- rule := xmlConfig.Rules[0] // AWS S3 supports only one rule
- // Validate SSE algorithm
- if rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm != EncryptionTypeAES256 &&
- rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm != EncryptionTypeKMS {
- s3err.WriteErrorResponse(w, r, s3err.ErrInvalidEncryptionAlgorithm)
- return
- }
- // For aws:kms, validate KMS key if provided
- if rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm == EncryptionTypeKMS {
- keyID := rule.ApplyServerSideEncryptionByDefault.KMSMasterKeyID
- if keyID != "" && !isValidKMSKeyID(keyID) {
- s3err.WriteErrorResponse(w, r, s3err.ErrKMSKeyNotFound)
- return
- }
- }
- // Convert XML to protobuf configuration
- encryptionConfig := encryptionConfigFromXML(&xmlConfig)
- // Update the bucket configuration
- errCode := s3a.updateEncryptionConfiguration(bucket, encryptionConfig)
- if errCode != s3err.ErrNone {
- s3err.WriteErrorResponse(w, r, errCode)
- return
- }
- w.WriteHeader(http.StatusOK)
- }
- // DeleteBucketEncryptionHandler handles DELETE bucket encryption requests
- func (s3a *S3ApiServer) DeleteBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
- bucket, _ := s3_constants.GetBucketAndObject(r)
- errCode := s3a.removeEncryptionConfiguration(bucket)
- if errCode != s3err.ErrNone {
- s3err.WriteErrorResponse(w, r, errCode)
- return
- }
- w.WriteHeader(http.StatusNoContent)
- }
- // GetBucketEncryptionConfig retrieves the bucket encryption configuration for internal use
- func (s3a *S3ApiServer) GetBucketEncryptionConfig(bucket string) (*s3_pb.EncryptionConfiguration, error) {
- config, errCode := s3a.getEncryptionConfiguration(bucket)
- if errCode != s3err.ErrNone {
- if errCode == s3err.ErrNoSuchBucketEncryptionConfiguration {
- return nil, fmt.Errorf("no encryption configuration found")
- }
- return nil, fmt.Errorf("failed to get encryption configuration")
- }
- return config, nil
- }
- // Internal methods following the bucket configuration pattern
- // getEncryptionConfiguration retrieves encryption configuration with caching
- func (s3a *S3ApiServer) getEncryptionConfiguration(bucket string) (*s3_pb.EncryptionConfiguration, s3err.ErrorCode) {
- // Get metadata using structured API
- metadata, err := s3a.GetBucketMetadata(bucket)
- if err != nil {
- glog.Errorf("getEncryptionConfiguration: failed to get bucket metadata for bucket %s: %v", bucket, err)
- return nil, s3err.ErrInternalError
- }
- if metadata.Encryption == nil {
- return nil, s3err.ErrNoSuchBucketEncryptionConfiguration
- }
- return metadata.Encryption, s3err.ErrNone
- }
- // updateEncryptionConfiguration updates the encryption configuration for a bucket
- func (s3a *S3ApiServer) updateEncryptionConfiguration(bucket string, encryptionConfig *s3_pb.EncryptionConfiguration) s3err.ErrorCode {
- // Update using structured API
- err := s3a.UpdateBucketEncryption(bucket, encryptionConfig)
- if err != nil {
- glog.Errorf("updateEncryptionConfiguration: failed to update encryption config for bucket %s: %v", bucket, err)
- return s3err.ErrInternalError
- }
- // Cache will be updated automatically via metadata subscription
- return s3err.ErrNone
- }
- // removeEncryptionConfiguration removes the encryption configuration for a bucket
- func (s3a *S3ApiServer) removeEncryptionConfiguration(bucket string) s3err.ErrorCode {
- // Check if encryption configuration exists
- metadata, err := s3a.GetBucketMetadata(bucket)
- if err != nil {
- glog.Errorf("removeEncryptionConfiguration: failed to get bucket metadata for bucket %s: %v", bucket, err)
- return s3err.ErrInternalError
- }
- if metadata.Encryption == nil {
- return s3err.ErrNoSuchBucketEncryptionConfiguration
- }
- // Update using structured API
- err = s3a.ClearBucketEncryption(bucket)
- if err != nil {
- glog.Errorf("removeEncryptionConfiguration: failed to remove encryption config for bucket %s: %v", bucket, err)
- return s3err.ErrInternalError
- }
- // Cache will be updated automatically via metadata subscription
- return s3err.ErrNone
- }
- // IsDefaultEncryptionEnabled checks if default encryption is enabled for a bucket
- func (s3a *S3ApiServer) IsDefaultEncryptionEnabled(bucket string) bool {
- config, err := s3a.GetBucketEncryptionConfig(bucket)
- if err != nil || config == nil {
- return false
- }
- return config.SseAlgorithm != ""
- }
- // GetDefaultEncryptionHeaders returns the default encryption headers for a bucket
- func (s3a *S3ApiServer) GetDefaultEncryptionHeaders(bucket string) map[string]string {
- config, err := s3a.GetBucketEncryptionConfig(bucket)
- if err != nil || config == nil {
- return nil
- }
- headers := make(map[string]string)
- headers[s3_constants.AmzServerSideEncryption] = config.SseAlgorithm
- if config.SseAlgorithm == EncryptionTypeKMS && config.KmsKeyId != "" {
- headers[s3_constants.AmzServerSideEncryptionAwsKmsKeyId] = config.KmsKeyId
- }
- if config.BucketKeyEnabled {
- headers[s3_constants.AmzServerSideEncryptionBucketKeyEnabled] = "true"
- }
- return headers
- }
- // IsDefaultEncryptionEnabled checks if default encryption is enabled for a configuration
- func IsDefaultEncryptionEnabled(config *s3_pb.EncryptionConfiguration) bool {
- return config != nil && config.SseAlgorithm != ""
- }
- // GetDefaultEncryptionHeaders generates default encryption headers from configuration
- func GetDefaultEncryptionHeaders(config *s3_pb.EncryptionConfiguration) map[string]string {
- if config == nil || config.SseAlgorithm == "" {
- return nil
- }
- headers := make(map[string]string)
- headers[s3_constants.AmzServerSideEncryption] = config.SseAlgorithm
- if config.SseAlgorithm == "aws:kms" && config.KmsKeyId != "" {
- headers[s3_constants.AmzServerSideEncryptionAwsKmsKeyId] = config.KmsKeyId
- }
- return headers
- }
- // encryptionConfigFromXMLBytes parses XML bytes to encryption configuration
- func encryptionConfigFromXMLBytes(xmlBytes []byte) (*s3_pb.EncryptionConfiguration, error) {
- var xmlConfig ServerSideEncryptionConfiguration
- if err := xml.Unmarshal(xmlBytes, &xmlConfig); err != nil {
- return nil, err
- }
- // Validate namespace - should be empty or the standard AWS namespace
- if xmlConfig.XMLName.Space != "" && xmlConfig.XMLName.Space != "http://s3.amazonaws.com/doc/2006-03-01/" {
- return nil, fmt.Errorf("invalid XML namespace: %s", xmlConfig.XMLName.Space)
- }
- // Validate the configuration
- if len(xmlConfig.Rules) == 0 {
- return nil, fmt.Errorf("encryption configuration must have at least one rule")
- }
- rule := xmlConfig.Rules[0]
- if rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm == "" {
- return nil, fmt.Errorf("encryption algorithm is required")
- }
- // Validate algorithm
- validAlgorithms := map[string]bool{
- "AES256": true,
- "aws:kms": true,
- }
- if !validAlgorithms[rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm] {
- return nil, fmt.Errorf("unsupported encryption algorithm: %s", rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm)
- }
- config := encryptionConfigFromXML(&xmlConfig)
- return config, nil
- }
- // encryptionConfigToXMLBytes converts encryption configuration to XML bytes
- func encryptionConfigToXMLBytes(config *s3_pb.EncryptionConfiguration) ([]byte, error) {
- if config == nil {
- return nil, fmt.Errorf("encryption configuration is nil")
- }
- xmlConfig := encryptionConfigToXML(config)
- return xml.Marshal(xmlConfig)
- }
|