s3_bucket_encryption.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. package s3api
  2. import (
  3. "encoding/xml"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "github.com/seaweedfs/seaweedfs/weed/glog"
  8. "github.com/seaweedfs/seaweedfs/weed/pb/s3_pb"
  9. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  10. "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
  11. )
  12. // ServerSideEncryptionConfiguration represents the bucket encryption configuration
  13. type ServerSideEncryptionConfiguration struct {
  14. XMLName xml.Name `xml:"ServerSideEncryptionConfiguration"`
  15. Rules []ServerSideEncryptionRule `xml:"Rule"`
  16. }
  17. // ServerSideEncryptionRule represents a single encryption rule
  18. type ServerSideEncryptionRule struct {
  19. ApplyServerSideEncryptionByDefault ApplyServerSideEncryptionByDefault `xml:"ApplyServerSideEncryptionByDefault"`
  20. BucketKeyEnabled *bool `xml:"BucketKeyEnabled,omitempty"`
  21. }
  22. // ApplyServerSideEncryptionByDefault specifies the default encryption settings
  23. type ApplyServerSideEncryptionByDefault struct {
  24. SSEAlgorithm string `xml:"SSEAlgorithm"`
  25. KMSMasterKeyID string `xml:"KMSMasterKeyID,omitempty"`
  26. }
  27. // encryptionConfigToProto converts EncryptionConfiguration to protobuf format
  28. func encryptionConfigToProto(config *s3_pb.EncryptionConfiguration) *s3_pb.EncryptionConfiguration {
  29. if config == nil {
  30. return nil
  31. }
  32. return &s3_pb.EncryptionConfiguration{
  33. SseAlgorithm: config.SseAlgorithm,
  34. KmsKeyId: config.KmsKeyId,
  35. BucketKeyEnabled: config.BucketKeyEnabled,
  36. }
  37. }
  38. // encryptionConfigFromXML converts XML ServerSideEncryptionConfiguration to protobuf
  39. func encryptionConfigFromXML(xmlConfig *ServerSideEncryptionConfiguration) *s3_pb.EncryptionConfiguration {
  40. if xmlConfig == nil || len(xmlConfig.Rules) == 0 {
  41. return nil
  42. }
  43. rule := xmlConfig.Rules[0] // AWS S3 supports only one rule
  44. return &s3_pb.EncryptionConfiguration{
  45. SseAlgorithm: rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm,
  46. KmsKeyId: rule.ApplyServerSideEncryptionByDefault.KMSMasterKeyID,
  47. BucketKeyEnabled: rule.BucketKeyEnabled != nil && *rule.BucketKeyEnabled,
  48. }
  49. }
  50. // encryptionConfigToXML converts protobuf EncryptionConfiguration to XML
  51. func encryptionConfigToXML(config *s3_pb.EncryptionConfiguration) *ServerSideEncryptionConfiguration {
  52. if config == nil {
  53. return nil
  54. }
  55. return &ServerSideEncryptionConfiguration{
  56. Rules: []ServerSideEncryptionRule{
  57. {
  58. ApplyServerSideEncryptionByDefault: ApplyServerSideEncryptionByDefault{
  59. SSEAlgorithm: config.SseAlgorithm,
  60. KMSMasterKeyID: config.KmsKeyId,
  61. },
  62. BucketKeyEnabled: &config.BucketKeyEnabled,
  63. },
  64. },
  65. }
  66. }
  67. // Default encryption algorithms
  68. const (
  69. EncryptionTypeAES256 = "AES256"
  70. EncryptionTypeKMS = "aws:kms"
  71. )
  72. // GetBucketEncryptionHandler handles GET bucket encryption requests
  73. func (s3a *S3ApiServer) GetBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
  74. bucket, _ := s3_constants.GetBucketAndObject(r)
  75. // Load bucket encryption configuration
  76. config, errCode := s3a.getEncryptionConfiguration(bucket)
  77. if errCode != s3err.ErrNone {
  78. if errCode == s3err.ErrNoSuchBucketEncryptionConfiguration {
  79. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucketEncryptionConfiguration)
  80. return
  81. }
  82. s3err.WriteErrorResponse(w, r, errCode)
  83. return
  84. }
  85. // Convert protobuf config to S3 XML response
  86. response := encryptionConfigToXML(config)
  87. if response == nil {
  88. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucketEncryptionConfiguration)
  89. return
  90. }
  91. w.Header().Set("Content-Type", "application/xml")
  92. if err := xml.NewEncoder(w).Encode(response); err != nil {
  93. glog.Errorf("Failed to encode bucket encryption response: %v", err)
  94. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  95. return
  96. }
  97. }
  98. // PutBucketEncryptionHandler handles PUT bucket encryption requests
  99. func (s3a *S3ApiServer) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
  100. bucket, _ := s3_constants.GetBucketAndObject(r)
  101. // Read and parse the request body
  102. body, err := io.ReadAll(r.Body)
  103. if err != nil {
  104. glog.Errorf("Failed to read request body: %v", err)
  105. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
  106. return
  107. }
  108. defer r.Body.Close()
  109. var xmlConfig ServerSideEncryptionConfiguration
  110. if err := xml.Unmarshal(body, &xmlConfig); err != nil {
  111. glog.Errorf("Failed to parse bucket encryption configuration: %v", err)
  112. s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML)
  113. return
  114. }
  115. // Validate the configuration
  116. if len(xmlConfig.Rules) == 0 {
  117. s3err.WriteErrorResponse(w, r, s3err.ErrMalformedXML)
  118. return
  119. }
  120. rule := xmlConfig.Rules[0] // AWS S3 supports only one rule
  121. // Validate SSE algorithm
  122. if rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm != EncryptionTypeAES256 &&
  123. rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm != EncryptionTypeKMS {
  124. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidEncryptionAlgorithm)
  125. return
  126. }
  127. // For aws:kms, validate KMS key if provided
  128. if rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm == EncryptionTypeKMS {
  129. keyID := rule.ApplyServerSideEncryptionByDefault.KMSMasterKeyID
  130. if keyID != "" && !isValidKMSKeyID(keyID) {
  131. s3err.WriteErrorResponse(w, r, s3err.ErrKMSKeyNotFound)
  132. return
  133. }
  134. }
  135. // Convert XML to protobuf configuration
  136. encryptionConfig := encryptionConfigFromXML(&xmlConfig)
  137. // Update the bucket configuration
  138. errCode := s3a.updateEncryptionConfiguration(bucket, encryptionConfig)
  139. if errCode != s3err.ErrNone {
  140. s3err.WriteErrorResponse(w, r, errCode)
  141. return
  142. }
  143. w.WriteHeader(http.StatusOK)
  144. }
  145. // DeleteBucketEncryptionHandler handles DELETE bucket encryption requests
  146. func (s3a *S3ApiServer) DeleteBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) {
  147. bucket, _ := s3_constants.GetBucketAndObject(r)
  148. errCode := s3a.removeEncryptionConfiguration(bucket)
  149. if errCode != s3err.ErrNone {
  150. s3err.WriteErrorResponse(w, r, errCode)
  151. return
  152. }
  153. w.WriteHeader(http.StatusNoContent)
  154. }
  155. // GetBucketEncryptionConfig retrieves the bucket encryption configuration for internal use
  156. func (s3a *S3ApiServer) GetBucketEncryptionConfig(bucket string) (*s3_pb.EncryptionConfiguration, error) {
  157. config, errCode := s3a.getEncryptionConfiguration(bucket)
  158. if errCode != s3err.ErrNone {
  159. if errCode == s3err.ErrNoSuchBucketEncryptionConfiguration {
  160. return nil, fmt.Errorf("no encryption configuration found")
  161. }
  162. return nil, fmt.Errorf("failed to get encryption configuration")
  163. }
  164. return config, nil
  165. }
  166. // Internal methods following the bucket configuration pattern
  167. // getEncryptionConfiguration retrieves encryption configuration with caching
  168. func (s3a *S3ApiServer) getEncryptionConfiguration(bucket string) (*s3_pb.EncryptionConfiguration, s3err.ErrorCode) {
  169. // Get metadata using structured API
  170. metadata, err := s3a.GetBucketMetadata(bucket)
  171. if err != nil {
  172. glog.Errorf("getEncryptionConfiguration: failed to get bucket metadata for bucket %s: %v", bucket, err)
  173. return nil, s3err.ErrInternalError
  174. }
  175. if metadata.Encryption == nil {
  176. return nil, s3err.ErrNoSuchBucketEncryptionConfiguration
  177. }
  178. return metadata.Encryption, s3err.ErrNone
  179. }
  180. // updateEncryptionConfiguration updates the encryption configuration for a bucket
  181. func (s3a *S3ApiServer) updateEncryptionConfiguration(bucket string, encryptionConfig *s3_pb.EncryptionConfiguration) s3err.ErrorCode {
  182. // Update using structured API
  183. err := s3a.UpdateBucketEncryption(bucket, encryptionConfig)
  184. if err != nil {
  185. glog.Errorf("updateEncryptionConfiguration: failed to update encryption config for bucket %s: %v", bucket, err)
  186. return s3err.ErrInternalError
  187. }
  188. // Cache will be updated automatically via metadata subscription
  189. return s3err.ErrNone
  190. }
  191. // removeEncryptionConfiguration removes the encryption configuration for a bucket
  192. func (s3a *S3ApiServer) removeEncryptionConfiguration(bucket string) s3err.ErrorCode {
  193. // Check if encryption configuration exists
  194. metadata, err := s3a.GetBucketMetadata(bucket)
  195. if err != nil {
  196. glog.Errorf("removeEncryptionConfiguration: failed to get bucket metadata for bucket %s: %v", bucket, err)
  197. return s3err.ErrInternalError
  198. }
  199. if metadata.Encryption == nil {
  200. return s3err.ErrNoSuchBucketEncryptionConfiguration
  201. }
  202. // Update using structured API
  203. err = s3a.ClearBucketEncryption(bucket)
  204. if err != nil {
  205. glog.Errorf("removeEncryptionConfiguration: failed to remove encryption config for bucket %s: %v", bucket, err)
  206. return s3err.ErrInternalError
  207. }
  208. // Cache will be updated automatically via metadata subscription
  209. return s3err.ErrNone
  210. }
  211. // IsDefaultEncryptionEnabled checks if default encryption is enabled for a bucket
  212. func (s3a *S3ApiServer) IsDefaultEncryptionEnabled(bucket string) bool {
  213. config, err := s3a.GetBucketEncryptionConfig(bucket)
  214. if err != nil || config == nil {
  215. return false
  216. }
  217. return config.SseAlgorithm != ""
  218. }
  219. // GetDefaultEncryptionHeaders returns the default encryption headers for a bucket
  220. func (s3a *S3ApiServer) GetDefaultEncryptionHeaders(bucket string) map[string]string {
  221. config, err := s3a.GetBucketEncryptionConfig(bucket)
  222. if err != nil || config == nil {
  223. return nil
  224. }
  225. headers := make(map[string]string)
  226. headers[s3_constants.AmzServerSideEncryption] = config.SseAlgorithm
  227. if config.SseAlgorithm == EncryptionTypeKMS && config.KmsKeyId != "" {
  228. headers[s3_constants.AmzServerSideEncryptionAwsKmsKeyId] = config.KmsKeyId
  229. }
  230. if config.BucketKeyEnabled {
  231. headers[s3_constants.AmzServerSideEncryptionBucketKeyEnabled] = "true"
  232. }
  233. return headers
  234. }
  235. // IsDefaultEncryptionEnabled checks if default encryption is enabled for a configuration
  236. func IsDefaultEncryptionEnabled(config *s3_pb.EncryptionConfiguration) bool {
  237. return config != nil && config.SseAlgorithm != ""
  238. }
  239. // GetDefaultEncryptionHeaders generates default encryption headers from configuration
  240. func GetDefaultEncryptionHeaders(config *s3_pb.EncryptionConfiguration) map[string]string {
  241. if config == nil || config.SseAlgorithm == "" {
  242. return nil
  243. }
  244. headers := make(map[string]string)
  245. headers[s3_constants.AmzServerSideEncryption] = config.SseAlgorithm
  246. if config.SseAlgorithm == "aws:kms" && config.KmsKeyId != "" {
  247. headers[s3_constants.AmzServerSideEncryptionAwsKmsKeyId] = config.KmsKeyId
  248. }
  249. return headers
  250. }
  251. // encryptionConfigFromXMLBytes parses XML bytes to encryption configuration
  252. func encryptionConfigFromXMLBytes(xmlBytes []byte) (*s3_pb.EncryptionConfiguration, error) {
  253. var xmlConfig ServerSideEncryptionConfiguration
  254. if err := xml.Unmarshal(xmlBytes, &xmlConfig); err != nil {
  255. return nil, err
  256. }
  257. // Validate namespace - should be empty or the standard AWS namespace
  258. if xmlConfig.XMLName.Space != "" && xmlConfig.XMLName.Space != "http://s3.amazonaws.com/doc/2006-03-01/" {
  259. return nil, fmt.Errorf("invalid XML namespace: %s", xmlConfig.XMLName.Space)
  260. }
  261. // Validate the configuration
  262. if len(xmlConfig.Rules) == 0 {
  263. return nil, fmt.Errorf("encryption configuration must have at least one rule")
  264. }
  265. rule := xmlConfig.Rules[0]
  266. if rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm == "" {
  267. return nil, fmt.Errorf("encryption algorithm is required")
  268. }
  269. // Validate algorithm
  270. validAlgorithms := map[string]bool{
  271. "AES256": true,
  272. "aws:kms": true,
  273. }
  274. if !validAlgorithms[rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm] {
  275. return nil, fmt.Errorf("unsupported encryption algorithm: %s", rule.ApplyServerSideEncryptionByDefault.SSEAlgorithm)
  276. }
  277. config := encryptionConfigFromXML(&xmlConfig)
  278. return config, nil
  279. }
  280. // encryptionConfigToXMLBytes converts encryption configuration to XML bytes
  281. func encryptionConfigToXMLBytes(config *s3_pb.EncryptionConfiguration) ([]byte, error) {
  282. if config == nil {
  283. return nil, fmt.Errorf("encryption configuration is nil")
  284. }
  285. xmlConfig := encryptionConfigToXML(config)
  286. return xml.Marshal(xmlConfig)
  287. }