From 53f5cd24534eb397afcd4651b8673c318df48f61 Mon Sep 17 00:00:00 2001 From: sfc-gh-tclinkenbeard Date: Fri, 25 Jun 2021 14:11:21 -0700 Subject: [PATCH] Support passing encryption file to BackupContainer::openContainer --- fdbclient/BackupAgent.actor.h | 13 +++++--- fdbclient/BackupContainer.actor.cpp | 5 +-- fdbclient/BackupContainer.h | 3 +- .../BackupContainerLocalDirectory.actor.cpp | 31 ++++++++++++++++++- fdbclient/BackupContainerLocalDirectory.h | 4 ++- fdbclient/FileBackupAgent.actor.cpp | 18 ++++++----- fdbrpc/AsyncFileEncrypted.actor.cpp | 5 +-- fdbrpc/AsyncFileEncrypted.h | 5 +-- flow/StreamCipher.cpp | 23 ++++++++++++-- flow/StreamCipher.h | 10 +++--- 10 files changed, 86 insertions(+), 31 deletions(-) diff --git a/fdbclient/BackupAgent.actor.h b/fdbclient/BackupAgent.actor.h index c8903b9fe4..a0222fbf65 100644 --- a/fdbclient/BackupAgent.actor.h +++ b/fdbclient/BackupAgent.actor.h @@ -362,20 +362,22 @@ public: Key outContainer, int initialSnapshotIntervalSeconds, int snapshotIntervalSeconds, - std::string tagName, + std::string const& tagName, Standalone> backupRanges, bool stopWhenDone = true, bool partitionedLog = false, - bool incrementalBackupOnly = false); + bool incrementalBackupOnly = false, + Optional const& encryptionKeyFileName = {}); Future submitBackup(Database cx, Key outContainer, int initialSnapshotIntervalSeconds, int snapshotIntervalSeconds, - std::string tagName, + std::string const& tagName, Standalone> backupRanges, bool stopWhenDone = true, bool partitionedLog = false, - bool incrementalBackupOnly = false) { + bool incrementalBackupOnly = false, + Optional const& encryptionKeyFileName = {}) { return runRYWTransactionFailIfLocked(cx, [=](Reference tr) { return submitBackup(tr, outContainer, @@ -385,7 +387,8 @@ public: backupRanges, stopWhenDone, partitionedLog, - incrementalBackupOnly); + incrementalBackupOnly, + encryptionKeyFileName); }); } diff --git a/fdbclient/BackupContainer.actor.cpp b/fdbclient/BackupContainer.actor.cpp index a71ab0c6ff..56357a1467 100644 --- a/fdbclient/BackupContainer.actor.cpp +++ b/fdbclient/BackupContainer.actor.cpp @@ -253,7 +253,8 @@ std::vector IBackupContainer::getURLFormats() { } // Get an IBackupContainer based on a container URL string -Reference IBackupContainer::openContainer(const std::string& url) { +Reference IBackupContainer::openContainer(const std::string& url, + Optional const& encryptionKeyFileName) { static std::map> m_cache; Reference& r = m_cache[url]; @@ -263,7 +264,7 @@ Reference IBackupContainer::openContainer(const std::string& u try { StringRef u(url); if (u.startsWith(LiteralStringRef("file://"))) { - r = Reference(new BackupContainerLocalDirectory(url)); + r = makeReference(url, encryptionKeyFileName); } else if (u.startsWith(LiteralStringRef("blobstore://"))) { std::string resource; diff --git a/fdbclient/BackupContainer.h b/fdbclient/BackupContainer.h index 2da1e50985..5a9af3d1d9 100644 --- a/fdbclient/BackupContainer.h +++ b/fdbclient/BackupContainer.h @@ -293,7 +293,8 @@ public: Version beginVersion = -1) = 0; // Get an IBackupContainer based on a container spec string - static Reference openContainer(const std::string& url); + static Reference openContainer(const std::string& url, + const Optional& encryptionKeyFileName = {}); static std::vector getURLFormats(); static Future> listContainers(const std::string& baseURL); diff --git a/fdbclient/BackupContainerLocalDirectory.actor.cpp b/fdbclient/BackupContainerLocalDirectory.actor.cpp index e0c78a31bf..d94c92441a 100644 --- a/fdbclient/BackupContainerLocalDirectory.actor.cpp +++ b/fdbclient/BackupContainerLocalDirectory.actor.cpp @@ -23,6 +23,7 @@ #include "fdbrpc/IAsyncFile.h" #include "flow/Platform.actor.h" #include "flow/Platform.h" +#include "flow/StreamCipher.h" #include "fdbrpc/simulator.h" #include "flow/actorcompiler.h" // This must be the last #include. @@ -131,7 +132,29 @@ std::string BackupContainerLocalDirectory::getURLFormat() { return "file://"; } -BackupContainerLocalDirectory::BackupContainerLocalDirectory(const std::string& url) { +ACTOR static Future readEncryptionKey(std::string encryptionKeyFileName) { + state Reference 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 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& encryptionKeyFileName) { + if (encryptionKeyFileName.present()) { + encryptionSetupFuture = readEncryptionKey(encryptionKeyFileName.get()); + } + std::string path; if (url.find("file://") != 0) { TraceEvent(SevWarn, "BackupContainerLocalDirectory") @@ -207,6 +230,9 @@ Future BackupContainerLocalDirectory::exists() { Future> BackupContainerLocalDirectory::readFile(const std::string& path) { 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, // 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. @@ -260,6 +286,9 @@ Future> BackupContainerLocalDirectory::readFile(const std: Future> BackupContainerLocalDirectory::writeFile(const std::string& path) { int flags = IAsyncFile::OPEN_NO_AIO | IAsyncFile::OPEN_UNCACHED | IAsyncFile::OPEN_CREATE | IAsyncFile::OPEN_ATOMIC_WRITE_AND_CREATE | IAsyncFile::OPEN_READWRITE; + if (usesEncryption()) { + flags |= IAsyncFile::OPEN_ENCRYPTED; + } std::string fullPath = joinPath(m_path, path); platform::createDirectory(parentDirectory(fullPath)); std::string temp = fullPath + "." + deterministicRandom()->randomUniqueID().toString() + ".temp"; diff --git a/fdbclient/BackupContainerLocalDirectory.h b/fdbclient/BackupContainerLocalDirectory.h index 9db8e07aef..52cd810907 100644 --- a/fdbclient/BackupContainerLocalDirectory.h +++ b/fdbclient/BackupContainerLocalDirectory.h @@ -33,7 +33,7 @@ public: static std::string getURLFormat(); - BackupContainerLocalDirectory(const std::string& url); + BackupContainerLocalDirectory(const std::string& url, Optional const& encryptionKeyFileName); static Future> listURLs(const std::string& url); @@ -54,6 +54,8 @@ public: private: std::string m_path; + Future encryptionSetupFuture; + bool usesEncryption() const; }; #endif diff --git a/fdbclient/FileBackupAgent.actor.cpp b/fdbclient/FileBackupAgent.actor.cpp index 35d6743821..a730822f96 100644 --- a/fdbclient/FileBackupAgent.actor.cpp +++ b/fdbclient/FileBackupAgent.actor.cpp @@ -4506,6 +4506,7 @@ public: } } + // TODO: Get rid of all of these confusing boolean flags ACTOR static Future submitBackup(FileBackupAgent* backupAgent, Reference tr, Key outContainer, @@ -4515,7 +4516,8 @@ public: Standalone> backupRanges, bool stopWhenDone, bool partitionedLog, - bool incrementalBackupOnly) { + bool incrementalBackupOnly, + Optional encryptionKeyFileName) { tr->setOption(FDBTransactionOptions::ACCESS_SYSTEM_KEYS); tr->setOption(FDBTransactionOptions::LOCK_AWARE); tr->setOption(FDBTransactionOptions::COMMIT_ON_FIRST_PROXY); @@ -4553,7 +4555,7 @@ public: backupContainer = joinPath(backupContainer, std::string("backup-") + nowStr.toString()); } - state Reference bc = IBackupContainer::openContainer(backupContainer); + state Reference bc = IBackupContainer::openContainer(backupContainer, encryptionKeyFileName); try { wait(timeoutError(bc->create(), 30)); } catch (Error& e) { @@ -5631,11 +5633,12 @@ Future FileBackupAgent::submitBackup(Reference Key outContainer, int initialSnapshotIntervalSeconds, int snapshotIntervalSeconds, - std::string tagName, + std::string const& tagName, Standalone> backupRanges, bool stopWhenDone, bool partitionedLog, - bool incrementalBackupOnly) { + bool incrementalBackupOnly, + Optional const& encryptionKeyFileName) { return FileBackupAgentImpl::submitBackup(this, tr, outContainer, @@ -5645,7 +5648,8 @@ Future FileBackupAgent::submitBackup(Reference backupRanges, stopWhenDone, partitionedLog, - incrementalBackupOnly); + incrementalBackupOnly, + encryptionKeyFileName); } Future FileBackupAgent::discontinueBackup(Reference tr, Key tagName) { @@ -5739,8 +5743,8 @@ ACTOR static Future writeKVs(Database cx, Standalone readBuffer(bytes, 0); ASSERT(g_network->isSimulated()); - StreamCipher::Key::initializeRandomKey(); + StreamCipher::Key::initializeRandomTestKey(); 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_NO_AIO; - state Reference file = wait(IAsyncFileSystem::filesystem()->open(params.getDataDir(), flags, 0600)); + state Reference file = + wait(IAsyncFileSystem::filesystem()->open(joinPath(params.getDataDir(), "test-encrypted-file"), flags, 0600)); state int bytesWritten = 0; while (bytesWritten < bytes) { chunkSize = std::min(deterministicRandom()->randomInt(0, 100), bytes - bytesWritten); diff --git a/fdbrpc/AsyncFileEncrypted.h b/fdbrpc/AsyncFileEncrypted.h index dc7f15f299..b474198e1c 100644 --- a/fdbrpc/AsyncFileEncrypted.h +++ b/fdbrpc/AsyncFileEncrypted.h @@ -18,8 +18,7 @@ * limitations under the License. */ -#ifndef __FDBRPC_ASYNC_FILE_ENCRYPTED_H__ -#define __FDBRPC_ASYNC_FILE_ENCRYPTED_H__ +#pragma once #include "fdbrpc/IAsyncFile.h" #include "flow/FastRef.h" @@ -75,5 +74,3 @@ public: void releaseZeroCopy(void* data, int length, int64_t offset) override; int64_t debugFD() const override; }; - -#endif diff --git a/flow/StreamCipher.cpp b/flow/StreamCipher.cpp index add9c8988a..e0066ae4a5 100644 --- a/flow/StreamCipher.cpp +++ b/flow/StreamCipher.cpp @@ -44,11 +44,18 @@ void StreamCipher::cleanup() noexcept { } } -void StreamCipher::Key::initializeRandomKey() { +void StreamCipher::Key::initializeKey(std::array&& arr) { + ASSERT(!globalKey); + globalKey = std::make_unique(ConstructorTag{}); + globalKey->arr = std::move(arr); + memset(arr.data(), 0, arr.size()); +} + +void StreamCipher::Key::initializeRandomTestKey() { ASSERT(g_network->isSimulated()); if (globalKey) return; globalKey = std::make_unique(ConstructorTag{}); - generateRandomData(globalKey.get()->arr.data(), globalKey.get()->arr.size()); + generateRandomData(globalKey->arr.data(), globalKey->arr.size()); } const StreamCipher::Key& StreamCipher::Key::getKey() { @@ -56,6 +63,16 @@ const StreamCipher::Key& StreamCipher::Key::getKey() { 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() { memset(arr.data(), 0, arr.size()); } @@ -111,7 +128,7 @@ void forceLinkStreamCipherTests() {} // Tests both encryption and decryption of random data // using the StreamCipher class TEST_CASE("flow/StreamCipher") { - StreamCipher::Key::initializeRandomKey(); + StreamCipher::Key::initializeRandomTestKey(); const auto& key = StreamCipher::Key::getKey(); StreamCipher::IV iv; diff --git a/flow/StreamCipher.h b/flow/StreamCipher.h index 0618b7585f..f91dc272ce 100644 --- a/flow/StreamCipher.h +++ b/flow/StreamCipher.h @@ -18,8 +18,7 @@ * limitations under the License. */ -#ifndef __FLOW_STREAM_CIPHER_H__ -#define __FLOW_STREAM_CIPHER_H__ +#pragma once #include "flow/Arena.h" #include "flow/FastRef.h" @@ -48,9 +47,12 @@ public: public: Key(ConstructorTag) {} + Key(Key&&); + Key& operator=(Key&&); ~Key(); unsigned char const* data() const { return arr.data(); } - static void initializeRandomKey(); + static void initializeKey(decltype(arr)&&); + static void initializeRandomTestKey(); static const Key& getKey(); static void cleanup() noexcept; }; @@ -75,5 +77,3 @@ public: StringRef decrypt(unsigned char const* ciphertext, int len, Arena&); StringRef finish(Arena&); }; - -#endif