From 98091b5ce5a8d43f30622a11dc4804a2adfcd81b Mon Sep 17 00:00:00 2001 From: Amaury Chamayou Date: Tue, 12 Dec 2023 11:36:27 +0000 Subject: [PATCH] Resolve env vars in SNP endorsements (#5862) --- .snpcc_canary | 2 +- CHANGELOG.md | 6 +++ include/ccf/ds/nonstd.h | 18 ++++++++ .../pal/attestation_sev_snp_endorsements.h | 42 +++++++++++++++---- scripts/azure_deployment/arm_aci.py | 1 + src/ds/test/nonstd.cpp | 20 +++++++++ src/host/main.cpp | 30 +++++++++++++ src/node/quote_endorsements_client.h | 18 ++++---- tests/reconfiguration.py | 2 + 9 files changed, 119 insertions(+), 20 deletions(-) diff --git a/.snpcc_canary b/.snpcc_canary index 03c2c32faf8b..0320ebb62f43 100644 --- a/.snpcc_canary +++ b/.snpcc_canary @@ -1,4 +1,4 @@ ___ ___ ___ (. =) Y (0 0) (x X) Y O \ o | / -/-xXx--//-----x=x--/-xXx--/---x---->>>-- \ No newline at end of file +/-xXx--//-----x=x--/-xXx--/---x---->>>--/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 30e35cec22cf..c98eaba4b5ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [5.0.0-dev10] + +[5.0.0-dev10]: https://github.com/microsoft/CCF/releases/tag/ccf-5.0.0-dev10 + +- The `url` field in `snp_endorsements_servers` can now contain environment variables that will be resolved at startup, such as "$Fabric_NodeIPOrFQDN:2377" (#5862). + ## [5.0.0-dev9] [5.0.0-dev9]: https://github.com/microsoft/CCF/releases/tag/ccf-5.0.0-dev9 diff --git a/include/ccf/ds/nonstd.h b/include/ccf/ds/nonstd.h index 21ce299e0bb2..12eeb7404259 100644 --- a/include/ccf/ds/nonstd.h +++ b/include/ccf/ds/nonstd.h @@ -238,4 +238,22 @@ namespace nonstd tuple_for_each(t, f); } } + + static inline std::string expand_envvar(const std::string& str) + { + if (str.empty() || str[0] != '$') + { + return str; + } + + char* e = std::getenv(str.c_str() + 1); + if (e == nullptr) + { + return str; + } + else + { + return std::string(e); + } + } } \ No newline at end of file diff --git a/include/ccf/pal/attestation_sev_snp_endorsements.h b/include/ccf/pal/attestation_sev_snp_endorsements.h index 12907370e4e9..23db1c2e6c7b 100644 --- a/include/ccf/pal/attestation_sev_snp_endorsements.h +++ b/include/ccf/pal/attestation_sev_snp_endorsements.h @@ -46,6 +46,7 @@ namespace ccf::pal::snp bool response_is_der = false; bool response_is_thim_json = false; std::map headers = {}; + bool tls = true; bool operator==(const EndpointInfo&) const = default; }; @@ -152,13 +153,36 @@ namespace ccf::pal::snp std::map params; params["tcbVersion"] = reported_tcb; params["platformId"] = chip_id_hex; - return { - {endpoint.host, - endpoint.port, - "/metadata/THIM/amd/certification", - params, - false, // Not DER - true, // But THIM JSON - {{"Metadata", "true"}}}}; + return {{ + endpoint.host, + endpoint.port, + "/metadata/THIM/amd/certification", + params, + false, // Not DER + true, // But THIM JSON + {{"Metadata", "true"}}, + false // No TLS + }}; + } +} + +FMT_BEGIN_NAMESPACE +template <> +struct formatter +{ + template + constexpr auto parse(ParseContext& ctx) + { + return ctx.begin(); + } + + template + auto format( + const ccf::pal::snp::EndorsementEndpointsConfiguration::EndpointInfo& e, + FormatContext& ctx) const + { + return format_to( + ctx.out(), "http{}://{}:{}", e.tls ? "s" : "", e.host, e.port); } -} \ No newline at end of file +}; +FMT_END_NAMESPACE \ No newline at end of file diff --git a/scripts/azure_deployment/arm_aci.py b/scripts/azure_deployment/arm_aci.py index cac6c3135091..0e1ca17a3719 100644 --- a/scripts/azure_deployment/arm_aci.py +++ b/scripts/azure_deployment/arm_aci.py @@ -44,6 +44,7 @@ def append_envvar_to_well_known_file(envvar): append_envvar_to_well_known_file("UVM_REFERENCE_INFO"), append_envvar_to_well_known_file("UVM_HOST_AMD_CERTIFICATE"), append_envvar_to_well_known_file("UVM_SECURITY_CONTEXT_DIR"), + append_envvar_to_well_known_file("Fabric_NodeIPOrFQDN"), ] diff --git a/src/ds/test/nonstd.cpp b/src/ds/test/nonstd.cpp index ea99635626e4..b97cf9d5bfb2 100644 --- a/src/ds/test/nonstd.cpp +++ b/src/ds/test/nonstd.cpp @@ -5,6 +5,7 @@ #include #include +#include #include TEST_CASE("split" * doctest::test_suite("nonstd")) @@ -258,4 +259,23 @@ TEST_CASE("rsplit" * doctest::test_suite("nonstd")) } } } +} + +TEST_CASE("envvars" * doctest::test_suite("nonstd")) +{ + { + INFO("Expand environment variable"); + + std::string test_value("test_value"); + ::setenv("TEST_ENV_VAR", test_value.c_str(), 1); + + REQUIRE("" == nonstd::expand_envvar("")); + REQUIRE("not an env var" == nonstd::expand_envvar("not an env var")); + REQUIRE("$ENV_VAR_NOT_SET" == nonstd::expand_envvar("$ENV_VAR_NOT_SET")); + REQUIRE(test_value == nonstd::expand_envvar("$TEST_ENV_VAR")); + + // ${} syntax is not supported + REQUIRE( + "${ENV_VAR_NOT_SET}" == nonstd::expand_envvar("${ENV_VAR_NOT_SET}")); + } } \ No newline at end of file diff --git a/src/host/main.cpp b/src/host/main.cpp index 0b77588e557e..c941c8747476 100644 --- a/src/host/main.cpp +++ b/src/host/main.cpp @@ -542,6 +542,36 @@ int main(int argc, char** argv) fs::path(dir) / fs::path(report_endorsements_filename)); } + for (auto endorsement_servers_it = + startup_config.attestation.snp_endorsements_servers.begin(); + endorsement_servers_it != + startup_config.attestation.snp_endorsements_servers.end(); + ++endorsement_servers_it) + { + LOG_DEBUG_FMT( + "Resolving snp_endorsements_server url: {}", + endorsement_servers_it->url.value()); + if (endorsement_servers_it->url.has_value()) + { + auto& url = endorsement_servers_it->url.value(); + auto pos = url.find(':'); + if (pos == std::string::npos) + { + endorsement_servers_it->url = nonstd::expand_envvar(url); + } + else + { + endorsement_servers_it->url = fmt::format( + "{}:{}", + nonstd::expand_envvar(url.substr(0, pos)), + nonstd::expand_envvar(url.substr(pos + 1))); + } + LOG_DEBUG_FMT( + "Resolved snp_endorsements_server url: {}", + endorsement_servers_it->url); + } + } + if (config.node_data_json_file.has_value()) { startup_config.node_data = diff --git a/src/node/quote_endorsements_client.h b/src/node/quote_endorsements_client.h index 6e7681abb301..3c6899fa42b6 100644 --- a/src/node/quote_endorsements_client.h +++ b/src/node/quote_endorsements_client.h @@ -102,9 +102,8 @@ namespace ccf r.set_header(http::headers::HOST, endpoint.host); LOG_INFO_FMT( - "Fetching endorsements for attestation report at http{}://{}{}{}", - endpoint.port == "80" ? "" : "s", - endpoint.host, + "Fetching endorsements for attestation report at {}{}{}", + endpoint, r.get_path(), r.get_formatted_query()); client->send_request(std::move(r)); @@ -122,8 +121,7 @@ namespace ccf if (msg->data.request_id >= msg->data.self->last_received_request_id) { LOG_FAIL_FMT( - "Timed out reaching endorsement server {}", - msg->data.endpoint.host); + "Timed out reaching endorsement server {}", msg->data.endpoint); auto& servers = msg->data.self->config.servers; msg->data.self->server_retries_count++; @@ -217,8 +215,8 @@ namespace ccf { auto endpoint = server.front(); - auto c = endpoint.port == "80" ? create_unencrypted_client() : - create_unauthenticated_client(); + auto c = endpoint.tls ? create_unauthenticated_client() : + create_unencrypted_client(); c->connect( endpoint.host, endpoint.port, @@ -266,7 +264,7 @@ namespace ccf LOG_INFO_FMT( "{} endorsements endpoint had too many requests. Retrying " "in {}s", - endpoint.host, + endpoint, retry_after_s); threading::ThreadMessaging::instance().add_task_after( @@ -274,10 +272,10 @@ namespace ccf } return; }, - [host = endpoint.host](const std::string& error_msg) { + [endpoint](const std::string& error_msg) { LOG_FAIL_FMT( "TLS error when connecting to quote endorsements endpoint {}: {}", - host, + endpoint, error_msg); }); send_request(c, endpoint); diff --git a/tests/reconfiguration.py b/tests/reconfiguration.py index b04d43d170e7..ab648637f4af 100644 --- a/tests/reconfiguration.py +++ b/tests/reconfiguration.py @@ -259,6 +259,8 @@ def test_add_node_endorsements_endpoints(network, args): (["AMD:kdsintf.amd.com"], True), (["AMD:invalid.amd.com"], False), (["Azure:invalid.azure.com", "AMD:kdsintf.amd.com"], True), # Fallback server + # Won't work yet, see #5852 + # (["THIM:$Fabric_NodeIPOrFQDN:2377"], True), ] for servers, expected_result in test_vectors: