Skip to content

Commit cc6a91e

Browse files
authored
snp-knative: support deployment on cc-vms (#18)
1 parent cdb1c68 commit cc6a91e

File tree

279 files changed

+4770
-1754
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

279 files changed

+4770
-1754
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ build-native
77
build-wasm
88

99
ansible/inventory/vms.ini
10+
11+
datasets*

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.5.0
1+
0.6.0

accless/CMakeLists.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ add_library(${CMAKE_PROJECT_TARGET}
2626
# in WASM because there is no easy way to pass CMake vars to `wasm_cmake` in
2727
# faasmtools
2828
if (ACCLESS_UBENCH)
29+
message(STATUS "Building UBENCH")
2930
target_compile_definitions(${CMAKE_PROJECT_TARGET} PUBLIC ACCLESS_UBENCH)
3031
endif ()
3132

@@ -42,18 +43,36 @@ if (CMAKE_SYSTEM_NAME STREQUAL "WASI")
4243

4344
set(ACCLESS_HEADERS ${CMAKE_CURRENT_LIST_DIR}/include)
4445
else ()
46+
# This lines do not suppress the warning
47+
set(MY_CUSTOM_RPATH "/usr/local/attestationcurl/lib;/usr/local/attestationssl/lib64")
48+
set(CMAKE_BUILD_RPATH "${MY_CUSTOM_RPATH}")
49+
set(CMAKE_INSTALL_RPATH "${MY_CUSTOM_RPATH}")
50+
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
51+
target_link_directories(${CMAKE_PROJECT_TARGET} PRIVATE BEFORE
52+
/usr/local/attestationcurl/lib
53+
/usr/local/attestationssl/lib64
54+
)
55+
56+
add_subdirectory(./libs/attestation)
4557
add_subdirectory(./libs/s3)
4658

4759
set(ACCLESS_LIBRARIES
4860
accless::s3
61+
accless::attestation
62+
# We use the installed curl as part of azure-cvm-attestation
63+
/usr/local/attestationcurl/lib/libcurl.a
4964
# Order matters: librabe-cpp must preceede librabe
5065
"/usr/local/lib/rabe/librabe-cpp.a"
5166
"/usr/local/lib/rabe/librabe.a"
67+
# Same for JWT
68+
"/usr/local/lib/tless-jwt/libtless-jwt-cpp.a"
69+
"/usr/local/lib/tless-jwt/libtless_jwt.a"
5270
)
5371
set(ACCLESS_HEADERS
5472
${CMAKE_CURRENT_LIST_DIR}/include
5573
${CMAKE_CURRENT_LIST_DIR}/libs
5674
"/usr/include/rabe"
75+
"/usr/include/tless-jwt"
5776
)
5877
endif()
5978

accless/include/accless.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
// mr_enclave in the SGX report has type sgx_measurement_t which is a SHA256
1111
// digest (see sgx_report.h)
1212
#define MRENCLAVE_SIZE 32
13-
#define ATT_PROVIDER_AUD "accless-attestation-service"
14-
#define ATT_PROVIDER_SUB "attested-client"
1513

1614
// We define with C-linkage all the external symbols that a TLess ECF needs
1715
// from the runtime environment. These are implemented by the runtime outside
@@ -24,7 +22,10 @@ void __accless_get_mrenclave(uint8_t *buf, int32_t bufSize);
2422
}
2523
#endif
2624

27-
/* Main TLess C++ API
25+
#define ATT_PROVIDER_AUD "accless-attestation-service"
26+
#define ATT_PROVIDER_SUB "attested-client"
27+
28+
/* Main Accless C++ API
2829
*
2930
* MISSING:
3031
* - Decrypt/Encrypt function input/output
@@ -50,4 +51,4 @@ int32_t chain(const std::string &workflow, const std::string &parentFuncName,
5051
// Wait for a function by its id, and get its output and return code
5152
std::pair<int, std::string> wait(int32_t functionId, bool ignoreOutput = false);
5253
#endif
53-
} // namespace tless
54+
} // namespace accless

accless/include/dag.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ std::vector<std::string>
2929
getFuncChainFromCertChain(const std::vector<uint8_t> &certChain);
3030
std::vector<std::string>
3131
getFuncChainFromCertChain(const std::string &certChain);
32-
} // namespace tless::dag
32+
} // namespace accless::dag

accless/include/utils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace accless::utils {
88
std::string byteArrayToHexString(const uint8_t *data, int dataSize);
99

10-
std::vector<uint8_t> base64Decode(const std::string& input);
10+
std::vector<uint8_t> base64Decode(const std::string &input);
1111

1212
#ifdef __faasm
1313
std::vector<uint8_t> doGetKeyBytes(const std::string &bucketName,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
set(CMAKE_PROJECT_TARGET attestation)
2+
3+
include(FetchContent)
4+
5+
FetchContent_Declare(
6+
AzGuestAttestation
7+
GIT_REPOSITORY https://github.com/faasm/azure-cvm-guest-attestation.git
8+
GIT_TAG main
9+
)
10+
FetchContent_MakeAvailable(AzGuestAttestation)
11+
12+
set(AZ_GUEST_ATTESTATION_INCLUDE_DIRS
13+
${azguestattestation_SOURCE_DIR}/AttestationClient
14+
${azguestattestation_SOURCE_DIR}/AttestationClient/include
15+
${azguestattestation_SOURCE_DIR}/LinuxTpm/include
16+
${azguestattestation_SOURCE_DIR}/external/jsoncpp-0.10.7/include
17+
)
18+
19+
add_library(${CMAKE_PROJECT_TARGET} attestation.cpp)
20+
target_link_libraries(${CMAKE_PROJECT_TARGET} PUBLIC azguestattestation curl)
21+
target_include_directories(${CMAKE_PROJECT_TARGET} PUBLIC ${AZ_GUEST_ATTESTATION_INCLUDE_DIRS})
22+
23+
add_library(accless::attestation ALIAS ${CMAKE_PROJECT_TARGET})
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
#include "attestation.h"
2+
3+
// Includes from Azure Guest Attestation library
4+
#include "AttestationLogger.h"
5+
#include "HclReportParser.h"
6+
#include "TpmCertOperations.h"
7+
8+
#include <array>
9+
#include <curl/curl.h>
10+
#include <fcntl.h>
11+
#include <filesystem>
12+
#include <optional>
13+
#include <sys/ioctl.h>
14+
#include <sys/stat.h>
15+
#include <vector>
16+
17+
using namespace attest;
18+
19+
#define SNP_GUEST_REQ_IOC_TYPE 'S'
20+
#define SNP_GET_REPORT _IOWR(SNP_GUEST_REQ_IOC_TYPE, 0x0, struct snp_guest_request_ioctl)
21+
22+
namespace accless::attestation {
23+
void Logger::Log(const char *log_tag, AttestationLogger::LogLevel level,
24+
const char *function, const int line, const char *fmt, ...) {
25+
va_list args;
26+
va_start(args, fmt);
27+
size_t len = std::vsnprintf(NULL, 0, fmt, args);
28+
va_end(args);
29+
30+
std::vector<char> str(len + 1);
31+
32+
va_start(args, fmt);
33+
std::vsnprintf(&str[0], len + 1, fmt, args);
34+
va_end(args);
35+
36+
// Uncomment for debug logs
37+
// std::cout << std::string(str.begin(), str.end()) << std::endl;
38+
}
39+
40+
std::vector<uint8_t> getSnpReportFromTPM() {
41+
// First, get HCL report
42+
Tpm tpm;
43+
Buffer hclReport = tpm.GetHCLReport();
44+
45+
Buffer snpReport;
46+
Buffer runtimeData;
47+
HclReportParser reportParser;
48+
49+
auto result = reportParser.ExtractSnpReportAndRuntimeDataFromHclReport(
50+
hclReport, snpReport, runtimeData);
51+
if (result.code_ != AttestationResult::ErrorCode::SUCCESS) {
52+
std::cerr << "accless: error parsing snp report from HCL report"
53+
<< std::endl;
54+
throw std::runtime_error("error parsing HCL report");
55+
}
56+
57+
return snpReport;
58+
}
59+
60+
void tpmRenewAkCert() {
61+
TpmCertOperations tpmCertOps;
62+
bool renewalRequired = false;
63+
auto result = tpmCertOps.IsAkCertRenewalRequired(renewalRequired);
64+
if (result.code_ != AttestationResult::ErrorCode::SUCCESS) {
65+
std::cerr << "accless: error checking AkCert renewal state"
66+
<< std::endl;
67+
68+
if (result.tpm_error_code_ != 0) {
69+
std::cerr << "accless: internal TPM error occured: "
70+
<< result.description_ << std::endl;
71+
throw std::runtime_error("internal TPM error");
72+
} else if (result.code_ == attest::AttestationResult::ErrorCode::
73+
ERROR_AK_CERT_PROVISIONING_FAILED) {
74+
std::cerr << "accless: attestation key cert provisioning delayed"
75+
<< std::endl;
76+
throw std::runtime_error("internal TPM error");
77+
}
78+
}
79+
80+
if (renewalRequired) {
81+
auto replaceResult = tpmCertOps.RenewAndReplaceAkCert();
82+
if (replaceResult.code_ != AttestationResult::ErrorCode::SUCCESS) {
83+
std::cerr << "accless: failed to renew AkCert: "
84+
<< result.description_ << std::endl;
85+
throw std::runtime_error("accless: internal TPM error");
86+
}
87+
}
88+
}
89+
90+
/******************************************************************************/
91+
/* SNP Related Methods */
92+
/******************************************************************************/
93+
94+
// This method fetches the SNP attestation report from /dev/sev-guest:
95+
// - message_version is not used in this simple example, but is kept for
96+
// interface compatibility.
97+
// - userData: Optional 64-byte data to be included in the report.
98+
// - vmpl: Optional VMPL level.
99+
std::vector<uint8_t>
100+
getSnpReportFromDev(std::optional<std::array<uint8_t, 64>> userData,
101+
std::optional<uint32_t> vmpl) {
102+
int fd = open("/dev/sev-guest", O_RDWR);
103+
if (fd < 0) {
104+
std::cerr << "accless(att): failed to open /dev/sev-guest" << std::endl;
105+
throw std::runtime_error("Failed to open /dev/sev-guest");
106+
}
107+
108+
// Prepare the request payload.
109+
snp_report_req reqPayload;
110+
std::memset(&reqPayload, 0, sizeof(reqPayload));
111+
reqPayload.vmpl = vmpl.value_or(0);
112+
if (userData.has_value()) {
113+
std::memcpy(reqPayload.user_data, userData->data(), userData->size());
114+
}
115+
116+
// Prepare the response buffer.
117+
snp_report_resp respPayload;
118+
std::memset(&respPayload, 0, sizeof(respPayload));
119+
120+
// Prepare the ioctl wrapper.
121+
snp_guest_request_ioctl guestReq;
122+
std::memset(&guestReq, 0, sizeof(guestReq));
123+
guestReq.msg_version = 1; // Must be non-zero.
124+
guestReq.req_data = reinterpret_cast<uint64_t>(&reqPayload);
125+
guestReq.resp_data = reinterpret_cast<uint64_t>(&respPayload);
126+
127+
// Issue the ioctl.
128+
if (ioctl(fd, SNP_GET_REPORT, &guestReq) < 0) {
129+
int err = errno;
130+
close(fd);
131+
std::cerr << "accless(att): ioctl SNP_GET_REPORT failed: " << strerror(err) << std::endl;
132+
throw std::runtime_error("ioctl SNP_GET_REPORT failed");
133+
}
134+
close(fd);
135+
136+
// Check for firmware or VMM errors.
137+
if (guestReq.fw_error != 0 || guestReq.vmm_error != 0) {
138+
std::cerr << "accless(att): firmware error: " << guestReq.fw_error
139+
<< " vmm error: " << guestReq.vmm_error << std::endl;
140+
throw std::runtime_error("Firmware reported error");
141+
}
142+
143+
// Convert the response to a vector.
144+
std::vector<uint8_t> report(respPayload.data, respPayload.data + SNP_REPORT_RESP_SIZE);
145+
return report;
146+
}
147+
148+
std::vector<uint8_t>
149+
getSnpReport(std::optional<std::array<uint8_t, 64>> reportData) {
150+
if (std::filesystem::exists("/dev/sev-guest")) {
151+
return getSnpReportFromDev(reportData, std::nullopt);
152+
}
153+
154+
if (std::filesystem::exists("/dev/tpmrm0")) {
155+
return getSnpReportFromTPM();
156+
}
157+
158+
std::cerr << "accless(att): no known SNP device found for attestation"
159+
<< std::endl;
160+
throw std::runtime_error("No known SNP device found!");
161+
}
162+
163+
/******************************************************************************/
164+
/* Attestation Service Methods */
165+
/******************************************************************************/
166+
167+
// Get the URL of our own attestation service (**not** MAA)
168+
std::string getAttestationServiceUrl() {
169+
const char *val = std::getenv("ACCLESS_AS_URL");
170+
if (val == nullptr) {
171+
// This url uses https://ip:port
172+
std::cerr << "accless(att): must set ACCLESS_AS_URL" << std::endl;
173+
throw std::runtime_error("must set ACCLESS_AS_URL");
174+
}
175+
return std::string(val);
176+
}
177+
178+
std::string getAttestationServiceCertPath() {
179+
const char *val = std::getenv("ACCLESS_AS_CERT");
180+
if (val == nullptr) {
181+
std::cerr << "accless(att): must set ACCLESS_AS_CERT" << std::endl;
182+
throw std::runtime_error("must set ACCLESS_AS_CERT");
183+
}
184+
return std::string(val);
185+
}
186+
187+
size_t curlWriteCallback(char *ptr, size_t size, size_t nmemb, void *userdata) {
188+
size_t totalSize = size * nmemb;
189+
auto *response = static_cast<std::string *>(userdata);
190+
response->append(ptr, totalSize);
191+
return totalSize;
192+
}
193+
194+
std::string asGetJwtFromReport(const std::vector<uint8_t> &snpReport) {
195+
std::string jwt;
196+
197+
CURL *curl = curl_easy_init();
198+
if (!curl) {
199+
std::cerr << "accless: failed to initialize CURL" << std::endl;
200+
throw std::runtime_error("curl error");
201+
}
202+
203+
std::string asUrl = getAttestationServiceUrl() + "/verify-snp-report";
204+
std::string certPath = getAttestationServiceCertPath();
205+
curl_easy_setopt(curl, CURLOPT_URL, asUrl.c_str());
206+
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
207+
curl_easy_setopt(curl, CURLOPT_CAINFO, certPath.c_str());
208+
curl_easy_setopt(curl, CURLOPT_POST, 1L);
209+
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, snpReport.data());
210+
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, snpReport.size());
211+
212+
struct curl_slist *headers = nullptr;
213+
headers =
214+
curl_slist_append(headers, "Content-Type: application/octet-stream");
215+
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
216+
217+
// Set write function and data
218+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
219+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &jwt);
220+
221+
// Perform the request
222+
CURLcode res = curl_easy_perform(curl);
223+
if (res != CURLE_OK) {
224+
std::cerr << "accless: CURL error: " << curl_easy_strerror(res)
225+
<< std::endl;
226+
curl_easy_cleanup(curl);
227+
curl_slist_free_all(headers);
228+
throw std::runtime_error("curl error");
229+
}
230+
231+
curl_easy_cleanup(curl);
232+
curl_slist_free_all(headers);
233+
234+
return jwt;
235+
}
236+
} // namespace accless::attestation

0 commit comments

Comments
 (0)