]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: Adding SSE-S3 support in GET and PUT paths (using Vault as KMS)
authorPriya Sehgal <priya.sehgal@flipkart.com>
Wed, 28 Jul 2021 13:48:59 +0000 (19:18 +0530)
committerMarcus Watts <mwatts@redhat.com>
Tue, 19 Apr 2022 21:35:39 +0000 (17:35 -0400)
Added the support to generate KEK based on bucket owner UID in
PutBucketEncryption. This is stored in bucket x-attrs. The KEK-ID is
later used in GET and PUT paths.
In the PUT path, we check if BucketEncryption is enabled for the bucket.
If yes, we detemine if the encryption type is AES256 (i.e., SSE-S3),
then we fetch the KEK-ID from the bucket x-attrs and use it to wrap the
data key. Thereafter, we call generate-data-key. We store the KEK-ID
and the wrapped data-key in the object x-attrs.
In the GET path, we simply pull out the KEK-ID from the object x-attr
and decrypt the object.

Signed-off-by: Priya Sehgal <priya.sehgal@flipkart.com>
src/rgw/rgw_crypt.cc
src/rgw/rgw_kms.cc
src/rgw/rgw_kms.h
src/rgw/rgw_op.cc

index b977e77cecb721e786899bd2aa2a4817129053af..fb6ea9956b279a5b6f597c72a9ce516d99b0388d 100644 (file)
@@ -16,6 +16,7 @@
 #include "crypto/crypto_accel.h"
 #include "crypto/crypto_plugin.h"
 #include "rgw/rgw_kms.h"
+#include "rgw/rgw_bucket_encryption.h"
 #include "rapidjson/document.h"
 #include "rapidjson/writer.h"
 #include "rapidjson/error/error.h"
