2022-01-14 03:11:01 +08:00
|
|
|
/*
|
|
|
|
* EncryptKeyProxy.actor.cpp
|
|
|
|
*
|
|
|
|
* This source file is part of the FoundationDB open source project
|
|
|
|
*
|
2022-03-22 04:36:23 +08:00
|
|
|
* Copyright 2013-2022 Apple Inc. and the FoundationDB project authors
|
2022-01-14 03:11:01 +08:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2022-04-12 00:08:42 +08:00
|
|
|
#include "fdbrpc/Locality.h"
|
|
|
|
#include "fdbrpc/Stats.h"
|
2022-01-14 03:11:01 +08:00
|
|
|
#include "fdbserver/EncryptKeyProxyInterface.h"
|
2022-04-22 23:53:39 +08:00
|
|
|
#include "fdbserver/KmsConnector.h"
|
|
|
|
#include "fdbserver/KmsConnectorInterface.h"
|
|
|
|
#include "fdbserver/Knobs.h"
|
2022-05-08 04:18:35 +08:00
|
|
|
#include "fdbserver/RESTKmsConnector.actor.h"
|
2022-04-12 00:08:42 +08:00
|
|
|
#include "fdbserver/ServerDBInfo.actor.h"
|
2022-04-22 23:53:39 +08:00
|
|
|
#include "fdbserver/SimKmsConnector.actor.h"
|
2022-01-14 03:11:01 +08:00
|
|
|
#include "fdbserver/WorkerInterface.actor.h"
|
|
|
|
#include "fdbserver/ServerDBInfo.h"
|
|
|
|
#include "flow/Arena.h"
|
2022-04-22 23:53:39 +08:00
|
|
|
#include "flow/EncryptUtils.h"
|
2022-01-14 03:11:01 +08:00
|
|
|
#include "flow/Error.h"
|
|
|
|
#include "flow/EventTypes.actor.h"
|
|
|
|
#include "flow/FastRef.h"
|
2022-03-11 04:06:49 +08:00
|
|
|
#include "flow/IRandom.h"
|
2022-04-12 00:08:42 +08:00
|
|
|
#include "flow/Knobs.h"
|
2022-01-14 03:11:01 +08:00
|
|
|
#include "flow/Trace.h"
|
2022-04-12 00:08:42 +08:00
|
|
|
#include "flow/flow.h"
|
2022-01-14 03:11:01 +08:00
|
|
|
#include "flow/genericactors.actor.h"
|
2022-03-11 04:06:49 +08:00
|
|
|
#include "flow/network.h"
|
|
|
|
|
2022-04-12 00:08:42 +08:00
|
|
|
#include <boost/mpl/not.hpp>
|
2022-04-22 05:56:09 +08:00
|
|
|
#include <utility>
|
2022-04-22 23:53:39 +08:00
|
|
|
#include <memory>
|
2022-04-12 00:08:42 +08:00
|
|
|
|
2022-01-14 03:11:01 +08:00
|
|
|
#include "flow/actorcompiler.h" // This must be the last #include.
|
|
|
|
|
2022-04-12 00:08:42 +08:00
|
|
|
namespace {
|
|
|
|
bool canReplyWith(Error e) {
|
|
|
|
switch (e.code()) {
|
|
|
|
case error_code_encrypt_key_not_found:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
struct EncryptBaseCipherKey {
|
2022-04-22 23:53:39 +08:00
|
|
|
EncryptCipherDomainId domainId;
|
|
|
|
EncryptCipherBaseKeyId baseCipherId;
|
2022-04-12 00:08:42 +08:00
|
|
|
Standalone<StringRef> baseCipherKey;
|
|
|
|
uint64_t creationTimeSec;
|
|
|
|
bool noExpiry;
|
|
|
|
|
|
|
|
EncryptBaseCipherKey()
|
|
|
|
: domainId(0), baseCipherId(0), baseCipherKey(StringRef()), creationTimeSec(0), noExpiry(false) {}
|
2022-04-22 23:53:39 +08:00
|
|
|
explicit EncryptBaseCipherKey(EncryptCipherDomainId dId,
|
|
|
|
EncryptCipherBaseKeyId cipherId,
|
2022-04-12 00:08:42 +08:00
|
|
|
StringRef cipherKey,
|
|
|
|
bool neverExpire)
|
|
|
|
: domainId(dId), baseCipherId(cipherId), baseCipherKey(cipherKey), creationTimeSec(now()), noExpiry(neverExpire) {
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isValid() { return noExpiry ? true : ((now() - creationTimeSec) < FLOW_KNOBS->ENCRYPT_CIPHER_KEY_CACHE_TTL); }
|
|
|
|
};
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
using EncryptBaseDomainIdCache = std::unordered_map<EncryptCipherDomainId, EncryptBaseCipherKey>;
|
|
|
|
using EncryptBaseCipherKeyIdCache = std::unordered_map<EncryptCipherBaseKeyId, EncryptBaseCipherKey>;
|
2022-04-12 00:08:42 +08:00
|
|
|
|
2022-01-14 03:11:01 +08:00
|
|
|
struct EncryptKeyProxyData : NonCopyable, ReferenceCounted<EncryptKeyProxyData> {
|
2022-04-12 00:08:42 +08:00
|
|
|
public:
|
2022-01-14 03:11:01 +08:00
|
|
|
UID myId;
|
|
|
|
PromiseStream<Future<Void>> addActor;
|
2022-04-12 00:08:42 +08:00
|
|
|
Future<Void> encryptionKeyRefresher;
|
|
|
|
|
|
|
|
EncryptBaseDomainIdCache baseCipherDomainIdCache;
|
|
|
|
EncryptBaseCipherKeyIdCache baseCipherKeyIdCache;
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
std::unique_ptr<KmsConnector> kmsConnector;
|
|
|
|
|
2022-04-12 00:08:42 +08:00
|
|
|
CounterCollection ekpCacheMetrics;
|
|
|
|
|
|
|
|
Counter baseCipherKeyIdCacheMisses;
|
|
|
|
Counter baseCipherKeyIdCacheHits;
|
|
|
|
Counter baseCipherDomainIdCacheMisses;
|
|
|
|
Counter baseCipherDomainIdCacheHits;
|
|
|
|
Counter baseCipherKeysRefreshed;
|
|
|
|
Counter numResponseWithErrors;
|
|
|
|
Counter numEncryptionKeyRefreshErrors;
|
|
|
|
|
|
|
|
explicit EncryptKeyProxyData(UID id)
|
|
|
|
: myId(id), ekpCacheMetrics("EKPMetrics", myId.toString()),
|
|
|
|
baseCipherKeyIdCacheMisses("EKPCipherIdCacheMisses", ekpCacheMetrics),
|
|
|
|
baseCipherKeyIdCacheHits("EKPCipherIdCacheHits", ekpCacheMetrics),
|
|
|
|
baseCipherDomainIdCacheMisses("EKPCipherDomainIdCacheMisses", ekpCacheMetrics),
|
|
|
|
baseCipherDomainIdCacheHits("EKPCipherDomainIdCacheHits", ekpCacheMetrics),
|
|
|
|
baseCipherKeysRefreshed("EKPCipherKeysRefreshed", ekpCacheMetrics),
|
|
|
|
numResponseWithErrors("EKPNumResponseWithErrors", ekpCacheMetrics),
|
|
|
|
numEncryptionKeyRefreshErrors("EKPNumEncryptionKeyRefreshErrors", ekpCacheMetrics) {}
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
void insertIntoBaseDomainIdCache(const EncryptCipherDomainId domainId,
|
|
|
|
const EncryptCipherBaseKeyId baseCipherId,
|
2022-04-12 00:08:42 +08:00
|
|
|
const StringRef baseCipherKey) {
|
|
|
|
// Entries in domainId cache are eligible for periodic refreshes to support 'limiting lifetime of encryption
|
|
|
|
// key' support if enabled on external KMS solutions.
|
|
|
|
|
|
|
|
baseCipherDomainIdCache[domainId] = EncryptBaseCipherKey(domainId, baseCipherId, baseCipherKey, false);
|
|
|
|
|
|
|
|
// Update cached the information indexed using baseCipherId
|
|
|
|
insertIntoBaseCipherIdCache(domainId, baseCipherId, baseCipherKey);
|
|
|
|
}
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
void insertIntoBaseCipherIdCache(const EncryptCipherDomainId domainId,
|
|
|
|
const EncryptCipherBaseKeyId baseCipherId,
|
2022-04-12 00:08:42 +08:00
|
|
|
const StringRef baseCipherKey) {
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
baseCipherKeyIdCache[baseCipherId] = EncryptBaseCipherKey(domainId, baseCipherId, baseCipherKey, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Reply>
|
|
|
|
using isEKPGetLatestBaseCipherKeysReply = std::is_base_of<EKPGetLatestBaseCipherKeysReply, Reply>;
|
|
|
|
template <class Reply>
|
|
|
|
using isEKPGetBaseCipherKeysByIdsReply = std::is_base_of<EKPGetBaseCipherKeysByIdsReply, Reply>;
|
|
|
|
|
|
|
|
// For errors occuring due to invalid input parameters such as: invalid encryptionDomainId or
|
|
|
|
// invalid baseCipherId, piggyback error with response to the client; approach allows clients
|
|
|
|
// to take necessary corrective actions such as: clearing up cache with invalid ids, log relevant
|
|
|
|
// details for further investigation etc.
|
2022-01-14 03:11:01 +08:00
|
|
|
|
2022-04-12 00:08:42 +08:00
|
|
|
template <class Reply>
|
|
|
|
typename std::enable_if<isEKPGetBaseCipherKeysByIdsReply<Reply>::value ||
|
|
|
|
isEKPGetLatestBaseCipherKeysReply<Reply>::value,
|
|
|
|
void>::type
|
|
|
|
sendErrorResponse(const ReplyPromise<Reply>& promise, const Error& e) {
|
|
|
|
Reply reply;
|
|
|
|
++numResponseWithErrors;
|
|
|
|
reply.error = e;
|
|
|
|
promise.send(reply);
|
|
|
|
}
|
2022-01-14 03:11:01 +08:00
|
|
|
};
|
|
|
|
|
2022-04-12 00:08:42 +08:00
|
|
|
ACTOR Future<Void> getCipherKeysByBaseCipherKeyIds(Reference<EncryptKeyProxyData> ekpProxyData,
|
2022-04-22 23:53:39 +08:00
|
|
|
KmsConnectorInterface kmsConnectorInf,
|
2022-04-12 00:08:42 +08:00
|
|
|
EKPGetBaseCipherKeysByIdsRequest req) {
|
|
|
|
// Scan the cached cipher-keys and filter our baseCipherIds locally cached
|
|
|
|
// for the rest, reachout to KMS to fetch the required details
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
std::vector<std::pair<EncryptCipherBaseKeyId, EncryptCipherDomainId>> lookupCipherIds;
|
2022-04-22 05:56:09 +08:00
|
|
|
state std::vector<EKPBaseCipherDetails> cachedCipherDetails;
|
2022-04-12 00:08:42 +08:00
|
|
|
|
2022-04-22 05:56:09 +08:00
|
|
|
state EKPGetBaseCipherKeysByIdsRequest keysByIds = req;
|
|
|
|
state EKPGetBaseCipherKeysByIdsReply keyIdsReply;
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
// Dedup the requested pair<baseCipherId, encryptDomainId>
|
|
|
|
// TODO: endpoint serialization of std::unordered_set isn't working at the moment
|
2022-04-23 04:16:30 +08:00
|
|
|
std::unordered_set<std::pair<EncryptCipherBaseKeyId, EncryptCipherDomainId>,
|
|
|
|
boost::hash<std::pair<EncryptCipherBaseKeyId, EncryptCipherDomainId>>>
|
|
|
|
dedupedCipherIds;
|
2022-04-22 05:56:09 +08:00
|
|
|
for (const auto& item : req.baseCipherIds) {
|
2022-04-22 23:53:39 +08:00
|
|
|
dedupedCipherIds.emplace(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& item : dedupedCipherIds) {
|
2022-04-22 05:56:09 +08:00
|
|
|
const auto itr = ekpProxyData->baseCipherKeyIdCache.find(item.first);
|
2022-04-12 00:08:42 +08:00
|
|
|
if (itr != ekpProxyData->baseCipherKeyIdCache.end()) {
|
|
|
|
ASSERT(itr->second.isValid());
|
2022-04-22 05:56:09 +08:00
|
|
|
cachedCipherDetails.emplace_back(
|
|
|
|
itr->second.domainId, itr->second.baseCipherId, itr->second.baseCipherKey, keyIdsReply.arena);
|
2022-04-12 00:08:42 +08:00
|
|
|
} else {
|
2022-04-22 05:56:09 +08:00
|
|
|
lookupCipherIds.emplace_back(std::make_pair(item.first, item.second));
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 05:56:09 +08:00
|
|
|
ekpProxyData->baseCipherKeyIdCacheHits += cachedCipherDetails.size();
|
2022-04-12 00:08:42 +08:00
|
|
|
ekpProxyData->baseCipherKeyIdCacheMisses += lookupCipherIds.size();
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
if (!lookupCipherIds.empty()) {
|
|
|
|
try {
|
|
|
|
KmsConnLookupEKsByKeyIdsReq keysByIdsReq(lookupCipherIds);
|
|
|
|
KmsConnLookupEKsByKeyIdsRep keysByIdsRep = wait(kmsConnectorInf.ekLookupByIds.getReply(keysByIdsReq));
|
2022-04-12 00:08:42 +08:00
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
for (const auto& item : keysByIdsRep.cipherKeyDetails) {
|
|
|
|
keyIdsReply.baseCipherDetails.emplace_back(
|
|
|
|
item.encryptDomainId, item.encryptKeyId, item.encryptKey, keyIdsReply.arena);
|
|
|
|
}
|
2022-04-12 00:08:42 +08:00
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
// 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;
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
2022-04-22 23:53:39 +08:00
|
|
|
TraceEvent("GetCipherKeysByIds", ekpProxyData->myId).detail("ErrorCode", e.code());
|
|
|
|
ekpProxyData->sendErrorResponse(keysByIds.reply, e);
|
|
|
|
return Void();
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 05:56:09 +08:00
|
|
|
// Append cached cipherKeyDetails to the result-set
|
|
|
|
keyIdsReply.baseCipherDetails.insert(
|
|
|
|
keyIdsReply.baseCipherDetails.end(), cachedCipherDetails.begin(), cachedCipherDetails.end());
|
2022-04-12 00:08:42 +08:00
|
|
|
|
2022-04-22 05:56:09 +08:00
|
|
|
keyIdsReply.numHits = cachedCipherDetails.size();
|
2022-04-12 00:08:42 +08:00
|
|
|
keysByIds.reply.send(keyIdsReply);
|
|
|
|
|
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
|
|
|
ACTOR Future<Void> getLatestCipherKeys(Reference<EncryptKeyProxyData> ekpProxyData,
|
2022-04-22 23:53:39 +08:00
|
|
|
KmsConnectorInterface kmsConnectorInf,
|
2022-04-12 00:08:42 +08:00
|
|
|
EKPGetLatestBaseCipherKeysRequest req) {
|
|
|
|
// Scan the cached cipher-keys and filter our baseCipherIds locally cached
|
|
|
|
// for the rest, reachout to KMS to fetch the required details
|
2022-04-22 05:56:09 +08:00
|
|
|
state std::vector<EKPBaseCipherDetails> cachedCipherDetails;
|
2022-04-12 00:08:42 +08:00
|
|
|
state EKPGetLatestBaseCipherKeysRequest latestKeysReq = req;
|
|
|
|
state EKPGetLatestBaseCipherKeysReply latestCipherReply;
|
|
|
|
state Arena& arena = latestCipherReply.arena;
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2022-04-12 00:08:42 +08:00
|
|
|
// 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.
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
std::vector<EncryptCipherDomainId> lookupCipherDomains;
|
|
|
|
for (EncryptCipherDomainId id : dedupedDomainIds) {
|
2022-04-12 00:08:42 +08:00
|
|
|
const auto itr = ekpProxyData->baseCipherDomainIdCache.find(id);
|
|
|
|
if (itr != ekpProxyData->baseCipherDomainIdCache.end() && itr->second.isValid()) {
|
2022-04-22 05:56:09 +08:00
|
|
|
cachedCipherDetails.emplace_back(id, itr->second.baseCipherId, itr->second.baseCipherKey, arena);
|
2022-04-12 00:08:42 +08:00
|
|
|
} else {
|
2022-04-22 23:53:39 +08:00
|
|
|
lookupCipherDomains.emplace_back(id);
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 05:56:09 +08:00
|
|
|
ekpProxyData->baseCipherDomainIdCacheHits += cachedCipherDetails.size();
|
2022-04-12 00:08:42 +08:00
|
|
|
ekpProxyData->baseCipherDomainIdCacheMisses += lookupCipherDomains.size();
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
if (!lookupCipherDomains.empty()) {
|
|
|
|
try {
|
|
|
|
KmsConnLookupEKsByDomainIdsReq keysByDomainIdReq(lookupCipherDomains);
|
|
|
|
KmsConnLookupEKsByDomainIdsRep keysByDomainIdRep =
|
|
|
|
wait(kmsConnectorInf.ekLookupByDomainIds.getReply(keysByDomainIdReq));
|
|
|
|
|
|
|
|
for (auto& item : keysByDomainIdRep.cipherKeyDetails) {
|
|
|
|
latestCipherReply.baseCipherDetails.emplace_back(
|
|
|
|
item.encryptDomainId, item.encryptKeyId, item.encryptKey, arena);
|
|
|
|
|
|
|
|
// Record the fetched cipher details to the local cache for the future references
|
|
|
|
ekpProxyData->insertIntoBaseDomainIdCache(item.encryptDomainId, item.encryptKeyId, item.encryptKey);
|
|
|
|
}
|
|
|
|
} catch (Error& e) {
|
|
|
|
if (!canReplyWith(e)) {
|
|
|
|
TraceEvent("GetLatestCipherKeys", ekpProxyData->myId).error(e);
|
|
|
|
throw;
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
2022-04-22 23:53:39 +08:00
|
|
|
TraceEvent("GetLatestCipherKeys", ekpProxyData->myId).detail("ErrorCode", e.code());
|
|
|
|
ekpProxyData->sendErrorResponse(latestKeysReq.reply, e);
|
|
|
|
return Void();
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 05:56:09 +08:00
|
|
|
for (auto& item : cachedCipherDetails) {
|
|
|
|
latestCipherReply.baseCipherDetails.emplace_back(
|
|
|
|
item.encryptDomainId, item.baseCipherId, item.baseCipherKey, arena);
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
|
|
|
|
2022-04-22 05:56:09 +08:00
|
|
|
latestCipherReply.numHits = cachedCipherDetails.size();
|
2022-04-12 00:08:42 +08:00
|
|
|
latestKeysReq.reply.send(latestCipherReply);
|
|
|
|
|
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
ACTOR Future<Void> refreshEncryptionKeysCore(Reference<EncryptKeyProxyData> ekpProxyData,
|
|
|
|
KmsConnectorInterface kmsConnectorInf) {
|
2022-04-12 00:08:42 +08:00
|
|
|
|
|
|
|
ASSERT(g_network->isSimulated());
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
TraceEvent("RefreshEKs_Start", ekpProxyData->myId).detail("KmsConnInf", kmsConnectorInf.id());
|
2022-04-12 00:08:42 +08:00
|
|
|
|
|
|
|
try {
|
2022-04-22 23:53:39 +08:00
|
|
|
KmsConnLookupEKsByDomainIdsReq req;
|
2022-04-12 00:08:42 +08:00
|
|
|
req.encryptDomainIds.reserve(ekpProxyData->baseCipherDomainIdCache.size());
|
|
|
|
|
|
|
|
for (auto& item : ekpProxyData->baseCipherDomainIdCache) {
|
|
|
|
req.encryptDomainIds.emplace_back(item.first);
|
|
|
|
}
|
2022-04-22 23:53:39 +08:00
|
|
|
KmsConnLookupEKsByDomainIdsRep rep = wait(kmsConnectorInf.ekLookupByDomainIds.getReply(req));
|
|
|
|
for (auto& item : rep.cipherKeyDetails) {
|
2022-04-22 05:56:09 +08:00
|
|
|
ekpProxyData->insertIntoBaseDomainIdCache(item.encryptDomainId, item.encryptKeyId, item.encryptKey);
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
ekpProxyData->baseCipherKeysRefreshed += rep.cipherKeyDetails.size();
|
|
|
|
TraceEvent("RefreshEKs_Done", ekpProxyData->myId).detail("KeyCount", rep.cipherKeyDetails.size());
|
2022-04-12 00:08:42 +08:00
|
|
|
} catch (Error& e) {
|
|
|
|
if (!canReplyWith(e)) {
|
|
|
|
TraceEvent("RefreshEncryptionKeys_Error").error(e);
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
TraceEvent("RefreshEncryptionKeys").detail("ErrorCode", e.code());
|
|
|
|
++ekpProxyData->numEncryptionKeyRefreshErrors;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Void();
|
|
|
|
}
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
void refreshEncryptionKeys(Reference<EncryptKeyProxyData> ekpProxyData, KmsConnectorInterface kmsConnectorInf) {
|
|
|
|
Future<Void> ignored = refreshEncryptionKeysCore(ekpProxyData, kmsConnectorInf);
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
void activateKmsConnector(Reference<EncryptKeyProxyData> ekpProxyData, KmsConnectorInterface kmsConnectorInf) {
|
2022-04-12 00:08:42 +08:00
|
|
|
if (g_network->isSimulated()) {
|
2022-04-22 23:53:39 +08:00
|
|
|
ekpProxyData->kmsConnector = std::make_unique<SimKmsConnector>();
|
2022-05-08 04:18:35 +08:00
|
|
|
} else if (SERVER_KNOBS->KMS_CONNECTOR_TYPE.compare("RESTKmsConnector")) {
|
|
|
|
ekpProxyData->kmsConnector = std::make_unique<RESTKmsConnector>();
|
2022-04-12 00:08:42 +08:00
|
|
|
} else {
|
2022-04-22 23:53:39 +08:00
|
|
|
throw not_implemented();
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
2022-04-22 23:53:39 +08:00
|
|
|
|
|
|
|
TraceEvent("EKP_ActiveKmsConnector", ekpProxyData->myId).detail("ConnectorType", SERVER_KNOBS->KMS_CONNECTOR_TYPE);
|
|
|
|
ekpProxyData->addActor.send(ekpProxyData->kmsConnector->connectorCore(kmsConnectorInf));
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
|
|
|
|
2022-01-14 03:11:01 +08:00
|
|
|
ACTOR Future<Void> encryptKeyProxyServer(EncryptKeyProxyInterface ekpInterface, Reference<AsyncVar<ServerDBInfo>> db) {
|
|
|
|
state Reference<EncryptKeyProxyData> self(new EncryptKeyProxyData(ekpInterface.id()));
|
|
|
|
state Future<Void> collection = actorCollection(self->addActor.getFuture());
|
|
|
|
self->addActor.send(traceRole(Role::ENCRYPT_KEY_PROXY, ekpInterface.id()));
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
state KmsConnectorInterface kmsConnectorInf;
|
|
|
|
kmsConnectorInf.initEndpoints();
|
2022-04-12 00:08:42 +08:00
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
TraceEvent("EKP_Start", self->myId).detail("KmsConnectorInf", kmsConnectorInf.id());
|
|
|
|
|
|
|
|
activateKmsConnector(self, kmsConnectorInf);
|
2022-04-12 00:08:42 +08:00
|
|
|
|
|
|
|
// 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:
|
|
|
|
// CPs, Redwood Storage Server node flush etc. The process doing the encryption refresh the cached cipher keys based
|
|
|
|
// on FLOW_KNOB->ENCRYPTION_CIPHER_KEY_CACHE_TTL_SEC interval which is intentionally kept longer than
|
|
|
|
// 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.
|
|
|
|
|
2022-04-22 23:53:39 +08:00
|
|
|
self->encryptionKeyRefresher = recurring([&]() { refreshEncryptionKeys(self, kmsConnectorInf); },
|
|
|
|
FLOW_KNOBS->ENCRYPT_KEY_REFRESH_INTERVAL,
|
|
|
|
TaskPriority::Worker);
|
2022-01-14 03:11:01 +08:00
|
|
|
|
|
|
|
try {
|
|
|
|
loop choose {
|
2022-04-12 00:08:42 +08:00
|
|
|
when(EKPGetBaseCipherKeysByIdsRequest req = waitNext(ekpInterface.getBaseCipherKeysByIds.getFuture())) {
|
2022-04-22 23:53:39 +08:00
|
|
|
wait(getCipherKeysByBaseCipherKeyIds(self, kmsConnectorInf, req));
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
|
|
|
when(EKPGetLatestBaseCipherKeysRequest req = waitNext(ekpInterface.getLatestBaseCipherKeys.getFuture())) {
|
2022-04-22 23:53:39 +08:00
|
|
|
wait(getLatestCipherKeys(self, kmsConnectorInf, req));
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|
2022-01-14 03:11:01 +08:00
|
|
|
when(HaltEncryptKeyProxyRequest req = waitNext(ekpInterface.haltEncryptKeyProxy.getFuture())) {
|
2022-04-12 00:08:42 +08:00
|
|
|
TraceEvent("EKP_Halted", self->myId).detail("ReqID", req.requesterID);
|
2022-01-14 03:11:01 +08:00
|
|
|
req.reply.send(Void());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
when(wait(collection)) {
|
|
|
|
ASSERT(false);
|
|
|
|
throw internal_error();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Error& e) {
|
2022-04-12 00:08:42 +08:00
|
|
|
TraceEvent("EKP_Terminated", self->myId).errorUnsuppressed(e);
|
2022-01-14 03:11:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return Void();
|
2022-04-12 00:08:42 +08:00
|
|
|
}
|