github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/c-deps/libroach/ccl/db.cc (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Licensed as a CockroachDB Enterprise file under the Cockroach Community
     4  // License (the "License"); you may not use this file except in compliance with
     5  // the License. You may obtain a copy of the License at
     6  //
     7  //     https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt
     8  
     9  #include "db.h"
    10  #include <iostream>
    11  #include <libroachccl.h>
    12  #include <memory>
    13  #include <rocksdb/comparator.h>
    14  #include <rocksdb/iterator.h>
    15  #include <rocksdb/utilities/write_batch_with_index.h>
    16  #include <rocksdb/write_batch.h>
    17  #include "../batch.h"
    18  #include "../comparator.h"
    19  #include "../encoding.h"
    20  #include "../env_manager.h"
    21  #include "../options.h"
    22  #include "../rocksdbutils/env_encryption.h"
    23  #include "../status.h"
    24  #include "ccl/baseccl/encryption_options.pb.h"
    25  #include "ccl/storageccl/engineccl/enginepbccl/stats.pb.h"
    26  #include "crypto_utils.h"
    27  #include "ctr_stream.h"
    28  #include "key_manager.h"
    29  
    30  using namespace cockroach;
    31  
    32  namespace cockroach {
    33  
    34  class CCLEnvStatsHandler : public EnvStatsHandler {
    35   public:
    36    explicit CCLEnvStatsHandler(DataKeyManager* data_key_manager)
    37        : data_key_manager_(data_key_manager) {}
    38    virtual ~CCLEnvStatsHandler() {}
    39  
    40    virtual rocksdb::Status GetEncryptionStats(std::string* serialized_stats) override {
    41      if (data_key_manager_ == nullptr) {
    42        return rocksdb::Status::OK();
    43      }
    44  
    45      enginepbccl::EncryptionStatus enc_status;
    46  
    47      // GetActiveStoreKeyInfo returns a unique_ptr containing a copy. Transfer ownership from the
    48      // unique_ptr to the proto. set_allocated_active_store deletes the existing field, if any.
    49      enc_status.set_allocated_active_store_key(data_key_manager_->GetActiveStoreKeyInfo().release());
    50  
    51      // CurrentKeyInfo returns a unique_ptr containing a copy. Transfer ownership from the
    52      // unique_ptr to the proto. set_allocated_active_store deletes the existing field, if any.
    53      enc_status.set_allocated_active_data_key(data_key_manager_->CurrentKeyInfo().release());
    54  
    55      if (!enc_status.SerializeToString(serialized_stats)) {
    56        return rocksdb::Status::InvalidArgument("failed to serialize encryption status");
    57      }
    58      return rocksdb::Status::OK();
    59    }
    60  
    61    virtual rocksdb::Status GetEncryptionRegistry(std::string* serialized_registry) override {
    62      if (data_key_manager_ == nullptr) {
    63        return rocksdb::Status::OK();
    64      }
    65  
    66      auto key_registry = data_key_manager_->GetScrubbedRegistry();
    67      if (key_registry == nullptr) {
    68        return rocksdb::Status::OK();
    69      }
    70  
    71      if (!key_registry->SerializeToString(serialized_registry)) {
    72        return rocksdb::Status::InvalidArgument("failed to serialize data keys registry");
    73      }
    74  
    75      return rocksdb::Status::OK();
    76    }
    77  
    78    virtual std::string GetActiveDataKeyID() override {
    79      // Look up the current data key.
    80      if (data_key_manager_ == nullptr) {
    81        // No data key manager: plaintext.
    82        return kPlainKeyID;
    83      }
    84  
    85      auto active_key_info = data_key_manager_->CurrentKeyInfo();
    86      if (active_key_info == nullptr) {
    87        // Plaintext.
    88        return kPlainKeyID;
    89      }
    90      return active_key_info->key_id();
    91    }
    92  
    93    virtual int32_t GetActiveStoreKeyType() override {
    94      if (data_key_manager_ == nullptr) {
    95        return enginepbccl::Plaintext;
    96      }
    97  
    98      auto store_key_info = data_key_manager_->GetActiveStoreKeyInfo();
    99      if (store_key_info == nullptr) {
   100        return enginepbccl::Plaintext;
   101      }
   102  
   103      return store_key_info->encryption_type();
   104    }
   105  
   106    virtual rocksdb::Status GetFileEntryKeyID(const enginepb::FileEntry* entry,
   107                                              std::string* id) override {
   108      if (entry == nullptr) {
   109        // No file entry: file written in plaintext before the file registry was used.
   110        *id = kPlainKeyID;
   111        return rocksdb::Status::OK();
   112      }
   113  
   114      enginepbccl::EncryptionSettings enc_settings;
   115      if (!enc_settings.ParseFromString(entry->encryption_settings())) {
   116        return rocksdb::Status::InvalidArgument("failed to parse encryption settings");
   117      }
   118  
   119      if (enc_settings.encryption_type() == enginepbccl::Plaintext) {
   120        *id = kPlainKeyID;
   121      } else {
   122        *id = enc_settings.key_id();
   123      }
   124  
   125      return rocksdb::Status::OK();
   126    }
   127  
   128   private:
   129    // The DataKeyManager is needed to get key information but is not owned by the StatsHandler.
   130    DataKeyManager* data_key_manager_;
   131  };
   132  
   133  // DBOpenHookCCL parses the extra_options field of DBOptions and initializes
   134  // encryption objects if needed.
   135  rocksdb::Status DBOpenHookCCL(std::shared_ptr<rocksdb::Logger> info_log, const std::string& db_dir,
   136                                const DBOptions db_opts, EnvManager* env_mgr) {
   137    DBSlice options = db_opts.extra_options;
   138    if (options.len == 0) {
   139      return rocksdb::Status::OK();
   140    }
   141  
   142    // The Go code sets the "file_registry" storage version if we specified encryption flags,
   143    // but let's double check anyway.
   144    if (!db_opts.use_file_registry) {
   145      return rocksdb::Status::InvalidArgument(
   146          "on-disk version does not support encryption, but we found encryption flags");
   147    }
   148  
   149    // We log to the primary CockroachDB log for encryption status
   150    // instead of the RocksDB specific log. This should only be used to
   151    // occasional logging (eg: key loading and rotation).
   152    std::shared_ptr<rocksdb::Logger> logger(NewDBLogger(true /* use_primary_log */));
   153  
   154    // We have encryption options. Check whether the AES instruction set is supported.
   155    if (!UsesAESNI()) {
   156      rocksdb::Warn(
   157          logger, "*** WARNING*** Encryption requested, but no AES instruction set detected: expect "
   158                  "significant performance degradation!");
   159    }
   160  
   161    // Attempt to disable core dumps.
   162    auto status = DisableCoreFile();
   163    if (!status.ok()) {
   164      rocksdb::Warn(logger,
   165                    "*** WARNING*** Encryption requested, but could not disable core dumps: %s. Keys "
   166                    "may be leaked in core dumps!",
   167                    status.getState());
   168    }
   169  
   170    // Parse extra_options.
   171    cockroach::ccl::baseccl::EncryptionOptions opts;
   172    if (!opts.ParseFromArray(options.data, options.len)) {
   173      return rocksdb::Status::InvalidArgument("failed to parse extra options");
   174    }
   175  
   176    if (opts.key_source() != cockroach::ccl::baseccl::KeyFiles) {
   177      return rocksdb::Status::InvalidArgument("unknown encryption key source");
   178    }
   179  
   180    // Initialize store key manager.
   181    // NOTE: FileKeyManager uses the default env as the MemEnv can never have pre-populated files.
   182    FileKeyManager* store_key_manager = new FileKeyManager(
   183        rocksdb::Env::Default(), logger, opts.key_files().current_key(), opts.key_files().old_key());
   184    status = store_key_manager->LoadKeys();
   185    if (!status.ok()) {
   186      delete store_key_manager;
   187      return status;
   188    }
   189  
   190    // Create a cipher stream creator using the store_key_manager.
   191    auto store_stream = new CTRCipherStreamCreator(store_key_manager, enginepb::Store);
   192  
   193    // Construct an EncryptedEnv using this stream creator and the base_env (Default or Mem).
   194    // It takes ownership of the stream.
   195    rocksdb::Env* store_keyed_env =
   196        rocksdb_utils::NewEncryptedEnv(env_mgr->base_env, env_mgr->file_registry.get(), store_stream);
   197    // Transfer ownership to the env manager.
   198    env_mgr->TakeEnvOwnership(store_keyed_env);
   199  
   200    // Initialize data key manager using the stored-keyed-env.
   201    DataKeyManager* data_key_manager = new DataKeyManager(
   202        store_keyed_env, logger, db_dir, opts.data_key_rotation_period(), db_opts.read_only);
   203    status = data_key_manager->LoadKeys();
   204    if (!status.ok()) {
   205      delete data_key_manager;
   206      return status;
   207    }
   208  
   209    // Create a cipher stream creator using the data_key_manager.
   210    auto data_stream = new CTRCipherStreamCreator(data_key_manager, enginepb::Data);
   211  
   212    // Construct an EncryptedEnv using this stream creator and the base_env (Default or Mem).
   213    // It takes ownership of the stream.
   214    rocksdb::Env* data_keyed_env =
   215        rocksdb_utils::NewEncryptedEnv(env_mgr->base_env, env_mgr->file_registry.get(), data_stream);
   216    // Transfer ownership to the env manager and make it as the db_env.
   217    env_mgr->TakeEnvOwnership(data_keyed_env);
   218    env_mgr->db_env = data_keyed_env;
   219  
   220    // Fetch the current store key info.
   221    std::unique_ptr<enginepbccl::KeyInfo> store_key = store_key_manager->CurrentKeyInfo();
   222    assert(store_key != nullptr);
   223  
   224    if (!db_opts.read_only) {
   225      // Generate a new data key if needed by giving the active store key info to the data key
   226      // manager.
   227      status = data_key_manager->SetActiveStoreKeyInfo(std::move(store_key));
   228      if (!status.ok()) {
   229        return status;
   230      }
   231    }
   232  
   233    // Everything's ok: initialize a stats handler.
   234    env_mgr->SetStatsHandler(new CCLEnvStatsHandler(data_key_manager));
   235  
   236    return rocksdb::Status::OK();
   237  }
   238  
   239  }  // namespace cockroach
   240  
   241  void* DBOpenHookCCL = (void*)cockroach::DBOpenHookCCL;
   242  
   243  DBStatus DBBatchReprVerify(DBSlice repr, DBKey start, DBKey end, int64_t now_nanos,
   244                             MVCCStatsResult* stats) {
   245    // TODO(dan): Inserting into a batch just to iterate over it is unfortunate.
   246    // Consider replacing this with WriteBatch's Iterate/Handler mechanism and
   247    // computing MVCC stats on the post-ApplyBatchRepr engine. splitTrigger does
   248    // the latter and it's a headache for propEvalKV, so wait to see how that
   249    // settles out before doing it that way.
   250    rocksdb::WriteBatchWithIndex batch(&kComparator, 0, true);
   251    rocksdb::WriteBatch b(ToString(repr));
   252    std::unique_ptr<rocksdb::WriteBatch::Handler> inserter(GetDBBatchInserter(&batch));
   253    rocksdb::Status status = b.Iterate(inserter.get());
   254    if (!status.ok()) {
   255      return ToDBStatus(status);
   256    }
   257    std::unique_ptr<rocksdb::Iterator> iter;
   258    iter.reset(batch.NewIteratorWithBase(rocksdb::NewEmptyIterator()));
   259  
   260    iter->SeekToFirst();
   261    if (iter->Valid() && kComparator.Compare(iter->key(), EncodeKey(start)) < 0) {
   262      return FmtStatus("key not in request range");
   263    }
   264    iter->SeekToLast();
   265    if (iter->Valid() && kComparator.Compare(iter->key(), EncodeKey(end)) >= 0) {
   266      return FmtStatus("key not in request range");
   267    }
   268  
   269    *stats = MVCCComputeStatsInternal(iter.get(), start, end, now_nanos);
   270  
   271    return kSuccess;
   272  }