Skip to content

Commit 20dc37e

Browse files
authored
Merge pull request #657 from Dstack-TEE/feat/kms-metrics-api
kms: add Prometheus metrics endpoint
2 parents 5e4ec90 + 1a7c59b commit 20dc37e

4 files changed

Lines changed: 98 additions & 4 deletions

File tree

kms/kms.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ cache_dir = "/usr/share/dstack/images"
3838
download_url = "http://localhost:8000/{OS_IMAGE_HASH}.tar.gz"
3939
download_timeout = "2m"
4040

41+
[core.metrics]
42+
# Expose unauthenticated Prometheus metrics on /metrics.
43+
# Disable this if the KMS RPC listener is not protected by network policy
44+
# or another access-control layer.
45+
enabled = true
46+
4147
[core.auth_api]
4248
type = "webhook"
4349

kms/src/config.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ pub(crate) struct KmsConfig {
4646
/// agent socket.
4747
#[serde(default = "default_true")]
4848
pub enforce_self_authorization: bool,
49+
pub metrics: MetricsConfig,
50+
}
51+
52+
#[derive(Debug, Clone, Deserialize)]
53+
pub(crate) struct MetricsConfig {
54+
/// Whether to expose the unauthenticated Prometheus `/metrics` endpoint.
55+
pub enabled: bool,
4956
}
5057

5158
fn default_true() -> bool {

kms/src/main.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use ra_rpc::rocket_helper::QuoteVerifier;
1010
use rocket::{
1111
fairing::AdHoc,
1212
figment::{providers::Serialized, Figment},
13-
response::content::RawHtml,
14-
Shutdown,
13+
response::content::{RawHtml, RawText},
14+
Shutdown, State,
1515
};
1616
use tracing::{info, warn};
1717

@@ -77,6 +77,34 @@ async fn run_onboard_service(kms_config: KmsConfig, figment: Figment) -> Result<
7777
Ok(())
7878
}
7979

80+
#[rocket::get("/metrics")]
81+
fn metrics(state: &State<KmsState>) -> RawText<String> {
82+
RawText(state.metrics().render_prometheus())
83+
}
84+
85+
// Count only RPCs whose primary job is to verify caller/app attestation.
86+
// Recording in a response fairing also catches failures that happen before
87+
// RpcHandler is constructed, such as malformed RA-TLS attestation.
88+
fn is_attestation_rpc_path(path: &str) -> bool {
89+
let Some(method) = path.strip_prefix("/prpc/") else {
90+
return false;
91+
};
92+
let method = method.trim_start_matches("KMS.");
93+
matches!(method, "GetAppKey" | "GetKmsKey" | "SignCert")
94+
}
95+
96+
fn record_attestation_metrics(req: &rocket::Request<'_>, res: &rocket::Response<'_>) {
97+
if !is_attestation_rpc_path(req.uri().path().as_str()) {
98+
return;
99+
}
100+
let Some(state) = req.rocket().state::<KmsState>() else {
101+
return;
102+
};
103+
state
104+
.metrics()
105+
.record_attestation_request(res.status().code >= 400);
106+
}
107+
80108
#[rocket::main]
81109
async fn main() -> Result<()> {
82110
{
@@ -109,6 +137,7 @@ async fn main() -> Result<()> {
109137
}
110138

111139
let pccs_url = config.pccs_url.clone();
140+
let metrics_enabled = config.metrics.enabled;
112141
let state = main_service::KmsState::new(config).context("Failed to initialize KMS state")?;
113142
let figment = figment
114143
.clone()
@@ -125,6 +154,16 @@ async fn main() -> Result<()> {
125154
)
126155
.manage(state);
127156

157+
if metrics_enabled {
158+
info!("Prometheus metrics endpoint enabled at /metrics");
159+
rocket = rocket
160+
.attach(AdHoc::on_response(
161+
"Record KMS attestation metrics",
162+
|req, res| Box::pin(async move { record_attestation_metrics(req, res) }),
163+
))
164+
.mount("/", rocket::routes![metrics]);
165+
}
166+
128167
let verifier = QuoteVerifier::new(pccs_url);
129168
rocket = rocket.manage(verifier);
130169

kms/src/main_service.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
use std::{
66
path::{Path, PathBuf},
7-
sync::Arc,
7+
sync::{
8+
atomic::{AtomicU64, Ordering},
9+
Arc,
10+
},
811
};
912

1013
use anyhow::{bail, Context, Result};
@@ -57,6 +60,38 @@ pub struct KmsStateInner {
5760
temp_ca_key: String,
5861
verifier: CvmVerifier,
5962
self_boot_info: OnceCell<BootInfo>,
63+
metrics: KmsMetrics,
64+
}
65+
66+
#[derive(Default)]
67+
pub(crate) struct KmsMetrics {
68+
attestation_requests_total: AtomicU64,
69+
attestation_failures_total: AtomicU64,
70+
}
71+
72+
impl KmsMetrics {
73+
pub(crate) fn record_attestation_request(&self, failed: bool) {
74+
self.attestation_requests_total
75+
.fetch_add(1, Ordering::Relaxed);
76+
if failed {
77+
self.attestation_failures_total
78+
.fetch_add(1, Ordering::Relaxed);
79+
}
80+
}
81+
82+
pub(crate) fn render_prometheus(&self) -> String {
83+
let attestation_requests_total = self.attestation_requests_total.load(Ordering::Relaxed);
84+
let attestation_failures_total = self.attestation_failures_total.load(Ordering::Relaxed);
85+
86+
format!(
87+
"# HELP dstack_kms_attestation_requests_total Total number of KMS attestation requests.\n\
88+
# TYPE dstack_kms_attestation_requests_total counter\n\
89+
dstack_kms_attestation_requests_total {attestation_requests_total}\n\
90+
# HELP dstack_kms_attestation_failures_total Total number of failed KMS attestation requests.\n\
91+
# TYPE dstack_kms_attestation_failures_total counter\n\
92+
dstack_kms_attestation_failures_total {attestation_failures_total}\n"
93+
)
94+
}
6095
}
6196

6297
impl KmsState {
@@ -77,7 +112,9 @@ impl KmsState {
77112
config.pccs_url.clone(),
78113
);
79114
if !config.enforce_self_authorization {
80-
warn!("self-authorization is disabled; trusted RPCs will not be gated by KMS self-attestation - do not use in production TEE deployments");
115+
warn!(
116+
"self-authorization is disabled; trusted RPCs will not be gated by KMS self-attestation - do not use in production TEE deployments"
117+
);
81118
}
82119
Ok(Self {
83120
inner: Arc::new(KmsStateInner {
@@ -88,9 +125,14 @@ impl KmsState {
88125
temp_ca_key,
89126
verifier,
90127
self_boot_info: OnceCell::new(),
128+
metrics: KmsMetrics::default(),
91129
}),
92130
})
93131
}
132+
133+
pub(crate) fn metrics(&self) -> &KmsMetrics {
134+
&self.inner.metrics
135+
}
94136
}
95137

96138
pub struct RpcHandler {

0 commit comments

Comments
 (0)