1
0
mirror of https://github.com/apple/foundationdb.git synced 2025-05-27 18:37:04 +08:00

Support passing encryption file to BackupContainer::openContainer

This commit is contained in:
sfc-gh-tclinkenbeard 2021-06-25 14:11:21 -07:00
parent 5858ca3c62
commit 53f5cd2453
10 changed files with 86 additions and 31 deletions

@ -362,20 +362,22 @@ public:
Key outContainer, Key outContainer,
int initialSnapshotIntervalSeconds, int initialSnapshotIntervalSeconds,
int snapshotIntervalSeconds, int snapshotIntervalSeconds,
std::string tagName, std::string const& tagName,
Standalone<VectorRef<KeyRangeRef>> backupRanges, Standalone<VectorRef<KeyRangeRef>> backupRanges,
bool stopWhenDone = true, bool stopWhenDone = true,
bool partitionedLog = false, bool partitionedLog = false,
bool incrementalBackupOnly = false); bool incrementalBackupOnly = false,
Optional<std::string> const& encryptionKeyFileName = {});
Future<Void> submitBackup(Database cx, Future<Void> submitBackup(Database cx,
Key outContainer, Key outContainer,
int initialSnapshotIntervalSeconds, int initialSnapshotIntervalSeconds,
int snapshotIntervalSeconds, int snapshotIntervalSeconds,
std::string tagName, std::string const& tagName,
Standalone<VectorRef<KeyRangeRef>> backupRanges, Standalone<VectorRef<KeyRangeRef>> backupRanges,
bool stopWhenDone = true, bool stopWhenDone = true,
bool partitionedLog = false, bool partitionedLog = false,
bool incrementalBackupOnly = false) { bool incrementalBackupOnly = false,
Optional<std::string> const& encryptionKeyFileName = {}) {
return runRYWTransactionFailIfLocked(cx, [=](Reference<ReadYourWritesTransaction> tr) { return runRYWTransactionFailIfLocked(cx, [=](Reference<ReadYourWritesTransaction> tr) {
return submitBackup(tr, return submitBackup(tr,
outContainer, outContainer,
@ -385,7 +387,8 @@ public:
backupRanges, backupRanges,
stopWhenDone, stopWhenDone,
partitionedLog, partitionedLog,
incrementalBackupOnly); incrementalBackupOnly,
encryptionKeyFileName);
}); });
} }

