diff --git a/docker-compose-lockbox.yml b/docker-compose-lockbox.yml index 96455cc2..d64f4b54 100644 --- a/docker-compose-lockbox.yml +++ b/docker-compose-lockbox.yml @@ -1,6 +1,49 @@ +# This docker-compose file is an example of how to start the lockbox service and its dependencies. +# First, you start the vault service, if you use it. The command is: +# docker compose -f docker-compose-lockbox.yml --profile hashicorp-container up --build +# Then, you start the Lockbox and Mercury server services. The command is: +# docker compose -f docker-compose-lockbox.yml --profile mercury up --build +# The Mercury service will start the Lockbox service and the Mercury server. The Lockbox service will connect to the vault service to get the seed value. + +# If you are not going to use the vault service, you do not need to run the first command. +# Change the environment variables to match your configuration. services: + vault: + profiles: + - hashicorp-container + image: hashicorp/vault + environment: + - VAULT_DEV_ROOT_TOKEN_ID=2bbz80mr1zm0tfrjwxyq7f48ofgky848 + ports: + - "8200:8200" + + vault-init: + profiles: + - hashicorp-container + image: curlimages/curl + depends_on: + - vault + command: > + sh -c ' + sleep 10 && + curl --header "X-Vault-Token: 2bbz80mr1zm0tfrjwxyq7f48ofgky848" \ + --header "Content-Type: application/json" \ + --request POST \ + --data '"'"'{"data": {"seed": "8b10a037120cf37441bd7623da2aa488c21889017ffb4f4d303b9dbcbada5bee"}}'"'"' \ + http://vault:8200/v1/secret/data/mercury-seed && + touch /tmp/healthy && + tail -f /dev/null + ' + healthcheck: + test: ["CMD-SHELL", "test -f /tmp/healthy"] + interval: 1s + timeout: 1s + retries: 30 + lockbox: + profiles: + - mercury build: context: lockbox dockerfile: ./Dockerfile @@ -9,17 +52,24 @@ services: environment: - LOCKBOX_DATABASE_URL=postgres://postgres:postgres@db_lockbox:5432/enclave - LOCKBOX_PORT=18080 - - KEY_MANAGER=filesystem - - SEED_FILEPATH=./seed + - KEY_MANAGER=hashicorp_container + - HASHICORP_CONTAINER_TOKEN=2bbz80mr1zm0tfrjwxyq7f48ofgky848 + - HASHICORP_CONTAINER_URL=http://vault:8200 + - HASHICORP_CONTAINER_PATH=mercury-seed + - HASHICORP_CONTAINER_MOUNT_POINT=secret + - HASHICORP_CONTAINER_KEY_NAME=seed ports: - "18080:18080" mercury-server: + profiles: + - mercury build: context: . dockerfile: server/Dockerfile depends_on: - db_server + - lockbox environment: BITCOIN_NETWORK: regtest LOCKHEIGHT_INIT: 1000 @@ -47,6 +97,8 @@ services: - "8000:8000" db_lockbox: + profiles: + - mercury image: postgres:16.2 environment: POSTGRES_PASSWORD: postgres @@ -57,6 +109,8 @@ services: - postgres_lockbox_data:/var/lib/postgresql/data db_server: + profiles: + - mercury image: postgres:16.2 environment: POSTGRES_PASSWORD: postgres diff --git a/lockbox/.env b/lockbox/.env index fd7c5aee..7b54077c 100644 --- a/lockbox/.env +++ b/lockbox/.env @@ -1,5 +1,5 @@ # general settings -# KEY_MANAGER can be one of: google_kms, hashicorp or filesystem +# KEY_MANAGER can be one of: google_kms, hashicorp_api, hashicorp_container or filesystem LOCKBOX_DATABASE_URL=postgresql://postgres:postgres@localhost/enclave LOCKBOX_PORT=18080 KEY_MANAGER=filesystem @@ -16,9 +16,15 @@ GCLOUD_KMS_RING=enclave GCLOUD_CRYPTO_KEY=sealing # hashicorp settings -# HASHICORP_HCP_CLIENT_ID= -# HASHICORP_HCP_CLIENT_SECRET= -# HASHICORP_ORGANIZATION_ID= -# HASHICORP_PROJECT_ID= -# HASHICORP_APP_NAME= -# HASHICORP_SECRET_NAME= +# HASHICORP_API_HCP_CLIENT_ID= +# HASHICORP_API_HCP_CLIENT_SECRET= +# HASHICORP_API_ORGANIZATION_ID= +# HASHICORP_API_PROJECT_ID= +# HASHICORP_API_APP_NAME= +# HASHICORP_API_SECRET_NAME= + +HASHICORP_CONTAINER_TOKEN=2bbz80mr1zm0tfrjwxyq7f48ofgky848 +HASHICORP_CONTAINER_URL=http://127.0.0.1:8200 +HASHICORP_CONTAINER_PATH=mercury-seed +HASHICORP_CONTAINER_MOUNT_POINT=secret +HASHICORP_CONTAINER_KEY_NAME=seed diff --git a/lockbox/CMakeLists.txt b/lockbox/CMakeLists.txt index 57d2c6b5..67b95d59 100644 --- a/lockbox/CMakeLists.txt +++ b/lockbox/CMakeLists.txt @@ -30,7 +30,8 @@ add_library(LockboxLibrary src/server.cpp src/utils.cpp src/google_key_manager.cpp - src/hashicorp_key_manager.cpp + src/hashicorp_api_key_manager.cpp + src/hashicorp_container_key_manager.cpp src/filesystem_key_manager.cpp src/enclave.cpp src/monocypher.c diff --git a/lockbox/Settings.toml b/lockbox/Settings.toml index 114a4f26..68a2b10b 100644 --- a/lockbox/Settings.toml +++ b/lockbox/Settings.toml @@ -1,7 +1,7 @@ [general] database_connection_string = "postgresql://postgres:postgres@localhost/enclave" server_port = 18080 -key_manager = "filesystem" # "google_kms", "hashicorp", "filesystem" +key_manager = "filesystem" # "google_kms", "hashicorp_container, "hashicorp_api", "filesystem" [filesystem] seed_filepath = "./seed" [gcloud] @@ -13,10 +13,16 @@ key_name = "encrypted-key" [kms] ring = "enclave" crypto_key = "sealing" -[hashicorp] +[hashicorp_api] hcp_client_id = "" hcp_client_secret = "" organization_id = "" project_id = "" app_name = "" secret_name = "" +[hashicorp_container] +token = "2bbz80mr1zm0tfrjwxyq7f48ofgky848" +url = "http://127.0.0.1:8200" +path = "mercury-seed" +mount_point = "secret" +key_name = "seed" diff --git a/lockbox/include/hashicorp_api_key_manager.h b/lockbox/include/hashicorp_api_key_manager.h new file mode 100644 index 00000000..66119a53 --- /dev/null +++ b/lockbox/include/hashicorp_api_key_manager.h @@ -0,0 +1,11 @@ +#ifndef HASHICORP_API_KEY_MANAGER_H +#define HASHICORP_API_KEY_MANAGER_H + +#include +#include + +namespace hashicorp_api_key_manager { + std::vector get_seed(); +} // namespace key_manager + +#endif // HASHICORP_API_KEY_MANAGER_H \ No newline at end of file diff --git a/lockbox/include/hashicorp_container_key_manager.h b/lockbox/include/hashicorp_container_key_manager.h new file mode 100644 index 00000000..1db2b90d --- /dev/null +++ b/lockbox/include/hashicorp_container_key_manager.h @@ -0,0 +1,11 @@ +#ifndef HASHICORP_CONTAINER_KEY_MANAGER_H +#define HASHICORP_CONTAINER_KEY_MANAGER_H + +#include +#include + +namespace hashicorp_container_key_manager { + std::vector get_seed(); +} // namespace key_manager + +#endif // HASHICORP_CONTAINER_KEY_MANAGER_H \ No newline at end of file diff --git a/lockbox/include/hashicorp_key_manager.h b/lockbox/include/hashicorp_key_manager.h deleted file mode 100644 index 914732ff..00000000 --- a/lockbox/include/hashicorp_key_manager.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef HASHICORP_KEY_MANAGER_H -#define HASHICORP_KEY_MANAGER_H - -#include -#include - -namespace hashicorp_key_manager { - std::vector get_seed(); -} // namespace key_manager - -#endif // HASHICORP_KEY_MANAGER_H \ No newline at end of file diff --git a/lockbox/include/utils.h b/lockbox/include/utils.h index 7b10f6b6..ff09138e 100644 --- a/lockbox/include/utils.h +++ b/lockbox/include/utils.h @@ -31,12 +31,18 @@ namespace utils { static const config_var GCLOUD_KMS_RING = {"GCLOUD_KMS_RING", "kms", "ring"}; static const config_var GCLOUD_CRYPTO_KEY = {"GCLOUD_CRYPTO_KEY", "kms", "crypto_key"}; - static const config_var HASHICORP_HCP_CLIENT_ID = {"HASHICORP_HCP_CLIENT_ID", "hashicorp", "hcp_client_id"}; - static const config_var HASHICORP_HCP_CLIENT_SECRET = {"HASHICORP_HCP_CLIENT_SECRET", "hashicorp", "hcp_client_secret"}; - static const config_var HASHICORP_ORGANIZATION_ID = {"HASHICORP_ORGANIZATION_ID", "hashicorp", "organization_id"}; - static const config_var HASHICORP_PROJECT_ID = {"HASHICORP_PROJECT_ID", "hashicorp", "project_id"}; - static const config_var HASHICORP_APP_NAME = {"HASHICORP_APP_NAME", "hashicorp", "app_name"}; - static const config_var HASHICORP_SECRET_NAME = {"HASHICORP_SECRET_NAME", "hashicorp", "secret_name"}; + static const config_var HASHICORP_API_HCP_CLIENT_ID = {"HASHICORP_API_HCP_CLIENT_ID", "hashicorp_api", "hcp_client_id"}; + static const config_var HASHICORP_API_HCP_CLIENT_SECRET = {"HASHICORP_API_HCP_CLIENT_SECRET", "hashicorp_api", "hcp_client_secret"}; + static const config_var HASHICORP_API_ORGANIZATION_ID = {"HASHICORP_API_ORGANIZATION_ID", "hashicorp_api", "organization_id"}; + static const config_var HASHICORP_API_PROJECT_ID = {"HASHICORP_API_PROJECT_ID", "hashicorp_api", "project_id"}; + static const config_var HASHICORP_API_APP_NAME = {"HASHICORP_API_APP_NAME", "hashicorp_api", "app_name"}; + static const config_var HASHICORP_API_SECRET_NAME = {"HASHICORP_API_SECRET_NAME", "hashicorp_api", "secret_name"}; + + static const config_var HASHICORP_CONTAINER_TOKEN = {"HASHICORP_CONTAINER_TOKEN", "hashicorp_container", "token"}; + static const config_var HASHICORP_CONTAINER_URL = {"HASHICORP_CONTAINER_URL", "hashicorp_container", "url"}; + static const config_var HASHICORP_CONTAINER_PATH = {"HASHICORP_CONTAINER_PATH", "hashicorp_container", "path"}; + static const config_var HASHICORP_CONTAINER_MOUNT_POINT = {"HASHICORP_CONTAINER_MOUNT_POINT", "hashicorp_container", "mount_point"}; + static const config_var HASHICORP_CONTAINER_KEY_NAME = {"HASHICORP_CONTAINER_KEY_NAME", "hashicorp_container", "key_name"}; struct chacha20_poly1305_encrypted_data { size_t data_len; @@ -57,6 +63,8 @@ namespace utils { std::string getStringConfigVar(const std::string& env_var, const std::string& toml_var_1, const std::string& toml_var_2); + std::string getStringConfigVar(const config_var& config_variable); + } // namespace utils #endif // UTILS_H \ No newline at end of file diff --git a/lockbox/src/db_manager.cpp b/lockbox/src/db_manager.cpp index e8aeb0b5..566e1e35 100644 --- a/lockbox/src/db_manager.cpp +++ b/lockbox/src/db_manager.cpp @@ -10,8 +10,7 @@ namespace db_manager { std::string getDatabaseConnectionString() { - return utils::getStringConfigVar( - utils::DATABASE_URL.env_var, utils::DATABASE_URL.toml_var_1, utils::DATABASE_URL.toml_var_2); + return utils::getStringConfigVar(utils::DATABASE_URL); } // Assumes the buffer is large enough. In a real application, ensure buffer safety. diff --git a/lockbox/src/filesystem_key_manager.cpp b/lockbox/src/filesystem_key_manager.cpp index 54ea0b85..a14a2efe 100644 --- a/lockbox/src/filesystem_key_manager.cpp +++ b/lockbox/src/filesystem_key_manager.cpp @@ -1,4 +1,4 @@ -#include "hashicorp_key_manager.h" +#include "filesystem_key_manager.h" #include #include @@ -11,8 +11,7 @@ namespace filesystem_key_manager { std::string getSeedFilePath() { - return utils::getStringConfigVar( - utils::SEED_FILEPATH.env_var, utils::SEED_FILEPATH.toml_var_1, utils::SEED_FILEPATH.toml_var_2); + return utils::getStringConfigVar(utils::SEED_FILEPATH); } std::vector get_seed() { diff --git a/lockbox/src/google_key_manager.cpp b/lockbox/src/google_key_manager.cpp index be9357db..4633a2e7 100644 --- a/lockbox/src/google_key_manager.cpp +++ b/lockbox/src/google_key_manager.cpp @@ -10,14 +10,11 @@ namespace key_manager { std::string get_encrypted_secret() { - auto project_id = utils::getStringConfigVar( - utils::GCLOUD_PROJECT_ID.env_var, utils::GCLOUD_PROJECT_ID.toml_var_1, utils::GCLOUD_PROJECT_ID.toml_var_2); + auto project_id = utils::getStringConfigVar(utils::GCLOUD_PROJECT_ID); - auto project_number = utils::getStringConfigVar( - utils::GCLOUD_PROJECT_NUMBER.env_var, utils::GCLOUD_PROJECT_NUMBER.toml_var_1, utils::GCLOUD_PROJECT_NUMBER.toml_var_2); + auto project_number = utils::getStringConfigVar(utils::GCLOUD_PROJECT_NUMBER); - auto key_name = utils::getStringConfigVar( - utils::GCLOUD_SECRET_MANAGER_KEY_NAME.env_var, utils::GCLOUD_SECRET_MANAGER_KEY_NAME.toml_var_1, utils::GCLOUD_SECRET_MANAGER_KEY_NAME.toml_var_2); + auto key_name = utils::getStringConfigVar(utils::GCLOUD_SECRET_MANAGER_KEY_NAME); auto version = 1; @@ -37,17 +34,13 @@ namespace key_manager { std::string decrypt_secret(std::string const& encrypted_secret) { - auto location_id = utils::getStringConfigVar( - utils::GCLOUD_LOCATION_ID.env_var, utils::GCLOUD_LOCATION_ID.toml_var_1, utils::GCLOUD_LOCATION_ID.toml_var_2); + auto location_id = utils::getStringConfigVar(utils::GCLOUD_LOCATION_ID); - auto project_id = utils::getStringConfigVar( - utils::GCLOUD_PROJECT_ID.env_var, utils::GCLOUD_PROJECT_ID.toml_var_1, utils::GCLOUD_PROJECT_ID.toml_var_2); + auto project_id = utils::getStringConfigVar(utils::GCLOUD_PROJECT_ID); - auto key_ring = utils::getStringConfigVar( - utils::GCLOUD_KMS_RING.env_var, utils::GCLOUD_KMS_RING.toml_var_1, utils::GCLOUD_KMS_RING.toml_var_2); + auto key_ring = utils::getStringConfigVar(utils::GCLOUD_KMS_RING); - auto crypto_key = utils::getStringConfigVar( - utils::GCLOUD_CRYPTO_KEY.env_var, utils::GCLOUD_CRYPTO_KEY.toml_var_1, utils::GCLOUD_CRYPTO_KEY.toml_var_2); + auto crypto_key = utils::getStringConfigVar(utils::GCLOUD_CRYPTO_KEY); namespace kms = ::google::cloud::kms_v1; auto client = kms::KeyManagementServiceClient(kms::MakeKeyManagementServiceConnection()); diff --git a/lockbox/src/hashicorp_key_manager.cpp b/lockbox/src/hashicorp_api_key_manager.cpp similarity index 76% rename from lockbox/src/hashicorp_key_manager.cpp rename to lockbox/src/hashicorp_api_key_manager.cpp index ff5df759..e4ba3372 100644 --- a/lockbox/src/hashicorp_key_manager.cpp +++ b/lockbox/src/hashicorp_api_key_manager.cpp @@ -1,19 +1,17 @@ -#include "hashicorp_key_manager.h" +#include "hashicorp_api_key_manager.h" #include #include #include #include "utils.h" -namespace hashicorp_key_manager { +namespace hashicorp_api_key_manager { std::string get_access_token() { - const std::string client_id = utils::getStringConfigVar( - utils::HASHICORP_HCP_CLIENT_ID.env_var, utils::HASHICORP_HCP_CLIENT_ID.toml_var_1, utils::HASHICORP_HCP_CLIENT_ID.toml_var_2); + const std::string client_id = utils::getStringConfigVar(utils::HASHICORP_API_HCP_CLIENT_ID); - const std::string client_secret = utils::getStringConfigVar( - utils::HASHICORP_HCP_CLIENT_SECRET.env_var, utils::HASHICORP_HCP_CLIENT_SECRET.toml_var_1, utils::HASHICORP_HCP_CLIENT_SECRET.toml_var_2); + const std::string client_secret = utils::getStringConfigVar(utils::HASHICORP_API_HCP_CLIENT_SECRET); // Make the HTTP POST request using cpr cpr::Response response = cpr::Post( @@ -50,17 +48,13 @@ namespace hashicorp_key_manager { std::string get_secret(const std::string& hcp_api_token) { - const std::string organization_id = utils::getStringConfigVar( - utils::HASHICORP_ORGANIZATION_ID.env_var, utils::HASHICORP_ORGANIZATION_ID.toml_var_1, utils::HASHICORP_ORGANIZATION_ID.toml_var_2); + const std::string organization_id = utils::getStringConfigVar(utils::HASHICORP_API_ORGANIZATION_ID); - const std::string project_id = utils::getStringConfigVar( - utils::HASHICORP_PROJECT_ID.env_var, utils::HASHICORP_PROJECT_ID.toml_var_1, utils::HASHICORP_PROJECT_ID.toml_var_2); + const std::string project_id = utils::getStringConfigVar(utils::HASHICORP_API_PROJECT_ID); - const std::string app_name = utils::getStringConfigVar( - utils::HASHICORP_APP_NAME.env_var, utils::HASHICORP_APP_NAME.toml_var_1, utils::HASHICORP_APP_NAME.toml_var_2); + const std::string app_name = utils::getStringConfigVar(utils::HASHICORP_API_APP_NAME); - const std::string secret_name = utils::getStringConfigVar( - utils::HASHICORP_SECRET_NAME.env_var, utils::HASHICORP_SECRET_NAME.toml_var_1, utils::HASHICORP_SECRET_NAME.toml_var_2); + const std::string secret_name = utils::getStringConfigVar(utils::HASHICORP_API_SECRET_NAME); // Construct the URL std::string url = "https://api.cloud.hashicorp.com/secrets/2023-11-28/organizations/" + organization_id + diff --git a/lockbox/src/hashicorp_container_key_manager.cpp b/lockbox/src/hashicorp_container_key_manager.cpp new file mode 100644 index 00000000..c9d79a2b --- /dev/null +++ b/lockbox/src/hashicorp_container_key_manager.cpp @@ -0,0 +1,49 @@ +#include "hashicorp_container_key_manager.h" + +#include +#include +#include +#include "utils.h" + +namespace hashicorp_container_key_manager { + + std::string get_secret() { + + const std::string token = utils::getStringConfigVar(utils::HASHICORP_CONTAINER_TOKEN); + const std::string container_url = utils::getStringConfigVar(utils::HASHICORP_CONTAINER_URL); + const std::string container_path = utils::getStringConfigVar(utils::HASHICORP_CONTAINER_PATH); + const std::string mount_point = utils::getStringConfigVar(utils::HASHICORP_CONTAINER_MOUNT_POINT); + const std::string key_name = utils::getStringConfigVar(utils::HASHICORP_CONTAINER_KEY_NAME); + + // Construct the URL + std::string url = container_url + "/v1/" + mount_point + "/data/" + container_path; + + // Make the HTTP GET request using cpr + cpr::Response response = cpr::Get( + cpr::Url{url}, + cpr::Header{{"X-Vault-Token", token}} + ); + + // Check for HTTP request success + if (response.status_code != 200) { + throw std::runtime_error("HTTP request failed with status code: " + + std::to_string(response.status_code) + "\nResponse: " + response.text); + } + + // Parse the JSON response using CrowCPP + crow::json::rvalue json = crow::json::load(response.text); + if (!json) { + throw std::runtime_error("Failed to parse JSON response: " + response.text); + } + + return json["data"]["data"][key_name].s(); + } + + std::vector get_seed() { + auto secret = get_secret(); + + std::vector serialized_secret = utils::ParseHex(secret); + + return serialized_secret; + } +} \ No newline at end of file diff --git a/lockbox/src/server.cpp b/lockbox/src/server.cpp index 7076bd78..d6228c42 100644 --- a/lockbox/src/server.cpp +++ b/lockbox/src/server.cpp @@ -4,7 +4,8 @@ #include "utils.h" #include "enclave.h" #include "google_key_manager.h" -#include "hashicorp_key_manager.h" +#include "hashicorp_api_key_manager.h" +#include "hashicorp_container_key_manager.h" #include "filesystem_key_manager.h" #include "db_manager.h" #include @@ -186,8 +187,7 @@ namespace lockbox { } std::string getKeyManager() { - return utils::getStringConfigVar( - utils::KEY_MANAGER.env_var, utils::KEY_MANAGER.toml_var_1, utils::KEY_MANAGER.toml_var_2); + return utils::getStringConfigVar(utils::KEY_MANAGER); } void start_server() { @@ -197,11 +197,25 @@ namespace lockbox { auto key_provider = getKeyManager(); if (key_provider == "filesystem") { + + std::cout << "Using filesystem key manager" << std::endl; + seed = filesystem_key_manager::get_seed(); } else if (key_provider == "google_kms") { + + std::cout << "Using Google KMS key manager" << std::endl; + seed = key_manager::get_seed(); - } else if (key_provider == "hashicorp") { - seed = hashicorp_key_manager::get_seed(); + } else if (key_provider == "hashicorp_api") { + + std::cout << "Using Hashicorp API key manager" << std::endl; + + seed = hashicorp_api_key_manager::get_seed(); + } else if (key_provider == "hashicorp_container") { + + std::cout << "Using Hashicorp container key manager" << std::endl; + + seed = hashicorp_container_key_manager::get_seed(); } else { throw std::runtime_error("Invalid key manager: " + key_provider); } diff --git a/lockbox/src/utils.cpp b/lockbox/src/utils.cpp index 96b9a740..0c7eb29e 100644 --- a/lockbox/src/utils.cpp +++ b/lockbox/src/utils.cpp @@ -128,4 +128,8 @@ namespace utils { return std::string(value); } } + + std::string getStringConfigVar(const config_var& config_variable) { + return getStringConfigVar(config_variable.env_var, config_variable.toml_var_1, config_variable.toml_var_2); + } }