]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: Introduce BucketEncryption APIs to support SSE-S3 feature
authorRahul Dev Parashar <rahul.dev@flipkart.com>
Mon, 19 Jul 2021 07:18:14 +0000 (12:48 +0530)
committerRahul Dev Parashar <rahul.dev@flipkart.com>
Mon, 19 Jul 2021 07:18:14 +0000 (12:48 +0530)
This patch introduces support for 3 new BucketEncryption APIs which are listed
below and are helpful in supporting AWS SSE-S3 encryption mode.
PutBucketEncryption: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketEncryption.html
GetBucketEncryption: https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketEncryption.html
DeleteBucketEncryption: https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketEncryption.html

The user provided parameters are parsed and stored in the bucket's extended
attributes RGW_ATTR_BUCKET_ENCRYPTION and
RGW_ATTR_BUCKET_ENCRYPTION_SSE_S3_KEY_ID.

Signed-off-by: Rahul Dev Parashar <rahul.dev@flipkart.com>
16 files changed:
doc/radosgw/encryption.rst
src/rgw/CMakeLists.txt
src/rgw/rgw_auth_s3.cc
src/rgw/rgw_bucket_encryption.cc [new file with mode: 0644]
src/rgw/rgw_bucket_encryption.h [new file with mode: 0644]
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_iam_policy.cc
src/rgw/rgw_iam_policy.h
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_op_type.h
src/rgw/rgw_rest.h
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h
src/test/rgw/test_rgw_iam_policy.cc

index 07fd60ac5d12449c8b2db17564e4d1be68dde1b4..2b51e088f5356c4bbed5588fce14214c22197bf5 100644 (file)
@@ -43,6 +43,15 @@ integration with `Barbican`_, `Vault`_, and `KMIP`_ are implemented.
 See `OpenStack Barbican Integration`_, `HashiCorp Vault Integration`_,
 and `KMIP Integration`_.
 
+Bucket Encryption APIs
+======================
+
+Bucket Encryption APIs to support server-side encryption with Amazon
+S3-managed keys (SSE-S3) or AWS KMS customer master keys (SSE-KMS). 
+SSE-KMS implementation via BucketEncryption APIs is not supported yet.
+
+See `PutBucketEncryption`_, `GetBucketEncryption`_, `DeleteBucketEncryption`_
+
 Automatic Encryption (for testing only)
 =======================================
 
@@ -63,6 +72,9 @@ The configuration expects a base64-encoded 256 bit key. For example::
 .. _Barbican: https://wiki.openstack.org/wiki/Barbican
 .. _Vault: https://www.vaultproject.io/docs/
 .. _KMIP: http://www.oasis-open.org/committees/kmip/
+.. _PutBucketEncryption: https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketEncryption.html
+.. _GetBucketEncryption: https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketEncryption.html
+.. _DeleteBucketEncryption: https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketEncryption.html
 .. _OpenStack Barbican Integration: ../barbican
 .. _HashiCorp Vault Integration: ../vault
 .. _KMIP Integration: ../kmip
index e738875a7f08bf21159bd9043cd9aba9549faa78..26d1673a992f24d5fb8fa341ac8684607d09a9da 100644 (file)
@@ -155,7 +155,8 @@ set(librgw_common_srcs
   cls_fifo_legacy.cc
   rgw_lua_utils.cc
   rgw_lua.cc
-  rgw_lua_request.cc)
+  rgw_lua_request.cc
+  rgw_bucket_encryption.cc)
 
 if(WITH_RADOSGW_AMQP_ENDPOINT)
   list(APPEND librgw_common_srcs rgw_amqp.cc)
