FDB native KMS Connector Framework (#6846)

* FDB native KMS Connector Framework

Description

Major changes includes:
1. Framework code to enable FDB native KMS connector implementation.
2. SERVER_KNOBS->KMS_CONNECTOR_TYPE controls the connector type selection.
3. KmsConnectorInterface endpoint definitions, every KMSConnector
   implementation needs to support defined endpoints.
4. Update EncryptKeyProxy to leverage KmsConnectorInterface endpoints
   to fetch encryption keys on-demand and/or periodic refreshes.
   Integrate SimKmsConnector implementation.
5. Implement SimKmsConnector by leveraging existing SimKeyProxy
   implementation.

Testing

Unit test: fdbserver/SimKmsConnector
Simulation: EncryptKeyProxy
This commit is contained in:
Ata E Husain Bohra 2022-04-22 08:53:39 -07:00 committed by GitHub
parent 44c5f87ac9
commit 670d40ef79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 420 additions and 170 deletions

View File

@ -855,6 +855,10 @@ void ServerKnobs::initialize(Randomize randomize, ClientKnobs* clientKnobs, IsSi
init( ENCRYPTION_MODE, "AES-256-CTR"); init( ENCRYPTION_MODE, "AES-256-CTR");
init( SIM_KMS_MAX_KEYS, 4096); init( SIM_KMS_MAX_KEYS, 4096);
// Support KmsConnector types are:
// KMS_CONNECTOR_TYPE_HTTP -> 1
init( KMS_CONNECTOR_TYPE, "HttpKmsConnector");
// Blob granlues // Blob granlues
init( BG_URL, isSimulated ? "file://fdbblob/" : "" ); // TODO: store in system key space or something, eventually init( BG_URL, isSimulated ? "file://fdbblob/" : "" ); // TODO: store in system key space or something, eventually
init( BG_SNAPSHOT_FILE_TARGET_BYTES, 10000000 ); if( buggifySmallShards ) BG_SNAPSHOT_FILE_TARGET_BYTES = 100000; else if (simulationMediumShards || (randomize && BUGGIFY) ) BG_SNAPSHOT_FILE_TARGET_BYTES = 1000000; init( BG_SNAPSHOT_FILE_TARGET_BYTES, 10000000 ); if( buggifySmallShards ) BG_SNAPSHOT_FILE_TARGET_BYTES = 100000; else if (simulationMediumShards || (randomize && BUGGIFY) ) BG_SNAPSHOT_FILE_TARGET_BYTES = 1000000;

View File

@ -822,6 +822,9 @@ public:
std::string ENCRYPTION_MODE; std::string ENCRYPTION_MODE;
int SIM_KMS_MAX_KEYS; int SIM_KMS_MAX_KEYS;
// Key Management Service (KMS) Connector
std::string KMS_CONNECTOR_TYPE;
// blob granule stuff // blob granule stuff
// FIXME: configure url with database configuration instead of knob eventually // FIXME: configure url with database configuration instead of knob eventually
std::string BG_URL; std::string BG_URL;

View File

@ -55,6 +55,8 @@ set(FDBSERVER_SRCS
KeyValueStoreMemory.actor.cpp KeyValueStoreMemory.actor.cpp
KeyValueStoreRocksDB.actor.cpp KeyValueStoreRocksDB.actor.cpp
KeyValueStoreSQLite.actor.cpp KeyValueStoreSQLite.actor.cpp
KmsConnector.h
KmsConnectorInterface.h
KnobProtectiveGroups.cpp KnobProtectiveGroups.cpp
KnobProtectiveGroups.h KnobProtectiveGroups.h
Knobs.h Knobs.h
@ -134,8 +136,8 @@ set(FDBSERVER_SRCS
ServerDBInfo.actor.h ServerDBInfo.actor.h
ServerDBInfo.h ServerDBInfo.h
SigStack.cpp SigStack.cpp
SimEncryptKmsProxy.actor.cpp SimKmsConnector.actor.h
SimEncryptKmsProxy.actor.h SimKmsConnector.actor.cpp
SimpleConfigConsumer.actor.cpp SimpleConfigConsumer.actor.cpp
SimpleConfigConsumer.h SimpleConfigConsumer.h
SimulatedCluster.actor.cpp SimulatedCluster.actor.cpp

View File

@ -21,11 +21,15 @@
#include "fdbrpc/Locality.h" #include "fdbrpc/Locality.h"
#include "fdbrpc/Stats.h" #include "fdbrpc/Stats.h"
#include "fdbserver/EncryptKeyProxyInterface.h" #include "fdbserver/EncryptKeyProxyInterface.h"
#include "fdbserver/KmsConnector.h"
#include "fdbserver/KmsConnectorInterface.h"
#include "fdbserver/Knobs.h"
#include "fdbserver/ServerDBInfo.actor.h" #include "fdbserver/ServerDBInfo.actor.h"
#include "fdbserver/SimEncryptKmsProxy.actor.h" #include "fdbserver/SimKmsConnector.actor.h"
#include "fdbserver/WorkerInterface.actor.h" #include "fdbserver/WorkerInterface.actor.h"
#include "fdbserver/ServerDBInfo.h" #include "fdbserver/ServerDBInfo.h"
#include "flow/Arena.h" #include "flow/Arena.h"
#include "flow/EncryptUtils.h"
#include "flow/Error.h" #include "flow/Error.h"
#include "flow/EventTypes.actor.h" #include "flow/EventTypes.actor.h"
#include "flow/FastRef.h" #include "flow/FastRef.h"
@ -38,12 +42,10 @@
#include <boost/mpl/not.hpp> #include <boost/mpl/not.hpp>
#include <utility> #include <utility>
#include <memory>
#include "flow/actorcompiler.h" // This must be the last #include. #include "flow/actorcompiler.h" // This must be the last #include.
using EncryptDomainId = uint64_t;
using EncryptBaseCipherId = uint64_t;
namespace { namespace {
bool canReplyWith(Error e) { bool canReplyWith(Error e) {
switch (e.code()) { switch (e.code()) {
@ -57,16 +59,16 @@ bool canReplyWith(Error e) {
} // namespace } // namespace
struct EncryptBaseCipherKey { struct EncryptBaseCipherKey {
EncryptDomainId domainId; EncryptCipherDomainId domainId;
EncryptBaseCipherId baseCipherId; EncryptCipherBaseKeyId baseCipherId;
Standalone<StringRef> baseCipherKey; Standalone<StringRef> baseCipherKey;
uint64_t creationTimeSec; uint64_t creationTimeSec;
bool noExpiry; bool noExpiry;
EncryptBaseCipherKey() EncryptBaseCipherKey()
: domainId(0), baseCipherId(0), baseCipherKey(StringRef()), creationTimeSec(0), noExpiry(false) {} : domainId(0), baseCipherId(0), baseCipherKey(StringRef()), creationTimeSec(0), noExpiry(false) {}
explicit EncryptBaseCipherKey(EncryptDomainId dId, explicit EncryptBaseCipherKey(EncryptCipherDomainId dId,
EncryptBaseCipherId cipherId, EncryptCipherBaseKeyId cipherId,
StringRef cipherKey, StringRef cipherKey,
bool neverExpire) bool neverExpire)
: domainId(dId), baseCipherId(cipherId), baseCipherKey(cipherKey), creationTimeSec(now()), noExpiry(neverExpire) { : domainId(dId), baseCipherId(cipherId), baseCipherKey(cipherKey), creationTimeSec(now()), noExpiry(neverExpire) {
@ -75,8 +77,8 @@ struct EncryptBaseCipherKey {
bool isValid() { return noExpiry ? true : ((now() - creationTimeSec) < FLOW_KNOBS->ENCRYPT_CIPHER_KEY_CACHE_TTL); } bool isValid() { return noExpiry ? true : ((now() - creationTimeSec) < FLOW_KNOBS->ENCRYPT_CIPHER_KEY_CACHE_TTL); }
}; };
using EncryptBaseDomainIdCache = std::unordered_map<EncryptDomainId, EncryptBaseCipherKey>; using EncryptBaseDomainIdCache = std::unordered_map<EncryptCipherDomainId, EncryptBaseCipherKey>;
using EncryptBaseCipherKeyIdCache = std::unordered_map<EncryptBaseCipherId, EncryptBaseCipherKey>; using EncryptBaseCipherKeyIdCache = std::unordered_map<EncryptCipherBaseKeyId, EncryptBaseCipherKey>;
struct EncryptKeyProxyData : NonCopyable, ReferenceCounted<EncryptKeyProxyData> { struct EncryptKeyProxyData : NonCopyable, ReferenceCounted<EncryptKeyProxyData> {
public: public:
@ -87,6 +89,8 @@ public:
EncryptBaseDomainIdCache baseCipherDomainIdCache; EncryptBaseDomainIdCache baseCipherDomainIdCache;
EncryptBaseCipherKeyIdCache baseCipherKeyIdCache; EncryptBaseCipherKeyIdCache baseCipherKeyIdCache;
std::unique_ptr<KmsConnector> kmsConnector;
CounterCollection ekpCacheMetrics; CounterCollection ekpCacheMetrics;
Counter baseCipherKeyIdCacheMisses; Counter baseCipherKeyIdCacheMisses;
@ -107,8 +111,8 @@ public:
numResponseWithErrors("EKPNumResponseWithErrors", ekpCacheMetrics), numResponseWithErrors("EKPNumResponseWithErrors", ekpCacheMetrics),
numEncryptionKeyRefreshErrors("EKPNumEncryptionKeyRefreshErrors", ekpCacheMetrics) {} numEncryptionKeyRefreshErrors("EKPNumEncryptionKeyRefreshErrors", ekpCacheMetrics) {}
void insertIntoBaseDomainIdCache(const EncryptDomainId domainId, void insertIntoBaseDomainIdCache(const EncryptCipherDomainId domainId,
const EncryptBaseCipherId baseCipherId, const EncryptCipherBaseKeyId baseCipherId,
const StringRef baseCipherKey) { const StringRef baseCipherKey) {
// Entries in domainId cache are eligible for periodic refreshes to support 'limiting lifetime of encryption // Entries in domainId cache are eligible for periodic refreshes to support 'limiting lifetime of encryption
// key' support if enabled on external KMS solutions. // key' support if enabled on external KMS solutions.
@ -119,8 +123,8 @@ public:
insertIntoBaseCipherIdCache(domainId, baseCipherId, baseCipherKey); insertIntoBaseCipherIdCache(domainId, baseCipherId, baseCipherKey);
} }
void insertIntoBaseCipherIdCache(const EncryptDomainId domainId, void insertIntoBaseCipherIdCache(const EncryptCipherDomainId domainId,
const EncryptBaseCipherId baseCipherId, const EncryptCipherBaseKeyId baseCipherId,
const StringRef baseCipherKey) { const StringRef baseCipherKey) {
// Given an cipherKey is immutable, it is OK to NOT expire cached information. // Given an cipherKey is immutable, it is OK to NOT expire cached information.
// TODO: Update cache to support LRU eviction policy to limit the total cache size. // TODO: Update cache to support LRU eviction policy to limit the total cache size.
@ -150,19 +154,37 @@ public:
} }
}; };
struct pair_hash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2>& pair) const {
auto hash1 = std::hash<T1>{}(pair.first);
auto hash2 = std::hash<T2>{}(pair.second);
// Equal hashes XOR would be ZERO.
return hash1 == hash2 ? hash1 : hash1 ^ hash2;
}
};
ACTOR Future<Void> getCipherKeysByBaseCipherKeyIds(Reference<EncryptKeyProxyData> ekpProxyData, ACTOR Future<Void> getCipherKeysByBaseCipherKeyIds(Reference<EncryptKeyProxyData> ekpProxyData,
SimKmsProxyInterface simKmsInterface, KmsConnectorInterface kmsConnectorInf,
EKPGetBaseCipherKeysByIdsRequest req) { EKPGetBaseCipherKeysByIdsRequest req) {
// Scan the cached cipher-keys and filter our baseCipherIds locally cached // Scan the cached cipher-keys and filter our baseCipherIds locally cached
// for the rest, reachout to KMS to fetch the required details // for the rest, reachout to KMS to fetch the required details
std::vector<std::pair<EncryptBaseCipherId, EncryptDomainId>> lookupCipherIds; std::vector<std::pair<EncryptCipherBaseKeyId, EncryptCipherDomainId>> lookupCipherIds;
state std::vector<EKPBaseCipherDetails> cachedCipherDetails; state std::vector<EKPBaseCipherDetails> cachedCipherDetails;
state EKPGetBaseCipherKeysByIdsRequest keysByIds = req; state EKPGetBaseCipherKeysByIdsRequest keysByIds = req;
state EKPGetBaseCipherKeysByIdsReply keyIdsReply; state EKPGetBaseCipherKeysByIdsReply keyIdsReply;
// Dedup the requested pair<baseCipherId, encryptDomainId>
// TODO: endpoint serialization of std::unordered_set isn't working at the moment
std::unordered_set<std::pair<EncryptCipherBaseKeyId, EncryptCipherDomainId>, pair_hash> dedupedCipherIds;
for (const auto& item : req.baseCipherIds) { for (const auto& item : req.baseCipherIds) {
dedupedCipherIds.emplace(item);
}
for (const auto& item : dedupedCipherIds) {
const auto itr = ekpProxyData->baseCipherKeyIdCache.find(item.first); const auto itr = ekpProxyData->baseCipherKeyIdCache.find(item.first);
if (itr != ekpProxyData->baseCipherKeyIdCache.end()) { if (itr != ekpProxyData->baseCipherKeyIdCache.end()) {
ASSERT(itr->second.isValid()); ASSERT(itr->second.isValid());
@ -176,38 +198,32 @@ ACTOR Future<Void> getCipherKeysByBaseCipherKeyIds(Reference<EncryptKeyProxyData
ekpProxyData->baseCipherKeyIdCacheHits += cachedCipherDetails.size(); ekpProxyData->baseCipherKeyIdCacheHits += cachedCipherDetails.size();
ekpProxyData->baseCipherKeyIdCacheMisses += lookupCipherIds.size(); ekpProxyData->baseCipherKeyIdCacheMisses += lookupCipherIds.size();
if (g_network->isSimulated()) { if (!lookupCipherIds.empty()) {
if (!lookupCipherIds.empty()) { try {
try { KmsConnLookupEKsByKeyIdsReq keysByIdsReq(lookupCipherIds);
SimGetEncryptKeysByKeyIdsRequest simKeyIdsReq(lookupCipherIds); KmsConnLookupEKsByKeyIdsRep keysByIdsRep = wait(kmsConnectorInf.ekLookupByIds.getReply(keysByIdsReq));
SimGetEncryptKeysByKeyIdsReply simKeyIdsReply =
wait(simKmsInterface.encryptKeyLookupByKeyIds.getReply(simKeyIdsReq));
for (const auto& item : simKeyIdsReply.encryptKeyDetails) { for (const auto& item : keysByIdsRep.cipherKeyDetails) {
keyIdsReply.baseCipherDetails.emplace_back( keyIdsReply.baseCipherDetails.emplace_back(
item.encryptDomainId, item.encryptKeyId, item.encryptKey, keyIdsReply.arena); item.encryptDomainId, item.encryptKeyId, item.encryptKey, keyIdsReply.arena);
}
// Record the fetched cipher details to the local cache for the future references
// Note: cache warm-up is done after reponding to the caller
for (auto& item : simKeyIdsReply.encryptKeyDetails) {
// DomainId isn't available here, the caller must know the encryption domainId
ekpProxyData->insertIntoBaseCipherIdCache(item.encryptDomainId, item.encryptKeyId, item.encryptKey);
}
} catch (Error& e) {
if (!canReplyWith(e)) {
TraceEvent("GetCipherKeysByIds", ekpProxyData->myId).error(e);
throw;
}
TraceEvent("GetCipherKeysByIds", ekpProxyData->myId).detail("ErrorCode", e.code());
ekpProxyData->sendErrorResponse(keysByIds.reply, e);
return Void();
} }
// Record the fetched cipher details to the local cache for the future references
// Note: cache warm-up is done after reponding to the caller
for (auto& item : keysByIdsRep.cipherKeyDetails) {
// DomainId isn't available here, the caller must know the encryption domainId
ekpProxyData->insertIntoBaseCipherIdCache(item.encryptDomainId, item.encryptKeyId, item.encryptKey);
}
} catch (Error& e) {
if (!canReplyWith(e)) {
TraceEvent("GetCipherKeysByIds", ekpProxyData->myId).error(e);
throw;
}
TraceEvent("GetCipherKeysByIds", ekpProxyData->myId).detail("ErrorCode", e.code());
ekpProxyData->sendErrorResponse(keysByIds.reply, e);
return Void();
} }
} else {
// TODO: Call to non-FDB KMS connector process.
throw not_implemented();
} }
// Append cached cipherKeyDetails to the result-set // Append cached cipherKeyDetails to the result-set
@ -221,7 +237,7 @@ ACTOR Future<Void> getCipherKeysByBaseCipherKeyIds(Reference<EncryptKeyProxyData
} }
ACTOR Future<Void> getLatestCipherKeys(Reference<EncryptKeyProxyData> ekpProxyData, ACTOR Future<Void> getLatestCipherKeys(Reference<EncryptKeyProxyData> ekpProxyData,
SimKmsProxyInterface simKmsInterface, KmsConnectorInterface kmsConnectorInf,
EKPGetLatestBaseCipherKeysRequest req) { EKPGetLatestBaseCipherKeysRequest req) {
// Scan the cached cipher-keys and filter our baseCipherIds locally cached // Scan the cached cipher-keys and filter our baseCipherIds locally cached
// for the rest, reachout to KMS to fetch the required details // for the rest, reachout to KMS to fetch the required details
@ -231,49 +247,51 @@ ACTOR Future<Void> getLatestCipherKeys(Reference<EncryptKeyProxyData> ekpProxyDa
state EKPGetLatestBaseCipherKeysReply latestCipherReply; state EKPGetLatestBaseCipherKeysReply latestCipherReply;
state Arena& arena = latestCipherReply.arena; state Arena& arena = latestCipherReply.arena;
// Dedup the requested domainIds.
// TODO: endpoint serialization of std::unordered_set isn't working at the moment
std::unordered_set<EncryptCipherDomainId> dedupedDomainIds;
for (EncryptCipherDomainId id : req.encryptDomainIds) {
dedupedDomainIds.emplace(id);
}
// First, check if the requested information is already cached by the server. // First, check if the requested information is already cached by the server.
// Ensure the cached information is within FLOW_KNOBS->ENCRYPT_CIPHER_KEY_CACHE_TTL time window. // Ensure the cached information is within FLOW_KNOBS->ENCRYPT_CIPHER_KEY_CACHE_TTL time window.
std::vector<EncryptDomainId> lookupCipherDomains; std::vector<EncryptCipherDomainId> lookupCipherDomains;
for (EncryptDomainId id : req.encryptDomainIds) { for (EncryptCipherDomainId id : dedupedDomainIds) {
const auto itr = ekpProxyData->baseCipherDomainIdCache.find(id); const auto itr = ekpProxyData->baseCipherDomainIdCache.find(id);
if (itr != ekpProxyData->baseCipherDomainIdCache.end() && itr->second.isValid()) { if (itr != ekpProxyData->baseCipherDomainIdCache.end() && itr->second.isValid()) {
cachedCipherDetails.emplace_back(id, itr->second.baseCipherId, itr->second.baseCipherKey, arena); cachedCipherDetails.emplace_back(id, itr->second.baseCipherId, itr->second.baseCipherKey, arena);
} else { } else {
lookupCipherDomains.push_back(id); lookupCipherDomains.emplace_back(id);
} }
} }
ekpProxyData->baseCipherDomainIdCacheHits += cachedCipherDetails.size(); ekpProxyData->baseCipherDomainIdCacheHits += cachedCipherDetails.size();
ekpProxyData->baseCipherDomainIdCacheMisses += lookupCipherDomains.size(); ekpProxyData->baseCipherDomainIdCacheMisses += lookupCipherDomains.size();
if (g_network->isSimulated()) { if (!lookupCipherDomains.empty()) {
if (!lookupCipherDomains.empty()) { try {
try { KmsConnLookupEKsByDomainIdsReq keysByDomainIdReq(lookupCipherDomains);
SimGetEncryptKeysByDomainIdsRequest simKeysByDomainIdReq(lookupCipherDomains); KmsConnLookupEKsByDomainIdsRep keysByDomainIdRep =
SimGetEncryptKeyByDomainIdReply simKeysByDomainIdRep = wait(kmsConnectorInf.ekLookupByDomainIds.getReply(keysByDomainIdReq));
wait(simKmsInterface.encryptKeyLookupByDomainId.getReply(simKeysByDomainIdReq));
for (auto& item : simKeysByDomainIdRep.encryptKeyDetails) { for (auto& item : keysByDomainIdRep.cipherKeyDetails) {
latestCipherReply.baseCipherDetails.emplace_back( latestCipherReply.baseCipherDetails.emplace_back(
item.encryptDomainId, item.encryptKeyId, item.encryptKey, arena); item.encryptDomainId, item.encryptKeyId, item.encryptKey, arena);
// Record the fetched cipher details to the local cache for the future references // Record the fetched cipher details to the local cache for the future references
ekpProxyData->insertIntoBaseDomainIdCache(item.encryptDomainId, item.encryptKeyId, item.encryptKey); ekpProxyData->insertIntoBaseDomainIdCache(item.encryptDomainId, item.encryptKeyId, item.encryptKey);
}
} catch (Error& e) {
if (!canReplyWith(e)) {
TraceEvent("GetLatestCipherKeys", ekpProxyData->myId).error(e);
throw;
}
TraceEvent("GetLatestCipherKeys", ekpProxyData->myId).detail("ErrorCode", e.code());
ekpProxyData->sendErrorResponse(latestKeysReq.reply, e);
return Void();
} }
} catch (Error& e) {
if (!canReplyWith(e)) {
TraceEvent("GetLatestCipherKeys", ekpProxyData->myId).error(e);
throw;
}
TraceEvent("GetLatestCipherKeys", ekpProxyData->myId).detail("ErrorCode", e.code());
ekpProxyData->sendErrorResponse(latestKeysReq.reply, e);
return Void();
} }
} else {
// TODO: Call to non-FDB KMS connector process.
throw not_implemented();
} }
for (auto& item : cachedCipherDetails) { for (auto& item : cachedCipherDetails) {
@ -287,27 +305,27 @@ ACTOR Future<Void> getLatestCipherKeys(Reference<EncryptKeyProxyData> ekpProxyDa
return Void(); return Void();
} }
ACTOR Future<Void> refreshEncryptionKeysUsingSimKms(Reference<EncryptKeyProxyData> ekpProxyData, ACTOR Future<Void> refreshEncryptionKeysCore(Reference<EncryptKeyProxyData> ekpProxyData,
SimKmsProxyInterface simKmsInterface) { KmsConnectorInterface kmsConnectorInf) {
ASSERT(g_network->isSimulated()); ASSERT(g_network->isSimulated());
TraceEvent("RefreshEKs_Start", ekpProxyData->myId).detail("Inf", simKmsInterface.id()); TraceEvent("RefreshEKs_Start", ekpProxyData->myId).detail("KmsConnInf", kmsConnectorInf.id());
try { try {
SimGetEncryptKeysByDomainIdsRequest req; KmsConnLookupEKsByDomainIdsReq req;
req.encryptDomainIds.reserve(ekpProxyData->baseCipherDomainIdCache.size()); req.encryptDomainIds.reserve(ekpProxyData->baseCipherDomainIdCache.size());
for (auto& item : ekpProxyData->baseCipherDomainIdCache) { for (auto& item : ekpProxyData->baseCipherDomainIdCache) {
req.encryptDomainIds.emplace_back(item.first); req.encryptDomainIds.emplace_back(item.first);
} }
SimGetEncryptKeyByDomainIdReply rep = wait(simKmsInterface.encryptKeyLookupByDomainId.getReply(req)); KmsConnLookupEKsByDomainIdsRep rep = wait(kmsConnectorInf.ekLookupByDomainIds.getReply(req));
for (auto& item : rep.encryptKeyDetails) { for (auto& item : rep.cipherKeyDetails) {
ekpProxyData->insertIntoBaseDomainIdCache(item.encryptDomainId, item.encryptKeyId, item.encryptKey); ekpProxyData->insertIntoBaseDomainIdCache(item.encryptDomainId, item.encryptKeyId, item.encryptKey);
} }
ekpProxyData->baseCipherKeysRefreshed += rep.encryptKeyDetails.size(); ekpProxyData->baseCipherKeysRefreshed += rep.cipherKeyDetails.size();
TraceEvent("RefreshEKs_Done", ekpProxyData->myId).detail("KeyCount", rep.encryptKeyDetails.size()); TraceEvent("RefreshEKs_Done", ekpProxyData->myId).detail("KeyCount", rep.cipherKeyDetails.size());
} catch (Error& e) { } catch (Error& e) {
if (!canReplyWith(e)) { if (!canReplyWith(e)) {
TraceEvent("RefreshEncryptionKeys_Error").error(e); TraceEvent("RefreshEncryptionKeys_Error").error(e);
@ -320,30 +338,34 @@ ACTOR Future<Void> refreshEncryptionKeysUsingSimKms(Reference<EncryptKeyProxyDat
return Void(); return Void();
} }
ACTOR Future<Void> refreshEncryptionKeysUsingKms(Reference<EncryptKeyProxyData> ekpProxyData) { void refreshEncryptionKeys(Reference<EncryptKeyProxyData> ekpProxyData, KmsConnectorInterface kmsConnectorInf) {
wait(delay(0)); // compiler needs to be happy Future<Void> ignored = refreshEncryptionKeysCore(ekpProxyData, kmsConnectorInf);
throw not_implemented();
} }
void refreshEncryptionKeys(Reference<EncryptKeyProxyData> ekpProxyData, SimKmsProxyInterface simKmsInterface) { void activateKmsConnector(Reference<EncryptKeyProxyData> ekpProxyData, KmsConnectorInterface kmsConnectorInf) {
Future<Void> ignored;
if (g_network->isSimulated()) { if (g_network->isSimulated()) {
ignored = refreshEncryptionKeysUsingSimKms(ekpProxyData, simKmsInterface); ekpProxyData->kmsConnector = std::make_unique<SimKmsConnector>();
} else if (SERVER_KNOBS->KMS_CONNECTOR_TYPE.compare("HttpKmsConnector")) {
throw not_implemented();
} else { } else {
ignored = refreshEncryptionKeysUsingKms(ekpProxyData); throw not_implemented();
} }
TraceEvent("EKP_ActiveKmsConnector", ekpProxyData->myId).detail("ConnectorType", SERVER_KNOBS->KMS_CONNECTOR_TYPE);
ekpProxyData->addActor.send(ekpProxyData->kmsConnector->connectorCore(kmsConnectorInf));
} }
ACTOR Future<Void> encryptKeyProxyServer(EncryptKeyProxyInterface ekpInterface, Reference<AsyncVar<ServerDBInfo>> db) { ACTOR Future<Void> encryptKeyProxyServer(EncryptKeyProxyInterface ekpInterface, Reference<AsyncVar<ServerDBInfo>> db) {
state Reference<EncryptKeyProxyData> self(new EncryptKeyProxyData(ekpInterface.id())); state Reference<EncryptKeyProxyData> self(new EncryptKeyProxyData(ekpInterface.id()));
state PromiseStream<Future<Void>> addActor;
state Future<Void> collection = actorCollection(self->addActor.getFuture()); state Future<Void> collection = actorCollection(self->addActor.getFuture());
self->addActor.send(traceRole(Role::ENCRYPT_KEY_PROXY, ekpInterface.id())); self->addActor.send(traceRole(Role::ENCRYPT_KEY_PROXY, ekpInterface.id()));
state SimKmsProxyInterface simKmsProxyInf; state KmsConnectorInterface kmsConnectorInf;
kmsConnectorInf.initEndpoints();
TraceEvent("EKP_Start", self->myId).log(); TraceEvent("EKP_Start", self->myId).detail("KmsConnectorInf", kmsConnectorInf.id());
activateKmsConnector(self, kmsConnectorInf);
// Register a recurring task to refresh the cached Encryption keys. // Register a recurring task to refresh the cached Encryption keys.
// Approach avoids external RPCs due to EncryptionKey refreshes for the inline write encryption codepath such as: // Approach avoids external RPCs due to EncryptionKey refreshes for the inline write encryption codepath such as:
@ -352,31 +374,17 @@ ACTOR Future<Void> encryptKeyProxyServer(EncryptKeyProxyInterface ekpInterface,
// FLOW_KNOB->ENCRRYPTION_KEY_REFRESH_INTERVAL_SEC, allowing the interactions with external Encryption Key Manager // FLOW_KNOB->ENCRRYPTION_KEY_REFRESH_INTERVAL_SEC, allowing the interactions with external Encryption Key Manager
// mostly not co-inciding with FDB process encryption key refresh attempts. // mostly not co-inciding with FDB process encryption key refresh attempts.
if (g_network->isSimulated()) { self->encryptionKeyRefresher = recurring([&]() { refreshEncryptionKeys(self, kmsConnectorInf); },
// In simulation construct an Encryption KMSProxy actor to satisfy encryption keys lookups otherwise satisfied FLOW_KNOBS->ENCRYPT_KEY_REFRESH_INTERVAL,
// by integrating external Encryption Key Management solutions. TaskPriority::Worker);
simKmsProxyInf.initEndpoints();
self->addActor.send(simEncryptKmsProxyCore(simKmsProxyInf));
TraceEvent("EKP_InitSimKmsInf", self->myId).detail("Inf", simKmsProxyInf.id());
self->encryptionKeyRefresher = recurring([&]() { refreshEncryptionKeys(self, simKmsProxyInf); },
FLOW_KNOBS->ENCRYPT_KEY_REFRESH_INTERVAL,
TaskPriority::Worker);
} else {
// TODO: Add recurring actor to talk to external KMS proxy process
throw not_implemented();
}
try { try {
loop choose { loop choose {
when(EKPGetBaseCipherKeysByIdsRequest req = waitNext(ekpInterface.getBaseCipherKeysByIds.getFuture())) { when(EKPGetBaseCipherKeysByIdsRequest req = waitNext(ekpInterface.getBaseCipherKeysByIds.getFuture())) {
wait(getCipherKeysByBaseCipherKeyIds(self, simKmsProxyInf, req)); wait(getCipherKeysByBaseCipherKeyIds(self, kmsConnectorInf, req));
} }
when(EKPGetLatestBaseCipherKeysRequest req = waitNext(ekpInterface.getLatestBaseCipherKeys.getFuture())) { when(EKPGetLatestBaseCipherKeysRequest req = waitNext(ekpInterface.getLatestBaseCipherKeys.getFuture())) {
wait(getLatestCipherKeys(self, simKmsProxyInf, req)); wait(getLatestCipherKeys(self, kmsConnectorInf, req));
} }
when(HaltEncryptKeyProxyRequest req = waitNext(ekpInterface.haltEncryptKeyProxy.getFuture())) { when(HaltEncryptKeyProxyRequest req = waitNext(ekpInterface.haltEncryptKeyProxy.getFuture())) {
TraceEvent("EKP_Halted", self->myId).detail("ReqID", req.requesterID); TraceEvent("EKP_Halted", self->myId).detail("ReqID", req.requesterID);

43
fdbserver/KmsConnector.h Normal file
View File

@ -0,0 +1,43 @@
/*
* KmsConnector.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef KMS_CONNECTOR_H
#define KMS_CONNECTOR_H
#pragma once
#include "fdbserver/KmsConnectorInterface.h"
#include "flow/Arena.h"
#include "flow/EncryptUtils.h"
// FDB encryption needs to interact with external Key Management Services (KMS) solutions to lookup/refresh encryption
// keys. KmsConnector interface is an abstract interface enabling implementing specialized KMS connector
// implementations.
// FDB KMSConnector implementation should inherit from KmsConnector and implement pure virtual function,
// EncryptKeyProxyServer instantiates desired implementation object based on SERVER_KNOB->KMS_CONNECTOR_TYPE knob.
class KmsConnector : public NonCopyable {
public:
KmsConnector() {}
virtual ~KmsConnector() {}
virtual Future<Void> connectorCore(struct KmsConnectorInterface interf) = 0;
};
#endif

View File

@ -0,0 +1,144 @@
/*
* KmsConnectorInterface.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FDBSERVER_KMSCONNECTORINTERFACE_H
#define FDBSERVER_KMSCONNECTORINTERFACE_H
#pragma once
#include "fdbrpc/fdbrpc.h"
#include "flow/EncryptUtils.h"
#include "flow/FileIdentifier.h"
#include "flow/Trace.h"
#include "flow/flow.h"
#include "flow/network.h"
struct KmsConnectorInterface {
constexpr static FileIdentifier file_identifier = 2416711;
RequestStream<ReplyPromise<Void>> waitFailure;
RequestStream<struct KmsConnLookupEKsByKeyIdsReq> ekLookupByIds;
RequestStream<struct KmsConnLookupEKsByDomainIdsReq> ekLookupByDomainIds;
KmsConnectorInterface() {}
UID id() const { return ekLookupByIds.getEndpoint().token; }
template <class Archive>
void serialize(Archive& ar) {
if constexpr (!is_fb_function<Archive>) {
ASSERT(ar.protocolVersion().isValid());
}
serializer(ar, waitFailure);
if (Archive::isDeserializing) {
ekLookupByIds =
RequestStream<struct KmsConnLookupEKsByKeyIdsReq>(waitFailure.getEndpoint().getAdjustedEndpoint(1));
ekLookupByDomainIds =
RequestStream<struct KmsConnLookupEKsByDomainIdsReq>(waitFailure.getEndpoint().getAdjustedEndpoint(2));
}
}
void initEndpoints() {
std::vector<std::pair<FlowReceiver*, TaskPriority>> streams;
streams.push_back(waitFailure.getReceiver());
streams.push_back(ekLookupByIds.getReceiver(TaskPriority::Worker));
streams.push_back(ekLookupByDomainIds.getReceiver(TaskPriority::Worker));
FlowTransport::transport().addEndpoints(streams);
}
};
struct EncryptCipherKeyDetails {
constexpr static FileIdentifier file_identifier = 1227025;
EncryptCipherDomainId encryptDomainId;
EncryptCipherBaseKeyId encryptKeyId;
StringRef encryptKey;
EncryptCipherKeyDetails() {}
explicit EncryptCipherKeyDetails(EncryptCipherDomainId dId,
EncryptCipherBaseKeyId keyId,
StringRef key,
Arena& arena)
: encryptDomainId(dId), encryptKeyId(keyId), encryptKey(StringRef(arena, key)) {}
bool operator==(const EncryptCipherKeyDetails& toCompare) {
return encryptDomainId == toCompare.encryptDomainId && encryptKeyId == toCompare.encryptKeyId &&
encryptKey.compare(toCompare.encryptKey) == 0;
}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, encryptDomainId, encryptKeyId, encryptKey);
}
};
struct KmsConnLookupEKsByKeyIdsRep {
constexpr static FileIdentifier file_identifier = 2313778;
Arena arena;
std::vector<EncryptCipherKeyDetails> cipherKeyDetails;
KmsConnLookupEKsByKeyIdsRep() {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, arena, cipherKeyDetails);
}
};
struct KmsConnLookupEKsByKeyIdsReq {
constexpr static FileIdentifier file_identifier = 6913396;
std::vector<std::pair<EncryptCipherBaseKeyId, EncryptCipherDomainId>> encryptKeyIds;
ReplyPromise<KmsConnLookupEKsByKeyIdsRep> reply;
KmsConnLookupEKsByKeyIdsReq() {}
explicit KmsConnLookupEKsByKeyIdsReq(
const std::vector<std::pair<EncryptCipherBaseKeyId, EncryptCipherDomainId>>& keyIds)
: encryptKeyIds(keyIds) {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, encryptKeyIds, reply);
}
};
struct KmsConnLookupEKsByDomainIdsRep {
constexpr static FileIdentifier file_identifier = 3009025;
Arena arena;
std::vector<EncryptCipherKeyDetails> cipherKeyDetails;
KmsConnLookupEKsByDomainIdsRep() {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, arena, cipherKeyDetails);
}
};
struct KmsConnLookupEKsByDomainIdsReq {
constexpr static FileIdentifier file_identifier = 9918682;
std::vector<EncryptCipherDomainId> encryptDomainIds;
ReplyPromise<KmsConnLookupEKsByDomainIdsRep> reply;
KmsConnLookupEKsByDomainIdsReq() {}
explicit KmsConnLookupEKsByDomainIdsReq(const std::vector<EncryptCipherDomainId>& ids) : encryptDomainIds(ids) {}
template <class Ar>
void serialize(Ar& ar) {
serializer(ar, encryptDomainIds, reply);
}
};
#endif

View File

@ -18,37 +18,38 @@
* limitations under the License. * limitations under the License.
*/ */
#include "fdbserver/SimKmsConnector.actor.h"
#include "fdbrpc/sim_validation.h"
#include "fdbserver/Knobs.h"
#include "flow/ActorCollection.h"
#include "flow/EncryptUtils.h"
#include "flow/Error.h"
#include "flow/FastRef.h"
#include "flow/IRandom.h"
#include "flow/ITrace.h"
#include "flow/network.h"
#include "flow/UnitTest.h"
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
#include "fdbrpc/sim_validation.h"
#include "fdbserver/Knobs.h"
#include "fdbserver/SimEncryptKmsProxy.actor.h"
#include "flow/ActorCollection.h"
#include "flow/Error.h"
#include "flow/IRandom.h"
#include "flow/ITrace.h"
#include "flow/StreamCipher.h"
#include "flow/Trace.h"
#include "flow/UnitTest.h"
#include "flow/actorcompiler.h" // This must be the last #include. #include "flow/actorcompiler.h" // This must be the last #include.
#include "flow/network.h"
using SimEncryptKey = std::string;
struct SimEncryptKeyCtx { struct SimEncryptKeyCtx {
SimEncryptKeyId id; EncryptCipherBaseKeyId id;
SimEncryptKey key; SimEncryptKey key;
SimEncryptKeyCtx() : id(0) {} explicit SimEncryptKeyCtx(EncryptCipherBaseKeyId kId, const char* data) : id(kId), key(data) {}
explicit SimEncryptKeyCtx(SimEncryptKeyId kId, const char* data) : id(kId), key(data) {}
}; };
struct SimEncryptKmsProxyContext { struct SimKmsConnectorContext {
uint32_t maxEncryptionKeys; uint32_t maxEncryptionKeys;
std::unordered_map<SimEncryptKeyId, std::unique_ptr<SimEncryptKeyCtx>> simEncryptKeyStore; std::unordered_map<EncryptCipherBaseKeyId, std::unique_ptr<SimEncryptKeyCtx>> simEncryptKeyStore;
SimEncryptKmsProxyContext() : maxEncryptionKeys(0) {} explicit SimKmsConnectorContext(uint32_t keyCount) : maxEncryptionKeys(keyCount) {
explicit SimEncryptKmsProxyContext(uint32_t keyCount) : maxEncryptionKeys(keyCount) {
uint8_t buffer[AES_256_KEY_LENGTH]; uint8_t buffer[AES_256_KEY_LENGTH];
// Construct encryption keyStore. // Construct encryption keyStore.
@ -60,25 +61,26 @@ struct SimEncryptKmsProxyContext {
} }
}; };
ACTOR Future<Void> simEncryptKmsProxyCore(SimKmsProxyInterface interf) { ACTOR Future<Void> simKmsConnectorCore_impl(KmsConnectorInterface interf) {
state SimEncryptKmsProxyContext kmsProxyCtx(SERVER_KNOBS->SIM_KMS_MAX_KEYS);
ASSERT(kmsProxyCtx.simEncryptKeyStore.size() == SERVER_KNOBS->SIM_KMS_MAX_KEYS);
TraceEvent("SimEncryptKmsProxy_Init", interf.id()).detail("MaxEncryptKeys", SERVER_KNOBS->SIM_KMS_MAX_KEYS); TraceEvent("SimEncryptKmsProxy_Init", interf.id()).detail("MaxEncryptKeys", SERVER_KNOBS->SIM_KMS_MAX_KEYS);
state bool success = true; state bool success = true;
state std::unique_ptr<SimKmsConnectorContext> ctx =
std::make_unique<SimKmsConnectorContext>(SERVER_KNOBS->SIM_KMS_MAX_KEYS);
ASSERT_EQ(ctx->simEncryptKeyStore.size(), SERVER_KNOBS->SIM_KMS_MAX_KEYS);
loop { loop {
choose { choose {
when(SimGetEncryptKeysByKeyIdsRequest req = waitNext(interf.encryptKeyLookupByKeyIds.getFuture())) { when(KmsConnLookupEKsByKeyIdsReq req = waitNext(interf.ekLookupByIds.getFuture())) {
state SimGetEncryptKeysByKeyIdsRequest keysByIdsReq = req; state KmsConnLookupEKsByKeyIdsReq keysByIdsReq = req;
state SimGetEncryptKeysByKeyIdsReply keysByIdsRep; state KmsConnLookupEKsByKeyIdsRep keysByIdsRep;
// Lookup corresponding EncryptKeyCtx for input keyId // Lookup corresponding EncryptKeyCtx for input keyId
for (const auto& item : req.encryptKeyIds) { for (const auto& item : req.encryptKeyIds) {
const auto& itr = kmsProxyCtx.simEncryptKeyStore.find(item.first); const auto& itr = ctx->simEncryptKeyStore.find(item.first);
if (itr != kmsProxyCtx.simEncryptKeyStore.end()) { if (itr != ctx->simEncryptKeyStore.end()) {
keysByIdsRep.encryptKeyDetails.emplace_back( keysByIdsRep.cipherKeyDetails.emplace_back(
item.second, item.second,
itr->first, itr->first,
StringRef(keysByIdsRep.arena, itr->second.get()->key), StringRef(keysByIdsRep.arena, itr->second.get()->key),
@ -93,18 +95,18 @@ ACTOR Future<Void> simEncryptKmsProxyCore(SimKmsProxyInterface interf) {
success ? keysByIdsReq.reply.send(keysByIdsRep) : keysByIdsReq.reply.sendError(encrypt_key_not_found()); success ? keysByIdsReq.reply.send(keysByIdsRep) : keysByIdsReq.reply.sendError(encrypt_key_not_found());
} }
when(SimGetEncryptKeysByDomainIdsRequest req = waitNext(interf.encryptKeyLookupByDomainId.getFuture())) { when(KmsConnLookupEKsByDomainIdsReq req = waitNext(interf.ekLookupByDomainIds.getFuture())) {
state SimGetEncryptKeysByDomainIdsRequest keysByDomainIdReq = req; state KmsConnLookupEKsByDomainIdsReq keysByDomainIdReq = req;
state SimGetEncryptKeyByDomainIdReply keysByDomainIdRep; state KmsConnLookupEKsByDomainIdsRep keysByDomainIdRep;
// Map encryptionDomainId to corresponding EncryptKeyCtx element using a modulo operation. This would // Map encryptionDomainId to corresponding EncryptKeyCtx element using a modulo operation. This would
// mean multiple domains gets mapped to the same encryption key which is fine, the EncryptKeyStore // mean multiple domains gets mapped to the same encryption key which is fine, the EncryptKeyStore
// guarantees that keyId -> plaintext encryptKey mapping is idempotent. // guarantees that keyId -> plaintext encryptKey mapping is idempotent.
for (SimEncryptDomainId domainId : req.encryptDomainIds) { for (EncryptCipherDomainId domainId : req.encryptDomainIds) {
SimEncryptKeyId keyId = domainId % SERVER_KNOBS->SIM_KMS_MAX_KEYS; EncryptCipherBaseKeyId keyId = domainId % SERVER_KNOBS->SIM_KMS_MAX_KEYS;
const auto& itr = kmsProxyCtx.simEncryptKeyStore.find(keyId); const auto& itr = ctx->simEncryptKeyStore.find(keyId);
if (itr != kmsProxyCtx.simEncryptKeyStore.end()) { if (itr != ctx->simEncryptKeyStore.end()) {
keysByDomainIdRep.encryptKeyDetails.emplace_back( keysByDomainIdRep.cipherKeyDetails.emplace_back(
domainId, keyId, StringRef(itr->second.get()->key), keysByDomainIdRep.arena); domainId, keyId, StringRef(itr->second.get()->key), keysByDomainIdRep.arena);
} else { } else {
success = false; success = false;
@ -121,35 +123,38 @@ ACTOR Future<Void> simEncryptKmsProxyCore(SimKmsProxyInterface interf) {
} }
} }
void forceLinkSimEncryptKmsProxyTests() {} Future<Void> SimKmsConnector::connectorCore(KmsConnectorInterface interf) {
return simKmsConnectorCore_impl(interf);
}
void forceLinkSimKmsConnectorTests() {}
namespace { namespace {
ACTOR Future<Void> testRunWorkload(SimKmsProxyInterface inf, uint32_t nEncryptionKeys) { ACTOR Future<Void> testRunWorkload(KmsConnectorInterface inf, uint32_t nEncryptionKeys) {
state uint32_t maxEncryptionKeys = nEncryptionKeys; state uint32_t maxEncryptionKeys = nEncryptionKeys;
state int maxDomainIds = deterministicRandom()->randomInt(121, 295); state int maxDomainIds = deterministicRandom()->randomInt(121, 295);
state int maxIterations = deterministicRandom()->randomInt(786, 1786); state int maxIterations = deterministicRandom()->randomInt(786, 1786);
state std::unordered_map<SimEncryptDomainId, std::unique_ptr<SimEncryptKeyCtx>> domainIdKeyMap; state std::unordered_map<EncryptCipherDomainId, std::unique_ptr<SimEncryptKeyCtx>> domainIdKeyMap;
state int i = 0; state int i = 0;
TraceEvent("RunWorkloadStart").detail("MaxDomainIds", maxDomainIds).detail("MaxIterations", maxIterations); TraceEvent("RunWorkloadStart").detail("MaxDomainIds", maxDomainIds).detail("MaxIterations", maxIterations);
{ {
// construct domainId to EncryptKeyCtx map // construct domainId to EncryptKeyCtx map
SimGetEncryptKeysByDomainIdsRequest domainIdsReq; KmsConnLookupEKsByDomainIdsReq domainIdsReq;
for (i = 0; i < maxDomainIds; i++) { for (i = 0; i < maxDomainIds; i++) {
domainIdsReq.encryptDomainIds.push_back(i); domainIdsReq.encryptDomainIds.push_back(i);
} }
SimGetEncryptKeyByDomainIdReply domainIdsReply = wait(inf.encryptKeyLookupByDomainId.getReply(domainIdsReq)); KmsConnLookupEKsByDomainIdsRep domainIdsRep = wait(inf.ekLookupByDomainIds.getReply(domainIdsReq));
for (auto& element : domainIdsReply.encryptKeyDetails) { for (auto& element : domainIdsRep.cipherKeyDetails) {
domainIdKeyMap.emplace( domainIdKeyMap.emplace(
element.encryptDomainId, element.encryptDomainId,
std::make_unique<SimEncryptKeyCtx>(element.encryptKeyId, element.encryptKey.toString().c_str())); std::make_unique<SimEncryptKeyCtx>(element.encryptKeyId, element.encryptKey.toString().c_str()));
} }
// randomly pick any domainId and validate if lookupByKeyId result matches // randomly pick any domainId and validate if lookupByKeyId result matches
state std::unordered_map<SimEncryptKeyId, StringRef> validationMap; state std::unordered_map<EncryptCipherBaseKeyId, StringRef> validationMap;
std::unordered_map<SimEncryptKeyId, SimEncryptDomainId> idsToLookup; std::unordered_map<EncryptCipherBaseKeyId, EncryptCipherDomainId> idsToLookup;
for (i = 0; i < maxIterations; i++) { for (i = 0; i < maxIterations; i++) {
state int idx = deterministicRandom()->randomInt(0, maxDomainIds); state int idx = deterministicRandom()->randomInt(0, maxDomainIds);
state SimEncryptKeyCtx* ctx = domainIdKeyMap[idx].get(); state SimEncryptKeyCtx* ctx = domainIdKeyMap[idx].get();
@ -157,27 +162,27 @@ ACTOR Future<Void> testRunWorkload(SimKmsProxyInterface inf, uint32_t nEncryptio
idsToLookup.emplace(ctx->id, idx); idsToLookup.emplace(ctx->id, idx);
} }
state SimGetEncryptKeysByKeyIdsRequest keyIdsReq; state KmsConnLookupEKsByKeyIdsReq keyIdsReq;
for (const auto& item : idsToLookup) { for (const auto& item : idsToLookup) {
keyIdsReq.encryptKeyIds.emplace_back(std::make_pair(item.first, item.second)); keyIdsReq.encryptKeyIds.emplace_back(std::make_pair(item.first, item.second));
} }
state SimGetEncryptKeysByKeyIdsReply keyIdsReply = wait(inf.encryptKeyLookupByKeyIds.getReply(keyIdsReq)); state KmsConnLookupEKsByKeyIdsRep keyIdsReply = wait(inf.ekLookupByIds.getReply(keyIdsReq));
/* TraceEvent("Lookup") /* TraceEvent("Lookup")
.detail("KeyIdReqSize", keyIdsReq.encryptKeyIds.size()) .detail("KeyIdReqSize", keyIdsReq.encryptKeyIds.size())
.detail("KeyIdsRepSz", keyIdsReply.encryptKeyDetails.size()) .detail("KeyIdsRepSz", keyIdsReply.encryptKeyDetails.size())
.detail("ValSz", validationMap.size()); */ .detail("ValSz", validationMap.size()); */
ASSERT(keyIdsReply.encryptKeyDetails.size() == validationMap.size()); ASSERT(keyIdsReply.cipherKeyDetails.size() == validationMap.size());
for (const auto& element : keyIdsReply.encryptKeyDetails) { for (const auto& element : keyIdsReply.cipherKeyDetails) {
ASSERT(validationMap[element.encryptDomainId].compare(element.encryptKey) == 0); ASSERT(validationMap[element.encryptKeyId].compare(element.encryptKey) == 0);
} }
} }
{ {
// Verify unknown key access returns the error // Verify unknown key access returns the error
state SimGetEncryptKeysByKeyIdsRequest req; state KmsConnLookupEKsByKeyIdsReq req;
req.encryptKeyIds.emplace_back(std::make_pair(maxEncryptionKeys + 1, 1)); req.encryptKeyIds.emplace_back(std::make_pair(maxEncryptionKeys + 1, 1));
try { try {
SimGetEncryptKeysByKeyIdsReply reply = wait(inf.encryptKeyLookupByKeyIds.getReply(req)); KmsConnLookupEKsByKeyIdsRep reply = wait(inf.ekLookupByIds.getReply(req));
} catch (Error& e) { } catch (Error& e) {
ASSERT(e.code() == error_code_encrypt_key_not_found); ASSERT(e.code() == error_code_encrypt_key_not_found);
} }
@ -189,12 +194,13 @@ ACTOR Future<Void> testRunWorkload(SimKmsProxyInterface inf, uint32_t nEncryptio
} // namespace } // namespace
TEST_CASE("fdbserver/SimEncryptKmsProxy") { TEST_CASE("fdbserver/SimKmsConnector") {
state SimKmsProxyInterface inf; state KmsConnectorInterface inf;
state uint32_t maxEncryptKeys = 64; state uint32_t maxEncryptKeys = 64;
state SimKmsConnector connector;
loop choose { loop choose {
when(wait(simEncryptKmsProxyCore(inf))) { throw internal_error(); } when(wait(connector.connectorCore(inf))) { throw internal_error(); }
when(wait(testRunWorkload(inf, maxEncryptKeys))) { break; } when(wait(testRunWorkload(inf, maxEncryptKeys))) { break; }
} }
return Void(); return Void();

View File

@ -0,0 +1,40 @@
/*
* SimKmsConnector.actor.h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#if defined(NO_INTELLISENSE) && !defined(FDBSERVER_SIMKMSCONNECTOR_ACTOR_G_H)
#define FDBSERVER_SIMKMSCONNECTOR_ACTOR_G_H
#include "fdbserver/SimKmsConnector.actor.g.h"
#elif !defined(FDBSERVER_SIMKMSCONNECTOR_ACTOR_H)
#define FDBSERVER_SIMKMSCONNECTOR_ACTOR_H
#include "fdbserver/KmsConnector.h"
#include "flow/BlobCipher.h"
#include "flow/actorcompiler.h" // This must be the last #include.
class SimKmsConnector : public KmsConnector {
public:
SimKmsConnector() = default;
Future<Void> connectorCore(KmsConnectorInterface interf);
};
#endif

View File

@ -35,7 +35,7 @@ void forceLinkBLockCiherTests();
void forceLinkParallelStreamTests(); void forceLinkParallelStreamTests();
void forceLinkSimExternalConnectionTests(); void forceLinkSimExternalConnectionTests();
void forceLinkMutationLogReaderTests(); void forceLinkMutationLogReaderTests();
void forceLinkSimEncryptKmsProxyTests(); void forceLinkSimKmsConnectorTests();
void forceLinkIThreadPoolTests(); void forceLinkIThreadPoolTests();
void forceLinkTokenSignTests(); void forceLinkTokenSignTests();
void forceLinkVersionVectorTests(); void forceLinkVersionVectorTests();
@ -84,7 +84,7 @@ struct UnitTestWorkload : TestWorkload {
forceLinkParallelStreamTests(); forceLinkParallelStreamTests();
forceLinkSimExternalConnectionTests(); forceLinkSimExternalConnectionTests();
forceLinkMutationLogReaderTests(); forceLinkMutationLogReaderTests();
forceLinkSimEncryptKmsProxyTests(); forceLinkSimKmsConnectorTests();
forceLinkIThreadPoolTests(); forceLinkIThreadPoolTests();
forceLinkTokenSignTests(); forceLinkTokenSignTests();
forceLinkVersionVectorTests(); forceLinkVersionVectorTests();

View File

@ -25,7 +25,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#if (!defined(TLS_DISABLED) && !defined(_WIN32)) #if (!defined(TLS_DISABLED))
#define ENCRYPTION_ENABLED 1 #define ENCRYPTION_ENABLED 1
#else #else
#define ENCRYPTION_ENABLED 0 #define ENCRYPTION_ENABLED 0