Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a69e942
[experiments] B: Correct Policy For Trustee
csegarragonz Dec 7, 2025
3372fe9
[experiments] E: More Refined Trustee Policy
csegarragonz Dec 7, 2025
000cf9e
[accli] E: Set Reference Values For Trustee
csegarragonz Dec 7, 2025
9887023
[applications] B: Rate-Limit-Safe Warm-Up
csegarragonz Dec 7, 2025
0b97fd3
[accli] B: More Trustee Policy Fixes
csegarragonz Dec 7, 2025
5facc67
[accless] E: Include VCEK In Collateral For Az
csegarragonz Dec 7, 2025
8289ba5
[accli] B: More Trustee Policy Tweaks
csegarragonz Dec 7, 2025
425e3a7
[as] E: Include Collateral In Cache
csegarragonz Dec 7, 2025
099fdd9
[accli] B: Robust RVPS
csegarragonz Dec 7, 2025
bcdc152
[config] B: Tweaks For Trustee
csegarragonz Dec 7, 2025
c31a2ea
[as] B: Run Code Formatting
csegarragonz Dec 7, 2025
2f0a9fd
[accless] B: Run Code Formatting
csegarragonz Dec 7, 2025
b5de26a
[applications] B: Clean-Up Escrow-Xput
csegarragonz Dec 7, 2025
79b41cd
[as] B: Remove Obsolete Code
csegarragonz Dec 7, 2025
d904ce3
[accli] B: Add FIXME Note
csegarragonz Dec 7, 2025
9344876
[accless] B: Fix Comment
csegarragonz Dec 7, 2025
36cdb86
[as] B: Fix Clippy Lint
csegarragonz Dec 7, 2025
376f50d
[ci] B: Fix Args In Snp Test
csegarragonz Dec 7, 2025
0233f9d
[accli] B: Fix Unwrap
csegarragonz Dec 7, 2025
6c999cc
[accli] B: Fix Error Messages
csegarragonz Dec 7, 2025
1d23d22
[ci] B: Fix Snp GHA
csegarragonz Dec 7, 2025
f73a6f9
[accli] B: Fix Formatting
csegarragonz Dec 7, 2025
0ded831
[experiments] E: Remove 'ubench/escrow-xput'
csegarragonz Dec 7, 2025
301c20b
[accli] E: Refine Escrow Plots
csegarragonz Dec 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/azure.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: "SNP End-to-End Tests"

on:
push:
branches: [main]
workflow_dispatch:

defaults:
run:
shell: bash

# Cancel previous running actions for the same PR
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
snp-vtpm-test:
runs-on: [self-hosted]
steps:
- name: "Check out the code"
uses: actions/checkout@v4

2 changes: 1 addition & 1 deletion .github/workflows/snp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:

# Build SNP applications and embed the attestation service's certificate.
- name: "Build SNP applications"
run: ./scripts/accli_wrapper.sh applications build --clean --as-cert-path ./certs/cert.pem --in-cvm
run: ./scripts/accli_wrapper.sh applications build --clean --as-cert-dir ./certs/cert.pem --in-cvm