index b6988be5a63ea088ba7182d3e4e3fa8322d15156..b253c93f89f225294f243a7327ebb9e57b609a35 100644 (file)
@@ -28,6 +28,7 @@ static const auto signed_subresources = {
   "acl",
   "cors",
   "delete",
+  "encryption",
   "lifecycle",
   "location",
   "logging",
diff --git a/src/rgw/rgw_bucket_encryption.cc b/src/rgw/rgw_bucket_encryption.cc
new file mode 100644 (file)
index 0000000..2913ce8
--- /dev/null
@@ -0,0 +1,34 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+//
+#include "rgw_bucket_encryption.h"
+#include "rgw_xml.h"
+
+void ApplyServerSideEncryptionByDefault::decode_xml(XMLObj *obj) {
+  RGWXMLDecoder::decode_xml("KMSMasterKeyID", kmsMasterKeyID, obj, false);
+  RGWXMLDecoder::decode_xml("SSEAlgorithm", sseAlgorithm, obj, false);
+}
+
+void ApplyServerSideEncryptionByDefault::dump_xml(Formatter *f) const {
+  encode_xml("SSEAlgorithm", sseAlgorithm, f);
+}
+
+void ServerSideEncryptionConfiguration::decode_xml(XMLObj *obj) {
+  RGWXMLDecoder::decode_xml("ApplyServerSideEncryptionByDefault", applyServerSideEncryptionByDefault, obj, true);
+  RGWXMLDecoder::decode_xml("BucketKeyEnabled", bucketKeyEnabled, obj, false);
+}
+
+void ServerSideEncryptionConfiguration::dump_xml(Formatter *f) const {
+  encode_xml("ApplyServerSideEncryptionByDefault", applyServerSideEncryptionByDefault, f);
+}
+
+void RGWBucketEncryptionConfig::decode_xml(XMLObj *obj) {
+  rule_exist = RGWXMLDecoder::decode_xml("Rule", rule, obj);
+  if(!rule_exist) {
+    throw RGWXMLDecoder::err("rule must be present in XML");
+  }
+}
+
+void RGWBucketEncryptionConfig::dump_xml(Formatter *f) const {
+  encode_xml("Rule", rule, f);
+}
diff --git a/src/rgw/rgw_bucket_encryption.h b/src/rgw/rgw_bucket_encryption.h
new file mode 100644 (file)
index 0000000..9df9ed5
--- /dev/null
@@ -0,0 +1,130 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+#pragma once
+#include <include/types.h>
+
+class XMLObj;
+
+class ApplyServerSideEncryptionByDefault
+{
+  string kmsMasterKeyID;
+  string sseAlgorithm;
+
+public:
+  ApplyServerSideEncryptionByDefault(): kmsMasterKeyID(""), sseAlgorithm("") {};
+
+  const string& kms_master_key_id() const {
+    return kmsMasterKeyID;
+  }
+
+  const string& sse_algorithm() const {
+    return sseAlgorithm;
+  }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    encode(kmsMasterKeyID, bl);
+    encode(sseAlgorithm, bl);
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::const_iterator& bl) {
+    DECODE_START(1, bl);
+    decode(kmsMasterKeyID, bl);
+    decode(sseAlgorithm, bl);
+    DECODE_FINISH(bl);
+  }
+
+  void decode_xml(XMLObj *obj);
+  void dump_xml(Formatter *f) const;
+};
+WRITE_CLASS_ENCODER(ApplyServerSideEncryptionByDefault)
+
+class ServerSideEncryptionConfiguration
+{
+protected:
+  ApplyServerSideEncryptionByDefault applyServerSideEncryptionByDefault;
+  bool bucketKeyEnabled;
+
+public:
+  ServerSideEncryptionConfiguration(): bucketKeyEnabled(false) {};
+
+  const string& kms_master_key_id() const {
+    return applyServerSideEncryptionByDefault.kms_master_key_id();
+  }
+
+  const string& sse_algorithm() const {
+    return applyServerSideEncryptionByDefault.sse_algorithm();
+  }
+
+  bool bucket_key_enabled() const {
+    return bucketKeyEnabled;
+  }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    encode(applyServerSideEncryptionByDefault, bl);
+    encode(bucketKeyEnabled, bl);
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::const_iterator& bl) {
+    DECODE_START(1, bl);
+    decode(applyServerSideEncryptionByDefault, bl);
+    decode(bucketKeyEnabled, bl);
+    DECODE_FINISH(bl);
+  }
+
+  void decode_xml(XMLObj *obj);
+  void dump_xml(Formatter *f) const;
+};
+WRITE_CLASS_ENCODER(ServerSideEncryptionConfiguration)
+
+class RGWBucketEncryptionConfig
+{
+protected:
+  bool rule_exist;
+  ServerSideEncryptionConfiguration rule;
+
+public:
+  RGWBucketEncryptionConfig(): rule_exist(false) {}
+
+  const string& kms_master_key_id() const {
+    return rule.kms_master_key_id();
+  }
+
+  const string& sse_algorithm() const {
+    return rule.sse_algorithm();
+  }
+
+  bool bucket_key_enabled() const {
+    return rule.bucket_key_enabled();
+  }
+
+  bool has_rule() const {
+    return rule_exist;
+  }
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    encode(rule_exist, bl);
+    if (rule_exist) {
+      encode(rule, bl);
+    }
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::const_iterator& bl) {
+    DECODE_START(1, bl);
+    decode(rule_exist, bl);
+    if (rule_exist) {
+      decode(rule, bl);
+    }
+    DECODE_FINISH(bl);
+  }
+
+  void decode_xml(XMLObj *obj);
+  void dump_xml(Formatter *f) const;
+};
+WRITE_CLASS_ENCODER(RGWBucketEncryptionConfig)
index 39da1fdb2bb2c426cdcb428313ce882d42633bf1..594cfa72d14a051c6f430d561b68e2f1a5840b88 100644 (file)
@@ -127,6 +127,7 @@ rgw_http_errors rgw_http_s3_errors({
     { ERR_RATE_LIMITED, {503, "SlowDown"}},
     { ERR_ZERO_IN_URL, {400, "InvalidRequest" }},
     { ERR_NO_SUCH_TAG_SET, {404, "NoSuchTagSetError"}},
+    { ERR_NO_SUCH_BUCKET_ENCRYPTION_CONFIGURATION, {404, "ServerSideEncryptionConfigurationNotFoundError"}},
 });
 
 rgw_http_errors rgw_http_swift_errors({
index dbe647f5817ccb3c08dbb49a4c23727474e8089b..ea8692c2be85ee20b41a426bf3f0d44400a6d0fd 100644 (file)
@@ -39,6 +39,7 @@
 #include "cls/rgw/cls_rgw_types.h"
 #include "include/rados/librados.hpp"
 #include "rgw_public_access.h"
+#include "rgw_bucket_encryption.h"
 
 namespace ceph {
   class Formatter;
@@ -143,6 +144,11 @@ using ceph::crypto::MD5;
 #define RGW_ATTR_CRYPT_CONTEXT  RGW_ATTR_CRYPT_PREFIX "context"
 #define RGW_ATTR_CRYPT_DATAKEY  RGW_ATTR_CRYPT_PREFIX "datakey"
 
+/* SSE-S3 Encryption Attributes */
+#define RGW_ATTR_BUCKET_ENCRYPTION_PREFIX RGW_ATTR_PREFIX "sse-s3."
+#define RGW_ATTR_BUCKET_ENCRYPTION_POLICY RGW_ATTR_BUCKET_ENCRYPTION_PREFIX "policy"
+#define RGW_ATTR_BUCKET_ENCRYPTION_KEY_ID RGW_ATTR_BUCKET_ENCRYPTION_PREFIX "key-id"
+
 
 #define RGW_FORMAT_PLAIN        0
 #define RGW_FORMAT_XML          1
@@ -230,6 +236,7 @@ using ceph::crypto::MD5;
 #define ERR_NO_SUCH_CORS_CONFIGURATION 2045
 #define ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION  2046
 #define ERR_INVALID_RETENTION_PERIOD 2047
+#define ERR_NO_SUCH_BUCKET_ENCRYPTION_CONFIGURATION 2048
 #define ERR_USER_SUSPENDED       2100
 #define ERR_INTERNAL_ERROR       2200
 #define ERR_NOT_IMPLEMENTED      2201
index 48780c2efbb020e6dd904dd66e57193879390899..686601d3ac7bbf1a78da3dbbaf4842a2b31faf19 100644 (file)
@@ -79,6 +79,7 @@ static const actpair actpairs[] =
  { "s3:GetAccelerateConfiguration", s3GetAccelerateConfiguration },
  { "s3:GetBucketAcl", s3GetBucketAcl },
  { "s3:GetBucketCORS", s3GetBucketCORS },
+ { "s3:GetBucketEncryption", s3GetBucketEncryption },
  { "s3:GetBucketLocation", s3GetBucketLocation },
  { "s3:GetBucketLogging", s3GetBucketLogging },
  { "s3:GetBucketNotification", s3GetBucketNotification },
@@ -111,6 +112,7 @@ static const actpair actpairs[] =
  { "s3:PutAccelerateConfiguration", s3PutAccelerateConfiguration },
  { "s3:PutBucketAcl", s3PutBucketAcl },
  { "s3:PutBucketCORS", s3PutBucketCORS },
+ { "s3:PutBucketEncryption", s3PutBucketEncryption },
  { "s3:PutBucketLogging", s3PutBucketLogging },
  { "s3:PutBucketNotification", s3PutBucketNotification },
  { "s3:PutBucketPolicy", s3PutBucketPolicy },
@@ -1136,6 +1138,12 @@ const char* action_bit_string(uint64_t action) {
   case s3PutBucketCORS:
     return "s3:PutBucketCORS";
 
+  case s3GetBucketEncryption:
+    return "s3:GetBucketEncryption";
+
+  case s3PutBucketEncryption:
+    return "s3:PutBucketEncryption";
+
   case s3GetBucketVersioning:
     return "s3:GetBucketVersioning";
 
index 808c2296c941fbfa9924eae109845ca8bb1c60d9..0575ce8617ca664ccd72b3839b179c379915441d 100644 (file)
@@ -107,7 +107,10 @@ static constexpr std::uint64_t s3DeletePublicAccessBlock = 64;
 static constexpr std::uint64_t s3GetBucketPublicAccessBlock = 65;
 static constexpr std::uint64_t s3PutBucketPublicAccessBlock = 66;
 static constexpr std::uint64_t s3DeleteBucketPublicAccessBlock = 67;
-static constexpr std::uint64_t s3All = 68;
+static constexpr std::uint64_t s3GetBucketEncryption = 68;
+static constexpr std::uint64_t s3PutBucketEncryption = 69;
+static constexpr std::uint64_t s3DeleteBucketEncryption = 70;
+static constexpr std::uint64_t s3All = 71;
 
 static constexpr std::uint64_t iamPutUserPolicy = s3All + 1;
 static constexpr std::uint64_t iamGetUserPolicy = s3All + 2;
@@ -197,6 +200,7 @@ inline int op_to_perm(std::uint64_t op) {
   case s3GetAccelerateConfiguration:
   case s3GetBucketAcl:
   case s3GetBucketCORS:
+  case s3GetBucketEncryption:
   case s3GetBucketLocation:
   case s3GetBucketLogging:
   case s3GetBucketNotification:
@@ -220,6 +224,7 @@ inline int op_to_perm(std::uint64_t op) {
   case s3PutAccelerateConfiguration:
   case s3PutBucketAcl:
   case s3PutBucketCORS:
+  case s3PutBucketEncryption:
   case s3PutBucketLogging:
   case s3PutBucketNotification:
   case s3PutBucketPolicy:
index 167ad3593e446542e84ef1f89c3999c82b180db4..dbe15d32d994b7a5374d3472f324c70fa8b36de3 100644 (file)
@@ -8510,3 +8510,133 @@ void RGWDeleteBucketPublicAccessBlock::execute(optional_yield y)
       return op_ret;
     });
 }
+
+int RGWPutBucketEncryption::get_params(optional_yield y)
+{
+  const auto max_size = s->cct->_conf->rgw_max_put_param_size;
+  std::tie(op_ret, data) = read_all_input(s, max_size, false);
+  return op_ret;
+}
+
+int RGWPutBucketEncryption::verify_permission(optional_yield y)
+{
+  if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketEncryption)) {
+    return -EACCES;
+  }
+  return 0;
+}
+
+void RGWPutBucketEncryption::execute(optional_yield y)
+{
+  RGWXMLDecoder::XMLParser parser;
+  if (!parser.init()) {
+    ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
+    op_ret = -EINVAL;
+    return;
+  }
+  op_ret = get_params(y);
+  if (op_ret < 0) {
+    return;
+  }
+  if (!parser.parse(data.c_str(), data.length(), 1)) {
+    ldpp_dout(this, 0) << "ERROR: malformed XML" << dendl;
+    op_ret = -ERR_MALFORMED_XML;
+    return;
+  }
+
+  try {
+    RGWXMLDecoder::decode_xml("ServerSideEncryptionConfiguration", bucket_encryption_conf, &parser, true);
+  } catch (RGWXMLDecoder::err& err) {
+    ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
+    op_ret = -ERR_MALFORMED_XML;
+    return;
+  }
+
+  if(bucket_encryption_conf.kms_master_key_id().compare("") != 0) {
+    ldpp_dout(this, 5) << "encryption not supported with sse-kms" << dendl;
+    op_ret = -ERR_NOT_IMPLEMENTED;
+    s->err.message = "SSE-KMS support is not provided";
+    return;
+  }
+
+  if(bucket_encryption_conf.sse_algorithm().compare("AES256") != 0) {
+    ldpp_dout(this, 5) << "only aes256 algorithm is supported for encryption" << dendl;
+    op_ret = -ERR_NOT_IMPLEMENTED;
+    s->err.message = "Encryption is supported only with AES256 algorithm";
+    return;
+  }
+
+  op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
+  if (op_ret < 0) {
+    ldpp_dout(this, 20) << "forward_request_to_master returned ret=" << op_ret << dendl;
+    return;
+  }
+
+  bufferlist key_id_bl;
+  string bucket_owner_id = s->bucket->get_info().owner.id;
+  key_id_bl.append(bucket_owner_id.c_str(), bucket_owner_id.size() + 1);
+
+  bufferlist conf_bl;
+  bucket_encryption_conf.encode(conf_bl);
+  op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, y, &conf_bl, &key_id_bl] {
+    rgw::sal::Attrs attrs = s->bucket->get_attrs();
+    attrs[RGW_ATTR_BUCKET_ENCRYPTION_POLICY] = conf_bl;
+    attrs[RGW_ATTR_BUCKET_ENCRYPTION_KEY_ID] = key_id_bl;
+    return s->bucket->set_instance_attrs(this, attrs, y);
+  });
+}
+
+int RGWGetBucketEncryption::verify_permission(optional_yield y)
+{
+  if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketEncryption)) {
+    return -EACCES;
+  }
+  return 0;
+}
+
+void RGWGetBucketEncryption::execute(optional_yield y)
+{
+  const auto& attrs = s->bucket_attrs;
+  if (auto aiter = attrs.find(RGW_ATTR_BUCKET_ENCRYPTION_POLICY);
+      aiter == attrs.end()) {
+    ldpp_dout(this, 0) << "can't find BUCKET ENCRYPTION attr for bucket_name = " << s->bucket_name << dendl;
+    op_ret = -ENOENT;
+    s->err.message = "The server side encryption configuration was not found";
+    return;
+  } else {
+    bufferlist::const_iterator iter{&aiter->second};
+    try {
+      bucket_encryption_conf.decode(iter);
+    } catch (const buffer::error& e) {
+      ldpp_dout(this, 0) << __func__ <<  "decode bucket_encryption_conf failed" << dendl;
+      op_ret = -EIO;
+      return;
+    }
+  }
+}
+
+int RGWDeleteBucketEncryption::verify_permission(optional_yield y)
+{
+  if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketEncryption)) {
+    return -EACCES;
+  }
+  return 0;
+}
+
+void RGWDeleteBucketEncryption::execute(optional_yield y)
+{
+  bufferlist data;
+  op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
+  if (op_ret < 0) {
+    ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
+    return;
+  }
+
+  op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, y] {
+    rgw::sal::Attrs attrs = s->bucket->get_attrs();
+    attrs.erase(RGW_ATTR_BUCKET_ENCRYPTION_POLICY);
+    attrs.erase(RGW_ATTR_BUCKET_ENCRYPTION_KEY_ID);
+    op_ret = s->bucket->set_instance_attrs(this, attrs, y);
+    return op_ret;
+  });
+}
index 58303cbf0f9addf712051ba380b5c68b8d506f60..8eb1ef18227127b737d2ef0e5e917515e255ddf7 100644 (file)
@@ -51,6 +51,7 @@
 #include "rgw_object_lock.h"
 #include "cls/rgw/cls_rgw_client.h"
 #include "rgw_public_access.h"