@@ -912,6 +913,7 @@ int rgw_s3_prepare_encrypt(struct req_state* s,
 {
   int res = 0;
   crypt_http_responses.clear();
+
   {
     std::string_view req_sse_ca =
         get_crypt_attribute(s->info.env, parts, X_AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM);
@@ -1100,6 +1102,78 @@ int rgw_s3_prepare_encrypt(struct req_state* s,
       }
     }
 
+    /* Checking bucket attributes if SSE is enabled. Currently only supporting SSE-S3 */
+    rgw::sal::Attrs buck_attrs(s->bucket_attrs);
+    auto aiter = buck_attrs.find(RGW_ATTR_BUCKET_ENCRYPTION_POLICY);
+    if (aiter != buck_attrs.end()) {
+      ldpp_dout(s, 5) << "Found RGW_ATTR_BUCKET_ENCRYPTION_POLICY on "
+             << s->bucket_name << dendl;
+
+      bufferlist::const_iterator iter{&aiter->second};
+      try {
+        RGWBucketEncryptionConfig bucket_encryption_conf;
+       bucket_encryption_conf.decode(iter);
+       if (bucket_encryption_conf.sse_algorithm() == "AES256") {
+         ldpp_dout(s, 5) << "RGW_ATTR_BUCKET_ENCRYPTION ALGO: "
+                 <<  bucket_encryption_conf.sse_algorithm() << dendl;
+         std::string_view context = "";
+          std::string cooked_context;
+         if ((res = make_canonical_context(s, context, cooked_context)))
+           return res;
+
+         /* Find the KEK ID */
+         auto kek_iter = buck_attrs.find(RGW_ATTR_BUCKET_ENCRYPTION_KEY_ID);
+         if (kek_iter == buck_attrs.end()) {
+           ldpp_dout(s, 5) << "ERROR: KEK ID absent for bucket having "
+                   "encryption enabled" << dendl;
+           s->err.message = "Server side error - SSE-S3 key absent";
+           return -EINVAL;
+         }
+
+         std::string_view key_id = kek_iter->second.to_str();
+         ldpp_dout(s, 5) << "Found KEK ID: " << key_id << dendl;
+         std::string key_selector = create_random_key_selector(s->cct);
+
+         set_attr(attrs, RGW_ATTR_CRYPT_KEYSEL, key_selector);
+         set_attr(attrs, RGW_ATTR_CRYPT_CONTEXT, cooked_context);
+         set_attr(attrs, RGW_ATTR_CRYPT_MODE, "AES256");
+         set_attr(attrs, RGW_ATTR_CRYPT_KEYID, key_id);
+         std::string actual_key;
+         res = make_actual_key_from_sse_s3(s->cct, attrs, actual_key);
+          if (res != 0) {
+           ldpp_dout(s, 5) << "ERROR: failed to retrieve actual key from key_id: " << key_id << dendl;
+           s->err.message = "Failed to retrieve the actual key " ;
+           return res;
+         }
+         if (actual_key.size() != AES_256_KEYSIZE) {
+           ldpp_dout(s, 5) << "ERROR: key obtained from key_id:" <<
+                           key_id << " is not 256 bit size" << dendl;
+           s->err.message = "SSE-S3 provided an invalid key for the given keyid.";
+           return -ERR_INVALID_ACCESS_KEY;
+         }
+
+          if (block_crypt) {
+           auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
+           aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE);
+           *block_crypt = std::move(aes);
+         }
+          ::ceph::crypto::zeroize_for_security(actual_key.data(), actual_key.length());
+
+         crypt_http_responses["x-amz-server-side-encryption"] = "AES256";
+
+         return 0;
+       } else {
+          ldpp_dout(s, 5) << "ERROR: SSE algorithm: "
+                 <<  bucket_encryption_conf.sse_algorithm()
+                 << " not supported" << dendl;
+         s->err.message = "The requested encryption algorithm is not valid, must be AES256.";
+         return -ERR_INVALID_ENCRYPTION_ALGORITHM;
+       }
+      } catch (const buffer::error& e) {
+        ldpp_dout(s, 5) << __func__ <<  "decode bucket_encryption_conf failed" << dendl;
+      }
+    }
+
     /* no other encryption mode, check if default encryption is selected */
     if (s->cct->_conf->rgw_crypt_default_encryption_key != "") {
       std::string master_encryption_key;
@@ -1312,6 +1386,40 @@ int rgw_s3_prepare_decrypt(struct req_state* s,
     if (block_crypt) *block_crypt = std::move(aes);
     return 0;
   }
+
+  /* SSE-S3 */
+  if (stored_mode == "AES256") {
+    if (s->cct->_conf->rgw_crypt_require_ssl &&
+        !rgw_transport_is_secure(s->cct, *s->info.env)) {
+      ldpp_dout(s, 5) << "ERROR: Insecure request, rgw_crypt_require_ssl is set" << dendl;
+      return -ERR_INVALID_REQUEST;
+    }
+    /* try to retrieve actual key */
+    std::string key_id = get_str_attribute(attrs, RGW_ATTR_CRYPT_KEYID);
+    std::string actual_key;
+    res = reconstitute_actual_key_from_sse_s3(s->cct, attrs, actual_key);
+    if (res != 0) {
+      ldpp_dout(s, 10) << "ERROR: failed to retrieve actual key  " << dendl;
+      s->err.message = "Failed to retrieve the actual key " ;
+      return res;
+    }
+    if (actual_key.size() != AES_256_KEYSIZE) {
+      ldpp_dout(s, 0) << "ERROR: key obtained " <<
+          " is not 256 bit size" << dendl;
+      s->err.message = "SSE-S3  provided an invalid key for the given keyid.";
+      return -ERR_INVALID_ACCESS_KEY;
+    }
+
+    auto aes = std::unique_ptr<AES_256_CBC>(new AES_256_CBC(s->cct));
+    aes->set_key(reinterpret_cast<const uint8_t*>(actual_key.c_str()), AES_256_KEYSIZE);
+    actual_key.replace(0, actual_key.length(), actual_key.length(), '\000');
+    if (block_crypt) *block_crypt = std::move(aes);
+
+    crypt_http_responses["x-amz-server-side-encryption"] = "AES256";
+    return 0;
+  }
+
+
   /*no decryption*/
   return 0;
 }
index 0195d8764fb08e9fd55ff0fb3456eaaaf1c84fed..86975c79449414118d9b4b8ae1f89e3ce30eb791 100644 (file)
@@ -700,6 +700,16 @@ public:
     }
     return 0;
   }