- name: "Run supported SNP applications"
run: |
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions accless/libs/attestation/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ add_library(${CMAKE_PROJECT_TARGET}
mock_snp.cpp
utils.cpp
snp.cpp
vcek_cache.cpp
)
target_link_libraries(${CMAKE_PROJECT_TARGET} PUBLIC
azguestattestation
Expand Down
6 changes: 6 additions & 0 deletions accless/libs/attestation/http_client.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "attestation.h"
#include "vcek_cache.h"

#include <cstring>
#include <curl/curl.h>
Expand All @@ -19,6 +20,11 @@ static size_t curlWriteCallback(char *ptr, size_t size, size_t nmemb,
}

HttpClient::HttpClient(const std::string &certPath) : certPath_(certPath) {
// Initialize process-wide VCEK cache once. This is only populated if
// we are deployed inside an Azure cVM, otherwise will return empty
// strings.
accless::attestation::snp::getVcekPemBundle();

curl_ = curl_easy_init();
if (!curl_) {
throw std::runtime_error("accless(att): failed to init curl");
Expand Down
10 changes: 10 additions & 0 deletions accless/libs/attestation/utils.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "attestation.h"
#include "vcek_cache.h"

#include <nlohmann/json.hpp>

Expand Down Expand Up @@ -49,6 +50,15 @@ std::string buildRequestBody(const std::string &quoteB64,
body["quote"] = quoteB64;
body["runtimeData"]["data"] = runtimeB64;
body["runtimeData"]["dataType"] = "Binary";

const auto &vcekPem = accless::attestation::snp::getVcekCertPem();
const auto &chainPem = accless::attestation::snp::getVcekChainPem();

if (!vcekPem.empty()) {
body["collateral"]["vcekCertPem"] = vcekPem;
body["collateral"]["certificateChainPem"] = chainPem;
}

return body.dump();
}
} // namespace accless::attestation::utils
123 changes: 123 additions & 0 deletions accless/libs/attestation/vcek_cache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#include "vcek_cache.h"

#include <curl/curl.h>
#include <nlohmann/json.hpp>

#include <stdexcept>
#include <string>

namespace accless::attestation::snp {
namespace {

constexpr const char *THIM_URL =
"http://169.254.169.254/metadata/THIM/amd/certification";
constexpr const char *THIM_METADATA_HEADER = "Metadata:true";

struct VcekCache {
std::once_flag once;
std::string vcekCert;
std::string certChain;
std::string bundle;
std::string error;
};

VcekCache g_cache;

size_t curlWriteCallback(char *ptr, size_t size, size_t nmemb, void *userdata) {
auto *out = static_cast<std::string *>(userdata);
if (!out)
return 0;
const size_t total = size * nmemb;
out->append(ptr, total);
return total;
}

// Perform one-time VCEK fetch, but *never throw*. If unavailable, returns empty
// PEM strings.
void initVcekCache() {
CURL *curl = curl_easy_init();
if (!curl) {
g_cache.error = "failed to init curl for VCEK fetch";
return;
}

std::string response;
char errbuf[CURL_ERROR_SIZE] = {0};

curl_easy_setopt(curl, CURLOPT_URL, THIM_URL);
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 500L); // do not block
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 300L);

// Add Metadata:true header
struct curl_slist *headers = nullptr;
headers = curl_slist_append(headers, THIM_METADATA_HEADER);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

CURLcode res = curl_easy_perform(curl);
long status = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);

curl_easy_setopt(curl, CURLOPT_HTTPHEADER, nullptr);
curl_slist_free_all(headers);
curl_easy_cleanup(curl);

// IMDS unreachable → not a CVM → silently return empty values
if (res != CURLE_OK) {
g_cache.error = std::string("VCEK fetch failed: curl error: ") +
(errbuf[0] ? errbuf : curl_easy_strerror(res));
return;
}

if (status != 200) {
g_cache.error =
"VCEK fetch failed: HTTP status " + std::to_string(status);
return;
}

// Parse JSON.
try {
auto json = nlohmann::json::parse(response);

g_cache.vcekCert = json.value("vcekCert", "");
g_cache.certChain = json.value("certificateChain", "");

// Normalize newlines
if (!g_cache.vcekCert.empty() && g_cache.vcekCert.back() != '\n')
g_cache.vcekCert.push_back('\n');

if (!g_cache.certChain.empty() && g_cache.certChain.back() != '\n')
g_cache.certChain.push_back('\n');

g_cache.bundle = g_cache.vcekCert + g_cache.certChain;
} catch (const std::exception &e) {
g_cache.error = std::string("VCEK fetch JSON parse error: ") + e.what();
// Leave empty certs
return;
}
}

void ensureInitialized() { std::call_once(g_cache.once, initVcekCache); }

} // namespace

// --- Public API ---

const std::string &getVcekPemBundle() {
ensureInitialized();
return g_cache.bundle;
}

const std::string &getVcekCertPem() {
ensureInitialized();
return g_cache.vcekCert;
}

const std::string &getVcekChainPem() {
ensureInitialized();
return g_cache.certChain;
}
} // namespace accless::attestation::snp
14 changes: 14 additions & 0 deletions accless/libs/attestation/vcek_cache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <mutex>
#include <string>

namespace accless::attestation::snp {

// Returns concatenated PEM (VCEK + chain) or an empty string on failure.
const std::string &getVcekPemBundle();

// If you prefer separate pieces:
const std::string &getVcekCertPem();
const std::string &getVcekChainPem();
} // namespace accless::attestation::snp
1 change: 1 addition & 0 deletions accli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ path = "src/lib.rs"

[dependencies]
anyhow.workspace = true
az-snp-vtpm.workspace = true
base64.workspace = true
bytes.workspace = true
chrono.workspace = true
Expand Down
Loading
Loading