+#include "rgw_bucket_encryption.h"
 
 #include "services/svc_sys_obj.h"
 #include "services/svc_tier_rados.h"
@@ -1736,6 +1737,50 @@ public:
   uint32_t op_mask() override { return RGW_OP_TYPE_READ; }
 };
 
+class RGWPutBucketEncryption : public RGWOp {
+protected:
+  RGWBucketEncryptionConfig bucket_encryption_conf;
+  bufferlist data;
+public:
+  RGWPutBucketEncryption() = default;
+  ~RGWPutBucketEncryption() {}
+
+  int get_params(optional_yield y);
+  int verify_permission(optional_yield y) override;
+  void execute(optional_yield y) override;
+  const char* name() const override { return "put_bucket_encryption"; }
+  RGWOpType get_type() override { return RGW_OP_PUT_BUCKET_ENCRYPTION; }
+  uint32_t op_mask() override { return RGW_OP_TYPE_WRITE; }
+};
+
+class RGWGetBucketEncryption : public RGWOp {
+protected:
+  RGWBucketEncryptionConfig bucket_encryption_conf;
+public:
+  RGWGetBucketEncryption() {}
+
+  int get_params(optional_yield y);
+  int verify_permission(optional_yield y) override;
+  void execute(optional_yield y) override;
+  const char* name() const override { return "get_bucket_encryption"; }
+  RGWOpType get_type() override { return RGW_OP_GET_BUCKET_ENCRYPTION; }
+  uint32_t op_mask() override { return RGW_OP_TYPE_READ; }
+};
+
+class RGWDeleteBucketEncryption : public RGWOp {
+protected:
+  RGWBucketEncryptionConfig bucket_encryption_conf;
+public:
+  RGWDeleteBucketEncryption() {}
+
+  int get_params(optional_yield y);
+  int verify_permission(optional_yield y) override;
+  void execute(optional_yield y) override;
+  const char* name() const override { return "delete_bucket_encryption"; }
+  RGWOpType get_type() override { return RGW_OP_DELETE_BUCKET_ENCRYPTION; }
+  uint32_t op_mask() override { return RGW_OP_TYPE_WRITE; }
+};
+
 class RGWGetRequestPayment : public RGWOp {
 protected:
   bool requester_pays;
index f25cbcf265e4701b85b6ea570dcc9cf954e996e0..a463b51e02a514b3b070b802524aefb10b8dfaf4 100644 (file)
@@ -33,6 +33,9 @@ enum RGWOpType {
   RGW_OP_PUT_CORS,
   RGW_OP_DELETE_CORS,
   RGW_OP_OPTIONS_CORS,
+  RGW_OP_GET_BUCKET_ENCRYPTION,
+  RGW_OP_PUT_BUCKET_ENCRYPTION,
+  RGW_OP_DELETE_BUCKET_ENCRYPTION,
   RGW_OP_GET_REQUEST_PAYMENT,
   RGW_OP_SET_REQUEST_PAYMENT,
   RGW_OP_INIT_MULTIPART,
index 6849fb443cb9de57ee2736edc9dee307001a94af..db4ceb14bad6352b7ed3aa2139e6efa22446912f 100644 (file)
@@ -399,6 +399,24 @@ public:
   ~RGWOptionsCORS_ObjStore() override {}
 };
 
+class RGWGetBucketEncryption_ObjStore : public RGWGetBucketEncryption {
+public:
+  RGWGetBucketEncryption_ObjStore() {}
+  ~RGWGetBucketEncryption_ObjStore() override {}
+};
+
+class RGWPutBucketEncryption_ObjStore : public RGWPutBucketEncryption {
+public:
+  RGWPutBucketEncryption_ObjStore() {}
+  ~RGWPutBucketEncryption_ObjStore() override {}
+};
+
+class RGWDeleteBucketEncryption_ObjStore : public RGWDeleteBucketEncryption {
+public:
+  RGWDeleteBucketEncryption_ObjStore() {}
+  ~RGWDeleteBucketEncryption_ObjStore() override {}
+};
+
 class RGWInitMultipart_ObjStore : public RGWInitMultipart {
 public:
   RGWInitMultipart_ObjStore() {}
index ae57ae78904466aaf45db7254bda79c88a37f7df..f54e89c6c9f468887a9d518182d11c09325ce3c1 100644 (file)
@@ -3546,6 +3546,45 @@ void RGWOptionsCORS_ObjStore_S3::send_response()
   end_header(s, NULL);
 }
 
+void RGWPutBucketEncryption_ObjStore_S3::send_response()
+{
+  if (op_ret) {
+    set_req_state_err(s, op_ret);
+  }
+  dump_errno(s);
+  end_header(s);
+}
+
+void RGWGetBucketEncryption_ObjStore_S3::send_response()
+{
+  if (op_ret) {
+    if (op_ret == -ENOENT)
+      set_req_state_err(s, ERR_NO_SUCH_BUCKET_ENCRYPTION_CONFIGURATION);
+    else
+      set_req_state_err(s, op_ret);
+  }
+
+  dump_errno(s);
+  end_header(s, this, "application/xml");
+  dump_start(s);
+
+  if (!op_ret) {
+    encode_xml("ServerSideEncryptionConfiguration", bucket_encryption_conf, s->formatter);
+    rgw_flush_formatter_and_reset(s, s->formatter);
+  }
+}
+
+void RGWDeleteBucketEncryption_ObjStore_S3::send_response()
+{
+  if (op_ret == 0) {
+    op_ret = STATUS_NO_CONTENT;
+  }
+
+  set_req_state_err(s, op_ret);
+  dump_errno(s);
+  end_header(s);
+}
+
 void RGWGetRequestPayment_ObjStore_S3::send_response()
 {
   dump_errno(s);
@@ -4342,6 +4381,8 @@ RGWOp *RGWHandler_REST_Bucket_S3::op_get()
     return new RGWGetBucketPolicyStatus_ObjStore_S3;
   } else if (is_block_public_access_op()) {
     return new RGWGetBucketPublicAccessBlock_ObjStore_S3;
+  } else if (is_bucket_encryption_op()) {
+    return new RGWGetBucketEncryption_ObjStore_S3;
   }
   return get_obj_op(true);
 }
