diff --git a/doc/radosgw/encryption.rst b/doc/radosgw/encryption.rst index 07fd60ac5d1..2b51e088f53 100644 --- a/doc/radosgw/encryption.rst +++ b/doc/radosgw/encryption.rst @@ -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 diff --git a/doc/radosgw/s3/authentication.rst b/doc/radosgw/s3/authentication.rst index 10143290d3c..64747cde276 100644 --- a/doc/radosgw/s3/authentication.rst +++ b/doc/radosgw/s3/authentication.rst @@ -185,6 +185,8 @@ Internally, S3 operations are mapped to ACL permissions thus: +---------------------------------------+---------------+ | ``s3:GetReplicationConfiguration`` | ``READ_ACP`` | +---------------------------------------+---------------+ +| ``s3:GetBucketEncryption`` | ``READ_ACP`` | ++---------------------------------------+---------------+ | ``s3:DeleteBucketPolicy`` | ``WRITE_ACP`` | +---------------------------------------+---------------+ | ``s3:DeleteBucketWebsite`` | ``WRITE_ACP`` | @@ -219,6 +221,8 @@ Internally, S3 operations are mapped to ACL permissions thus: +---------------------------------------+---------------+ | ``s3:PutReplicationConfiguration`` | ``WRITE_ACP`` | +---------------------------------------+---------------+ +| ``s3:PutBucketEncryption`` | ``WRITE_ACP`` | ++---------------------------------------+---------------+ Some mappings, (e.g. ``s3:CreateBucket`` to ``WRITE``) are not applicable to S3 operation, but are required to allow Swift and S3 to diff --git a/src/rgw/CMakeLists.txt b/src/rgw/CMakeLists.txt index 73364c3aedb..8f8c3d25362 100644 --- a/src/rgw/CMakeLists.txt +++ b/src/rgw/CMakeLists.txt @@ -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) diff --git a/src/rgw/rgw_auth_s3.cc b/src/rgw/rgw_auth_s3.cc index b6988be5a63..b253c93f89f 100644 --- a/src/rgw/rgw_auth_s3.cc +++ b/src/rgw/rgw_auth_s3.cc @@ -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 index 00000000000..0310e5ac5a8 --- /dev/null +++ b/src/rgw/rgw_bucket_encryption.cc @@ -0,0 +1,31 @@ +// -*- 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); +} + +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 index 00000000000..9df9ed527ea --- /dev/null +++ b/src/rgw/rgw_bucket_encryption.h @@ -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 + +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) diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 9da2678fb20..f099590b72e 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -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({ diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index 2be5b1db1d9..f4ac4c2795d 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -143,6 +143,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 +235,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 diff --git a/src/rgw/rgw_iam_policy.cc b/src/rgw/rgw_iam_policy.cc index 48780c2efbb..686601d3ac7 100644 --- a/src/rgw/rgw_iam_policy.cc +++ b/src/rgw/rgw_iam_policy.cc @@ -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"; diff --git a/src/rgw/rgw_iam_policy.h b/src/rgw/rgw_iam_policy.h index 808c2296c94..d609716c43b 100644 --- a/src/rgw/rgw_iam_policy.h +++ b/src/rgw/rgw_iam_policy.h @@ -107,7 +107,9 @@ 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 s3All = 70; static constexpr std::uint64_t iamPutUserPolicy = s3All + 1; static constexpr std::uint64_t iamGetUserPolicy = s3All + 2; @@ -197,6 +199,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 +223,7 @@ inline int op_to_perm(std::uint64_t op) { case s3PutAccelerateConfiguration: case s3PutBucketAcl: case s3PutBucketCORS: + case s3PutBucketEncryption: case s3PutBucketLogging: case s3PutBucketNotification: case s3PutBucketPolicy: diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 25b2afa4435..46aea80d9e1 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -8551,3 +8551,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; + }); +} diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index f77e16d01f7..156d0c541b0 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -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; diff --git a/src/rgw/rgw_op_type.h b/src/rgw/rgw_op_type.h index f25cbcf265e..a463b51e02a 100644 --- a/src/rgw/rgw_op_type.h +++ b/src/rgw/rgw_op_type.h @@ -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, diff --git a/src/rgw/rgw_rest.h b/src/rgw/rgw_rest.h index 6849fb443cb..db4ceb14bad 100644 --- a/src/rgw/rgw_rest.h +++ b/src/rgw/rgw_rest.h @@ -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() {} diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 6ccc87855b8..316b36ef2f4 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -3549,6 +3549,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); @@ -4345,6 +4384,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); } @@ -4398,6 +4439,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; } @@ -4422,6 +4465,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")) { @@ -5470,6 +5515,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: diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index c908a9f441d..cc562ece807 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -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; diff --git a/src/test/rgw/test_rgw_iam_policy.cc b/src/test/rgw/test_rgw_iam_policy.cc index 40d374f3566..1b450af1c62 100644 --- a/src/test/rgw/test_rgw_iam_policy.cc +++ b/src/test/rgw/test_rgw_iam_policy.cc @@ -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,