@ -253,7 +253,8 @@ std::vector<std::string> IBackupContainer::getURLFormats() {
} }
// Get an IBackupContainer based on a container URL string // Get an IBackupContainer based on a container URL string
Reference<IBackupContainer> IBackupContainer::openContainer(const std::string& url) { Reference<IBackupContainer> IBackupContainer::openContainer(const std::string& url,
Optional<std::string> const& encryptionKeyFileName) {
static std::map<std::string, Reference<IBackupContainer>> m_cache; static std::map<std::string, Reference<IBackupContainer>> m_cache;
Reference<IBackupContainer>& r = m_cache[url]; Reference<IBackupContainer>& r = m_cache[url];
@ -263,7 +264,7 @@ Reference<IBackupContainer> IBackupContainer::openContainer(const std::string& u
try { try {
StringRef u(url); StringRef u(url);
if (u.startsWith(LiteralStringRef("file://"))) { if (u.startsWith(LiteralStringRef("file://"))) {
r = Reference<IBackupContainer>(new BackupContainerLocalDirectory(url)); r = makeReference<BackupContainerLocalDirectory>(url, encryptionKeyFileName);
} else if (u.startsWith(LiteralStringRef("blobstore://"))) { } else if (u.startsWith(LiteralStringRef("blobstore://"))) {
std::string resource; std::string resource;

@ -293,7 +293,8 @@ public:
Version beginVersion = -1) = 0; Version beginVersion = -1) = 0;
// Get an IBackupContainer based on a container spec string // Get an IBackupContainer based on a container spec string
static Reference<IBackupContainer> openContainer(const std::string& url); static Reference<IBackupContainer> openContainer(const std::string& url,
const Optional<std::string>& encryptionKeyFileName = {});
static std::vector<std::string> getURLFormats(); static std::vector<std::string> getURLFormats();
static Future<std::vector<std::string>> listContainers(const std::string& baseURL); static Future<std::vector<std::string>> listContainers(const std::string& baseURL);

@ -23,6 +23,7 @@
#include "fdbrpc/IAsyncFile.h" #include "fdbrpc/IAsyncFile.h"
#include "flow/Platform.actor.h" #include "flow/Platform.actor.h"
#include "flow/Platform.h" #include "flow/Platform.h"
#include "flow/StreamCipher.h"
#include "fdbrpc/simulator.h" #include "fdbrpc/simulator.h"
#include "flow/actorcompiler.h" // This must be the last #include. #include "flow/actorcompiler.h" // This must be the last #include.
@ -131,7 +132,29 @@ std::string BackupContainerLocalDirectory::getURLFormat() {
return "file://</path/to/base/dir/>"; return "file://</path/to/base/dir/>";
} }
BackupContainerLocalDirectory::BackupContainerLocalDirectory(const std::string& url) { ACTOR static Future<Void> readEncryptionKey(std::string encryptionKeyFileName) {
state Reference<IAsyncFile> keyFile = wait(IAsyncFileSystem::filesystem()->open(encryptionKeyFileName, 0x0, 0400));
int64_t fileSize = wait(keyFile->size());
// TODO: Use new error code and avoid hard-coding expected size
if (fileSize != 16) {
throw internal_error();
}
state std::array<uint8_t, 16> key;
wait(success(keyFile->read(key.data(), key.size(), 0)));
StreamCipher::Key::initializeKey(std::move(key));
return Void();
}
bool BackupContainerLocalDirectory::usesEncryption() const {
return encryptionSetupFuture.isValid();
}
BackupContainerLocalDirectory::BackupContainerLocalDirectory(const std::string& url,
const Optional<std::string>& encryptionKeyFileName) {
if (encryptionKeyFileName.present()) {
encryptionSetupFuture = readEncryptionKey(encryptionKeyFileName.get());
}
std::string path; std::string path;
if (url.find("file://") != 0) { if (url.find("file://") != 0) {
TraceEvent(SevWarn, "BackupContainerLocalDirectory") TraceEvent(SevWarn, "BackupContainerLocalDirectory")
@ -207,6 +230,9 @@ Future<bool> BackupContainerLocalDirectory::exists() {
Future<Reference<IAsyncFile>> BackupContainerLocalDirectory::readFile(const std::string& path) { Future<Reference<IAsyncFile>> BackupContainerLocalDirectory::readFile(const std::string& path) {
int flags = IAsyncFile::OPEN_NO_AIO | IAsyncFile::OPEN_READONLY | IAsyncFile::OPEN_UNCACHED; int flags = IAsyncFile::OPEN_NO_AIO | IAsyncFile::OPEN_READONLY | IAsyncFile::OPEN_UNCACHED;
if (usesEncryption()) {
flags |= IAsyncFile::OPEN_ENCRYPTED;
}
// Simulation does not properly handle opening the same file from multiple machines using a shared filesystem, // Simulation does not properly handle opening the same file from multiple machines using a shared filesystem,
// so create a symbolic link to make each file opening appear to be unique. This could also work in production // so create a symbolic link to make each file opening appear to be unique. This could also work in production
// but only if the source directory is writeable which shouldn't be required for a restore. // but only if the source directory is writeable which shouldn't be required for a restore.
@ -260,6 +286,9 @@ Future<Reference<IAsyncFile>> BackupContainerLocalDirectory::readFile(const std:
Future<Reference<IBackupFile>> BackupContainerLocalDirectory::writeFile(const std::string& path) { Future<Reference<IBackupFile>> BackupContainerLocalDirectory::writeFile(const std::string& path) {
int flags = IAsyncFile::OPEN_NO_AIO | IAsyncFile::OPEN_UNCACHED | IAsyncFile::OPEN_CREATE | IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE | int flags = IAsyncFile::OPEN_NO_AIO | IAsyncFile::OPEN_UNCACHED | IAsyncFile::OPEN_CREATE | IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE |
IAsyncFile::OPEN_READWRITE; IAsyncFile::OPEN_READWRITE;
if (usesEncryption()) {
flags |= IAsyncFile::OPEN_ENCRYPTED;
}
std::string fullPath = joinPath(m_path, path); std::string fullPath = joinPath(m_path, path);
platform::createDirectory(parentDirectory(fullPath)); platform::createDirectory(parentDirectory(fullPath));
std::string temp = fullPath + "." + deterministicRandom()->randomUniqueID().toString() + ".temp"; std::string temp = fullPath + "." + deterministicRandom()->randomUniqueID().toString() + ".temp";

@ -33,7 +33,7 @@ public:
static std::string getURLFormat(); static std::string getURLFormat();
BackupContainerLocalDirectory(const std::string& url); BackupContainerLocalDirectory(const std::string& url, Optional<std::string> const& encryptionKeyFileName);
static Future<std::vector<std::string>> listURLs(const std::string& url); static Future<std::vector<std::string>> listURLs(const std::string& url);
@ -54,6 +54,8 @@ public:
private: private:
std::string m_path; std::string m_path;
Future<Void> encryptionSetupFuture;
bool usesEncryption() const;
}; };
#endif #endif

@ -4506,6 +4506,7 @@ public:
} }
} }
// TODO: Get rid of all of these confusing boolean flags
ACTOR static Future<Void> submitBackup(FileBackupAgent* backupAgent, ACTOR static Future<Void> submitBackup(FileBackupAgent* backupAgent,
Reference<ReadYourWritesTransaction> tr, Reference<ReadYourWritesTransaction> tr,
Key outContainer, Key outContainer,
@ -4515,7 +4516,8 @@ public:
Standalone<VectorRef<KeyRangeRef>> backupRanges, Standalone<VectorRef<KeyRangeRef>> backupRanges,
bool stopWhenDone, bool stopWhenDone,
bool partitionedLog, bool partitionedLog,
bool incrementalBackupOnly) { bool incrementalBackupOnly,
Optional<std::string> encryptionKeyFileName) {
tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS);
tr->setOption(FDBTransactionOptions::LOCK_AWARE); tr->setOption(FDBTransactionOptions::LOCK_AWARE);
tr->setOption(FDBTransactionOptions::COMMIT_ON_FIRST_PROXY); tr->setOption(FDBTransactionOptions::COMMIT_ON_FIRST_PROXY);
@ -4553,7 +4555,7 @@ public:
backupContainer = joinPath(backupContainer, std::string("backup-") + nowStr.toString()); backupContainer = joinPath(backupContainer, std::string("backup-") + nowStr.toString());
} }
state Reference<IBackupContainer> bc = IBackupContainer::openContainer(backupContainer); state Reference<IBackupContainer> bc = IBackupContainer::openContainer(backupContainer, encryptionKeyFileName);
try { try {
wait(timeoutError(bc->create(), 30)); wait(timeoutError(bc->create(), 30));
} catch (Error& e) { } catch (Error& e) {
@ -5631,11 +5633,12 @@ Future<Void> FileBackupAgent::submitBackup(Reference<ReadYourWritesTransaction>
Key outContainer, Key outContainer,
int initialSnapshotIntervalSeconds, int initialSnapshotIntervalSeconds,
int snapshotIntervalSeconds, int snapshotIntervalSeconds,
std::string tagName, std::string const& tagName,
Standalone<VectorRef<KeyRangeRef>> backupRanges, Standalone<VectorRef<KeyRangeRef>> backupRanges,
bool stopWhenDone, bool stopWhenDone,
bool partitionedLog, bool partitionedLog,
bool incrementalBackupOnly) { bool incrementalBackupOnly,
Optional<std::string> const& encryptionKeyFileName) {
return FileBackupAgentImpl::submitBackup(this, return FileBackupAgentImpl::submitBackup(this,
tr, tr,
outContainer, outContainer,
@ -5645,7 +5648,8 @@ Future<Void> FileBackupAgent::submitBackup(Reference<ReadYourWritesTransaction>
backupRanges, backupRanges,
stopWhenDone, stopWhenDone,
partitionedLog, partitionedLog,
incrementalBackupOnly); incrementalBackupOnly,
encryptionKeyFileName);
} }
Future<Void> FileBackupAgent::discontinueBackup(Reference<ReadYourWritesTransaction> tr, Key tagName) { Future<Void> FileBackupAgent::discontinueBackup(Reference<ReadYourWritesTransaction> tr, Key tagName) {
@ -5739,8 +5743,8 @@ ACTOR static Future<Void> writeKVs(Database cx, Standalone<VectorRef<KeyValueRef
state ReadYourWritesTransaction tr(cx); state ReadYourWritesTransaction tr(cx);
loop { loop {
try { try {
tr.setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr.setOption(FDBTransactionOptions::READ_SYSTEM_KEYS);
tr.setOption(FDBTransactionOptions::LOCK_AWARE); tr.setOption(FDBTransactionOptions::READ_LOCK_AWARE);
KeyRef k1 = kvs[begin].key; KeyRef k1 = kvs[begin].key;
KeyRef k2 = end < kvs.size() ? kvs[end].key : normalKeys.end; KeyRef k2 = end < kvs.size() ? kvs[end].key : normalKeys.end;
TraceEvent(SevFRTestInfo, "TransformDatabaseContentsWriteKVReadBack") TraceEvent(SevFRTestInfo, "TransformDatabaseContentsWriteKVReadBack")

@ -241,11 +241,12 @@ TEST_CASE("fdbrpc/AsyncFileEncrypted") {
generateRandomData(&writeBuffer.front(), bytes); generateRandomData(&writeBuffer.front(), bytes);
state std::vector<unsigned char> readBuffer(bytes, 0); state std::vector<unsigned char> readBuffer(bytes, 0);
ASSERT(g_network->isSimulated()); ASSERT(g_network->isSimulated());
StreamCipher::Key::initializeRandomKey(); StreamCipher::Key::initializeRandomTestKey();
int flags = IAsyncFile::OPEN_READWRITE | IAsyncFile::OPEN_CREATE | IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE | int flags = IAsyncFile::OPEN_READWRITE | IAsyncFile::OPEN_CREATE | IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE |
IAsyncFile::OPEN_UNBUFFERED | IAsyncFile::OPEN_ENCRYPTED | IAsyncFile::OPEN_UNCACHED | IAsyncFile::OPEN_UNBUFFERED | IAsyncFile::OPEN_ENCRYPTED | IAsyncFile::OPEN_UNCACHED |
IAsyncFile::OPEN_NO_AIO; IAsyncFile::OPEN_NO_AIO;
state Reference<IAsyncFile> file = wait(IAsyncFileSystem::filesystem()->open(params.getDataDir(), flags, 0600)); state Reference<IAsyncFile> file =
wait(IAsyncFileSystem::filesystem()->open(joinPath(params.getDataDir(), "test-encrypted-file"), flags, 0600));
state int bytesWritten = 0; state int bytesWritten = 0;
while (bytesWritten < bytes) { while (bytesWritten < bytes) {
chunkSize = std::min(deterministicRandom()->randomInt(0, 100), bytes - bytesWritten); chunkSize = std::min(deterministicRandom()->randomInt(0, 100), bytes - bytesWritten);

@ -18,8 +18,7 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef __FDBRPC_ASYNC_FILE_ENCRYPTED_H__ #pragma once
#define __FDBRPC_ASYNC_FILE_ENCRYPTED_H__
#include "fdbrpc/IAsyncFile.h" #include "fdbrpc/IAsyncFile.h"
#include "flow/FastRef.h" #include "flow/FastRef.h"
@ -75,5 +74,3 @@ public:
void releaseZeroCopy(void* data, int length, int64_t offset) override; void releaseZeroCopy(void* data, int length, int64_t offset) override;
int64_t debugFD() const override; int64_t debugFD() const override;
}; };
#endif

@ -44,11 +44,18 @@ void StreamCipher::cleanup() noexcept {
} }
} }
void StreamCipher::Key::initializeRandomKey() { void StreamCipher::Key::initializeKey(std::array<unsigned char, 16>&& arr) {
ASSERT(!globalKey);
globalKey = std::make_unique<Key>(ConstructorTag{});
globalKey->arr = std::move(arr);
memset(arr.data(), 0, arr.size());
}
void StreamCipher::Key::initializeRandomTestKey() {
ASSERT(g_network->isSimulated()); ASSERT(g_network->isSimulated());
if (globalKey) return; if (globalKey) return;
globalKey = std::make_unique<Key>(ConstructorTag{}); globalKey = std::make_unique<Key>(ConstructorTag{});
generateRandomData(globalKey.get()->arr.data(), globalKey.get()->arr.size()); generateRandomData(globalKey->arr.data(), globalKey->arr.size());
} }
const StreamCipher::Key& StreamCipher::Key::getKey() { const StreamCipher::Key& StreamCipher::Key::getKey() {
@ -56,6 +63,16 @@ const StreamCipher::Key& StreamCipher::Key::getKey() {
return *globalKey; return *globalKey;
} }
StreamCipher::Key::Key(Key&& rhs) : arr(std::move(rhs.arr)) {
memset(arr.data(), 0, arr.size());
}
StreamCipher::Key& StreamCipher::Key::operator=(Key&& rhs) {
arr = std::move(rhs.arr);
memset(arr.data(), 0, arr.size());
return *this;
}
StreamCipher::Key::~Key() { StreamCipher::Key::~Key() {
memset(arr.data(), 0, arr.size()); memset(arr.data(), 0, arr.size());
} }
@ -111,7 +128,7 @@ void forceLinkStreamCipherTests() {}
// Tests both encryption and decryption of random data // Tests both encryption and decryption of random data
// using the StreamCipher class // using the StreamCipher class
TEST_CASE("flow/StreamCipher") { TEST_CASE("flow/StreamCipher") {
StreamCipher::Key::initializeRandomKey(); StreamCipher::Key::initializeRandomTestKey();
const auto& key = StreamCipher::Key::getKey(); const auto& key = StreamCipher::Key::getKey();
StreamCipher::IV iv; StreamCipher::IV iv;

@ -18,8 +18,7 @@
* limitations under the License. * limitations under the License.
*/ */
#ifndef __FLOW_STREAM_CIPHER_H__ #pragma once
#define __FLOW_STREAM_CIPHER_H__
#include "flow/Arena.h" #include "flow/Arena.h"
#include "flow/FastRef.h" #include "flow/FastRef.h"
@ -48,9 +47,12 @@ public:
public: public:
Key(ConstructorTag) {} Key(ConstructorTag) {}
Key(Key&&);
Key& operator=(Key&&);
~Key(); ~Key();
unsigned char const* data() const { return arr.data(); } unsigned char const* data() const { return arr.data(); }
static void initializeRandomKey(); static void initializeKey(decltype(arr)&&);
static void initializeRandomTestKey();
static const Key& getKey(); static const Key& getKey();
static void cleanup() noexcept; static void cleanup() noexcept;
}; };
@ -75,5 +77,3 @@ public:
StringRef decrypt(unsigned char const* ciphertext, int len, Arena&); StringRef decrypt(unsigned char const* ciphertext, int len, Arena&);
StringRef finish(Arena&); StringRef finish(Arena&);
}; };
#endif