@@ -4395,6 +4436,8 @@ RGWOp *RGWHandler_REST_Bucket_S3::op_put()
     return new RGWPutBucketReplication_ObjStore_S3;
   } else if (is_block_public_access_op()) {
     return new RGWPutBucketPublicAccessBlock_ObjStore_S3;
+  } else if (is_bucket_encryption_op()) {
+    return new RGWPutBucketEncryption_ObjStore_S3;
   }
   return new RGWCreateBucket_ObjStore_S3;
 }
@@ -4419,6 +4462,8 @@ RGWOp *RGWHandler_REST_Bucket_S3::op_delete()
     return new RGWDeleteBucketReplication_ObjStore_S3;
   } else if (is_block_public_access_op()) {
     return new RGWDeleteBucketPublicAccessBlock;
+  } else if (is_bucket_encryption_op()) {
+    return new RGWDeleteBucketEncryption_ObjStore_S3;
   }
 
   if (s->info.args.sub_resource_exists("website")) {
@@ -5467,6 +5512,9 @@ AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s,
         case RGW_OP_PUT_OBJ:
         case RGW_OP_PUT_ACLS:
         case RGW_OP_PUT_CORS:
+        case RGW_OP_PUT_BUCKET_ENCRYPTION:
+        case RGW_OP_GET_BUCKET_ENCRYPTION:
+        case RGW_OP_DELETE_BUCKET_ENCRYPTION:
         case RGW_OP_INIT_MULTIPART: // in case that Init Multipart uses CHUNK encoding
         case RGW_OP_COMPLETE_MULTIPART:
         case RGW_OP_SET_BUCKET_VERSIONING:
index c908a9f441dc7a964a94c721fae6740e4a80c735..cc562ece807ea4c742e5aed6aef172a5d512185d 100644 (file)
@@ -420,6 +420,30 @@ public:
   void send_response() override;
 };
 