+
+  int make_kek_s3(std::string key_id)
+  {
+    bufferlist secret_bl;
+    int res = send_request("POST", "/keys/", key_id,
+       string{}, secret_bl);
+
+    ldout(cct, 20) << "Generate KEK Response: " << res << dendl;
+    return res;
+  }
 };
 
 class KvSecretEngine: public VaultSecretEngine {
@@ -1276,3 +1286,30 @@ int remove_ss3_s3_bucket_key(const DoutPrefixProvider *dpp,
     return -EINVAL;
   }
 }
+
+int generate_kek_sse_s3(CephContext *cct, string kek_id)
+{
+  SseS3Context kctx { cct };
+  std::string kms_backend { kctx.backend() };
+  if (RGW_SSE_KMS_BACKEND_VAULT != kms_backend) {
+    ldout(cct, 0) << "ERROR: Unsupported rgw_crypt_s3_backend: " << kms_backend << dendl;
+    return -EINVAL;
+  }
+
+  std::string secret_engine_str = kctx.secret_engine();
+  EngineParmMap secret_engine_parms;
+  auto secret_engine { config_to_engine_and_parms(
+    cct, "rgw_crypt_vault_secret_engine",
+    secret_engine_str, secret_engine_parms) };
+  ldout(cct, 20) << "Vault authentication method: " << kctx.auth() << dendl;
+  ldout(cct, 20) << "Vault Secrets Engine: " << secret_engine << dendl;
+
+  if (RGW_SSE_KMS_VAULT_SE_TRANSIT == secret_engine){
+    TransitSecretEngine engine(cct, kctx, std::move(secret_engine_parms));
+    return engine.make_kek_s3(kek_id);
+  } else {
+    ldout(cct, 0) << "Missing or invalid/unsupported secret engine" << dendl;
+    return -EINVAL;
+  }
+
+}
index ba9b436139ed76be67eaaa1904975b371d47279f..fdb35382890f1ae9a698bd29fe627daba997d688 100644 (file)
@@ -45,6 +45,7 @@ int make_actual_key_from_sse_s3(const DoutPrefixProvider *dpp, CephContext *cct,
 int reconstitute_actual_key_from_sse_s3(const DoutPrefixProvider *dpp, CephContext *cct,
                             std::map<std::string, bufferlist>& attrs,
                             std::string& actual_key);
+int generate_kek_sse_s3(CephContext *cct, string kek_id);
 
 int create_sse_s3_bucket_key(const DoutPrefixProvider *dpp, CephContext *cct,
                             const std::string& actual_key);
index 961d3aec8e0e2a4e91a95548421cfe75656e03d7..22c44671637730291ab9e860a0b4b4c375ce5974 100644 (file)
@@ -52,6 +52,7 @@
 #include "rgw_notify_event_type.h"
 #include "rgw_sal.h"
 #include "rgw_sal_rados.h"
+#include "rgw_kms.h"
 
 #include "services/svc_zone.h"
 #include "services/svc_quota.h"
@@ -8687,6 +8688,14 @@ void RGWPutBucketEncryption::execute(optional_yield y)
   string bucket_owner_id = s->bucket->get_info().owner.id;
   key_id_bl.append(bucket_owner_id.c_str(), bucket_owner_id.size() + 1);
 
+  /* Generating KEK on the vault */
+  ldpp_dout(this, 5) << "Generating KEK: " << bucket_owner_id << dendl;
+  op_ret = generate_kek_sse_s3(s->cct, bucket_owner_id);
+  if (op_ret < 0) {
+    ldpp_dout(this, 20) << "Generate KEK returned =" << op_ret << dendl;
+    return;
+  }
+
   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] {