+class RGWGetBucketEncryption_ObjStore_S3 : public RGWGetBucketEncryption_ObjStore {
+public:
+  RGWGetBucketEncryption_ObjStore_S3() {}
+  ~RGWGetBucketEncryption_ObjStore_S3() override {}
+
+  void send_response() override;
+};
+
+class RGWPutBucketEncryption_ObjStore_S3 : public RGWPutBucketEncryption_ObjStore {
+public:
+  RGWPutBucketEncryption_ObjStore_S3() {}
+  ~RGWPutBucketEncryption_ObjStore_S3() override {}
+
+  void send_response() override;
+};
+
+class RGWDeleteBucketEncryption_ObjStore_S3 : public RGWDeleteBucketEncryption_ObjStore {
+public:
+  RGWDeleteBucketEncryption_ObjStore_S3() {}
+  ~RGWDeleteBucketEncryption_ObjStore_S3() override {}
+
+  void send_response() override;
+};
+
 class RGWGetRequestPayment_ObjStore_S3 : public RGWGetRequestPayment {
 public:
   RGWGetRequestPayment_ObjStore_S3() {}
@@ -700,6 +724,9 @@ protected:
   bool is_block_public_access_op() {
     return s->info.args.exists("publicAccessBlock");
   }
+  bool is_bucket_encryption_op() {
+    return s->info.args.exists("encryption");
+  }
 
   RGWOp *get_obj_op(bool get_data) const;
   RGWOp *op_get() override;
index 40d374f3566f5dd8b86d8633dc107a311e5ef193..1b450af1c62e5b819ddb292451d0bc1ae3fab17d 100644 (file)
@@ -56,6 +56,7 @@ using rgw::IAM::s3GetBucketNotification;
 using rgw::IAM::s3GetBucketPolicy;
 using rgw::IAM::s3GetBucketPolicyStatus;
 using rgw::IAM::s3GetBucketPublicAccessBlock;
+using rgw::IAM::s3GetBucketEncryption;
 using rgw::IAM::s3GetBucketRequestPayment;
 using rgw::IAM::s3GetBucketTagging;
 using rgw::IAM::s3GetBucketVersioning;
@@ -386,6 +387,7 @@ TEST_F(PolicyTest, Parse3) {
   act2[s3GetBucketPolicyStatus] = 1;
   act2[s3GetBucketPublicAccessBlock] = 1;
   act2[s3GetPublicAccessBlock] = 1;
+  act2[s3GetBucketEncryption] = 1;
 
   EXPECT_EQ(p->statements[2].action, act2);
   EXPECT_EQ(p->statements[2].notaction, None);
@@ -455,6 +457,7 @@ TEST_F(PolicyTest, Eval3) {
   s3allow[s3GetBucketPolicyStatus] = 1;
   s3allow[s3GetBucketPublicAccessBlock] = 1;
   s3allow[s3GetPublicAccessBlock] = 1;
+  s3allow[s3GetBucketEncryption] = 1;
 
   EXPECT_EQ(p.eval(em, none, s3PutBucketPolicy,
                   ARN(Partition::aws, Service::s3,