Skip to content

Commit 7bb8f7b

Browse files
committed
Add option to hide tcbinfo from 8090 port
1 parent dbe64f9 commit 7bb8f7b

File tree

12 files changed

+144
-90
lines changed

12 files changed

+144
-90
lines changed

dstack-types/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub struct AppCompose {
1717
pub public_logs: bool,
1818
#[serde(default)]
1919
pub public_sysinfo: bool,
20+
#[serde(default = "default_true")]
21+
pub public_tcbinfo: bool,
2022
#[serde(default)]
2123
pub kms_enabled: bool,
2224
#[serde(deserialize_with = "deserialize_gateway_enabled", flatten)]

dstack-util/src/system_setup.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -773,9 +773,6 @@ impl Stage1<'_> {
773773
let config = serde_json::json!({
774774
"default": {
775775
"core": {
776-
"app_name": self.shared.app_compose.name,
777-
"public_logs": self.shared.app_compose.public_logs,
778-
"public_sysinfo": self.shared.app_compose.public_sysinfo,
779776
"pccs_url": self.shared.sys_config.pccs_url,
780777
"data_disks": data_disks,
781778
}

guest-agent/dstack.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@ keep_alive = 10
77
log_level = "debug"
88

99
[default.core]
10-
app_name = ""
1110
keys_file = "/dstack/.host-shared/.appkeys.json"
1211
compose_file = "/dstack/.host-shared/app-compose.json"
1312
sys_config_file = "/dstack/.host-shared/.sys-config.json"
14-
public_logs = true
15-
public_sysinfo = true
1613
data_disks = ["/"]
1714

1815
[default.core.simulator]

guest-agent/rpc/proto/agent_rpc.proto

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ service Tappd {
2323
// identifier in the report_data to avoid Quote Repurposing Attacks.
2424
rpc RawQuote(RawQuoteArgs) returns (TdxQuoteResponse) {}
2525

26-
// Get worker info
27-
rpc Info(google.protobuf.Empty) returns (WorkerInfo) {}
26+
// Get app info
27+
rpc Info(google.protobuf.Empty) returns (AppInfo) {}
2828
}
2929

3030
// The service for the dstack guest agent
@@ -42,8 +42,8 @@ service DstackGuest {
4242
// Emit an event. This extends the event to RTMR3 on TDX platform.
4343
rpc EmitEvent(EmitEventArgs) returns (google.protobuf.Empty) {}
4444

45-
// Get worker info
46-
rpc Info(google.protobuf.Empty) returns (WorkerInfo) {}
45+
// Get app info
46+
rpc Info(google.protobuf.Empty) returns (AppInfo) {}
4747
}
4848

4949
// The request to derive a key
@@ -172,7 +172,7 @@ message EmitEventArgs {
172172
}
173173

174174
// The request to derive a key
175-
message WorkerInfo {
175+
message AppInfo {
176176
// App ID
177177
bytes app_id = 1;
178178
// App Instance ID
@@ -183,10 +183,6 @@ message WorkerInfo {
183183
string tcb_info = 4;
184184
// App name
185185
string app_name = 5;
186-
// Whether the app logs are public
187-
bool public_logs = 6;
188-
// Whether the app sysinfo is public
189-
bool public_sysinfo = 7;
190186
// Device ID
191187
bytes device_id = 8;
192188
// MR Aggregated
@@ -203,7 +199,7 @@ message WorkerInfo {
203199
string vm_config = 14;
204200
}
205201

206-
// The response to a WorkerInfo request
202+
// The response to a Version request
207203
message WorkerVersion {
208204
// Dstack version
209205
string version = 1;
@@ -212,8 +208,8 @@ message WorkerVersion {
212208
}
213209

214210
service Worker {
215-
// Get worker info
216-
rpc Info(google.protobuf.Empty) returns (WorkerInfo) {}
211+
// Get app info
212+
rpc Info(google.protobuf.Empty) returns (AppInfo) {}
217213
// Get the guest agent version
218214
rpc Version(google.protobuf.Empty) returns (WorkerVersion) {}
219215
}

guest-agent/src/config.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
use std::{collections::HashSet, path::PathBuf};
1+
use std::{collections::HashSet, ops::Deref, path::PathBuf};
22

3+
use dstack_types::AppCompose;
34
use figment::Figment;
5+
use fs_err as fs;
46
use load_config::load_config;
5-
use serde::Deserialize;
7+
use serde::{de::Error, Deserialize};
68

79
pub const DEFAULT_CONFIG: &str = include_str!("../dstack.toml");
810

@@ -15,13 +17,25 @@ pub struct BindAddr {
1517
pub port: u16,
1618
}
1719

20+
#[derive(Debug, Clone)]
21+
pub struct AppComposeWrapper {
22+
pub app_compose: AppCompose,
23+
pub raw: String,
24+
}
25+
26+
impl Deref for AppComposeWrapper {
27+
type Target = AppCompose;
28+
29+
fn deref(&self) -> &Self::Target {
30+
&self.app_compose
31+
}
32+
}
33+
1834
#[derive(Debug, Clone, Deserialize)]
1935
pub struct Config {
20-
pub app_name: String,
2136
pub keys_file: String,
22-
pub public_logs: bool,
23-
pub public_sysinfo: bool,
24-
pub compose_file: String,
37+
#[serde(deserialize_with = "deserialize_app_compose", flatten)]
38+
pub app_compose: AppComposeWrapper,
2539
pub sys_config_file: PathBuf,
2640
#[serde(default)]
2741
pub pccs_url: Option<String>,
@@ -30,6 +44,26 @@ pub struct Config {
3044
pub data_disks: HashSet<PathBuf>,
3145
}
3246

47+
fn deserialize_app_compose<'de, D>(deserializer: D) -> Result<AppComposeWrapper, D::Error>
48+
where
49+
D: serde::Deserializer<'de>,
50+
{
51+
#[derive(Debug, Clone, Deserialize)]
52+
struct Config {
53+
compose_file: String,
54+
}
55+
56+
let config = Config::deserialize(deserializer)?;
57+
let content = fs::read_to_string(&config.compose_file)
58+
.map_err(|e| D::Error::custom(format!("Failed to read compose file: {e}")))?;
59+
let app_compose = serde_json::from_str(&content)
60+
.map_err(|e| D::Error::custom(format!("Failed to parse compose file: {e}")))?;
61+
Ok(AppComposeWrapper {
62+
app_compose,
63+
raw: content,
64+
})
65+
}
66+
3367
#[derive(Debug, Clone, Deserialize)]
3468
pub struct Simulator {
3569
pub enabled: bool,

guest-agent/src/http_routes.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::guest_api_service::{list_containers, GuestApiHandler};
55
use crate::rpc_service::{AppState, ExternalRpcHandler};
66
use anyhow::Result;
77
use docker_logs::parse_duration;
8-
use dstack_guest_agent_rpc::{worker_server::WorkerRpc, WorkerInfo};
8+
use dstack_guest_agent_rpc::{worker_server::WorkerRpc, AppInfo};
99
use guest_api::guest_api_server::GuestApiRpc;
1010
use ra_rpc::{CallContext, RpcCall};
1111
use rinja::Template;
@@ -15,18 +15,24 @@ use rocket::{get, response::content::RawHtml, routes, Route, State};
1515

1616
pub fn external_routes(config: &Config) -> Vec<Route> {
1717
let mut routes = routes![index];
18-
if config.public_logs {
19-
routes.extend(routes![get_logs, metrics]);
18+
if config.app_compose.public_logs {
19+
routes.extend(routes![get_logs]);
20+
}
21+
if config.app_compose.public_sysinfo {
22+
routes.extend(routes![metrics]);
2023
}
2124
routes
2225
}
2326

2427
#[get("/")]
2528
async fn index(state: &State<AppState>) -> Result<RawHtml<String>, String> {
29+
let public_logs = state.config().app_compose.public_logs;
30+
let public_sysinfo = state.config().app_compose.public_sysinfo;
31+
let public_tcbinfo = state.config().app_compose.public_tcbinfo;
2632
let context = CallContext::builder().state(&**state).build();
2733
let handler = ExternalRpcHandler::construct(context.clone())
2834
.map_err(|e| format!("Failed to construct RPC handler: {}", e))?;
29-
let WorkerInfo {
35+
let AppInfo {
3036
app_name,
3137
app_id,
3238
instance_id,
@@ -38,8 +44,6 @@ async fn index(state: &State<AppState>) -> Result<RawHtml<String>, String> {
3844
compose_hash: _,
3945
tcb_info,
4046
app_cert: _,
41-
public_logs,
42-
public_sysinfo,
4347
vm_config: _,
4448
} = handler
4549
.info()
@@ -62,6 +66,7 @@ async fn index(state: &State<AppState>) -> Result<RawHtml<String>, String> {
6266
system_info,
6367
public_sysinfo,
6468
public_logs,
69+
public_tcbinfo,
6570
};
6671
match model.render() {
6772
Ok(html) => Ok(RawHtml(html)),
@@ -72,7 +77,7 @@ async fn index(state: &State<AppState>) -> Result<RawHtml<String>, String> {
7277
// Returns metrics about the guest in prometheus format if public_sysinfo is enabled
7378
#[get("/metrics")]
7479
async fn metrics(state: &State<AppState>) -> Result<String, String> {
75-
let public_sysinfo = state.config().public_sysinfo;
80+
let public_sysinfo = state.config().app_compose.public_sysinfo;
7681
if !public_sysinfo {
7782
return Err("Sysinfo API is disabled".to_string());
7883
}

guest-agent/src/models.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub struct Dashboard {
4343
pub system_info: SystemInfo,
4444
pub public_sysinfo: bool,
4545
pub public_logs: bool,
46+
pub public_tcbinfo: bool,
4647
}
4748

4849
#[derive(Template)]

guest-agent/src/rpc_service.rs

Lines changed: 68 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ use dstack_guest_agent_rpc::{
66
dstack_guest_server::{DstackGuestRpc, DstackGuestServer},
77
tappd_server::{TappdRpc, TappdServer},
88
worker_server::{WorkerRpc, WorkerServer},
9-
DeriveK256KeyResponse, DeriveKeyArgs, EmitEventArgs, GetKeyArgs, GetKeyResponse,
9+
AppInfo, DeriveK256KeyResponse, DeriveKeyArgs, EmitEventArgs, GetKeyArgs, GetKeyResponse,
1010
GetQuoteResponse, GetTlsKeyArgs, GetTlsKeyResponse, RawQuoteArgs, TdxQuoteArgs,
11-
TdxQuoteResponse, WorkerInfo, WorkerVersion,
11+
TdxQuoteResponse, WorkerVersion,
1212
};
1313
use dstack_types::{AppKeys, SysConfig};
1414
use fs_err as fs;
@@ -89,6 +89,66 @@ pub struct InternalRpcHandler {
8989
state: AppState,
9090
}
9191

92+
pub async fn get_info(state: &AppState, external: bool) -> Result<AppInfo> {
93+
let hide_tcb_info = external && !state.config().app_compose.public_tcbinfo;
94+
let response = InternalRpcHandler {
95+
state: state.clone(),
96+
}
97+
.get_quote(RawQuoteArgs {
98+
report_data: [0; 64].to_vec(),
99+
})
100+
.await;
101+
let Ok(response) = response else {
102+
return Ok(AppInfo::default());
103+
};
104+
let Ok(attestation) = Attestation::new(response.quote, response.event_log.into()) else {
105+
return Ok(AppInfo::default());
106+
};
107+
let app_info = attestation
108+
.decode_app_info(false)
109+
.context("Failed to decode app info")?;
110+
let event_log = &attestation.event_log;
111+
let tcb_info = if hide_tcb_info {
112+
"".to_string()
113+
} else {
114+
let app_compose = state.config().app_compose.raw.clone();
115+
serde_json::to_string_pretty(&json!({
116+
"mrtd": hex::encode(app_info.mrtd),
117+
"rtmr0": hex::encode(app_info.rtmr0),
118+
"rtmr1": hex::encode(app_info.rtmr1),
119+
"rtmr2": hex::encode(app_info.rtmr2),
120+
"rtmr3": hex::encode(app_info.rtmr3),
121+
"mr_aggregated": hex::encode(app_info.mr_aggregated),
122+
"os_image_hash": hex::encode(&app_info.os_image_hash),
123+
"mr_key_provider": hex::encode(app_info.mr_key_provider),
124+
"compose_hash": hex::encode(&app_info.compose_hash),
125+
"device_id": hex::encode(&app_info.device_id),
126+
"event_log": event_log,
127+
"app_compose": app_compose,
128+
}))
129+
.unwrap_or_default()
130+
};
131+
let vm_config = if hide_tcb_info {
132+
"".to_string()
133+
} else {
134+
state.inner.vm_config.clone()
135+
};
136+
Ok(AppInfo {
137+
app_name: state.config().app_compose.name.clone(),
138+
app_id: app_info.app_id,
139+
instance_id: app_info.instance_id,
140+
device_id: app_info.device_id,
141+
mr_aggregated: app_info.mr_aggregated.to_vec(),
142+
os_image_hash: app_info.os_image_hash.clone(),
143+
mr_key_provider: app_info.mr_key_provider.to_vec(),
144+
key_provider_info: String::from_utf8(app_info.key_provider_info).unwrap_or_default(),
145+
compose_hash: app_info.compose_hash.clone(),
146+
app_cert: state.inner.demo_cert.clone(),
147+
tcb_info,
148+
vm_config,
149+
})
150+
}
151+
92152
impl DstackGuestRpc for InternalRpcHandler {
93153
async fn get_tls_key(self, request: GetTlsKeyArgs) -> anyhow::Result<GetTlsKeyResponse> {
94154
let mut seed = [0u8; 32];
@@ -175,8 +235,8 @@ impl DstackGuestRpc for InternalRpcHandler {
175235
tdx_attest::extend_rtmr3(&request.event, &request.payload)
176236
}
177237

178-
async fn info(self) -> Result<WorkerInfo> {
179-
ExternalRpcHandler { state: self.state }.info().await
238+
async fn info(self) -> Result<AppInfo> {
239+
get_info(&self.state, false).await
180240
}
181241
}
182242

@@ -304,8 +364,8 @@ impl TappdRpc for InternalRpcHandlerV0 {
304364
.await
305365
}
306366

307-
async fn info(self) -> Result<WorkerInfo> {
308-
ExternalRpcHandler { state: self.state }.info().await
367+
async fn info(self) -> Result<AppInfo> {
368+
get_info(&self.state, false).await
309369
}
310370
}
311371

@@ -330,56 +390,8 @@ impl ExternalRpcHandler {
330390
}
331391

332392
impl WorkerRpc for ExternalRpcHandler {
333-
async fn info(self) -> Result<WorkerInfo> {
334-
let response = InternalRpcHandler {
335-
state: self.state.clone(),
336-
}
337-
.get_quote(RawQuoteArgs {
338-
report_data: [0; 64].to_vec(),
339-
})
340-
.await;
341-
let Ok(response) = response else {
342-
return Ok(WorkerInfo::default());
343-
};
344-
let Ok(attestation) = Attestation::new(response.quote, response.event_log.into()) else {
345-
return Ok(WorkerInfo::default());
346-
};
347-
let app_info = attestation
348-
.decode_app_info(false)
349-
.context("Failed to decode app info")?;
350-
let event_log = &attestation.event_log;
351-
let app_compose = fs::read_to_string(&self.state.config().compose_file).unwrap_or_default();
352-
let tcb_info = serde_json::to_string_pretty(&json!({
353-
"mrtd": hex::encode(app_info.mrtd),
354-
"rtmr0": hex::encode(app_info.rtmr0),
355-
"rtmr1": hex::encode(app_info.rtmr1),
356-
"rtmr2": hex::encode(app_info.rtmr2),
357-
"rtmr3": hex::encode(app_info.rtmr3),
358-
"mr_aggregated": hex::encode(app_info.mr_aggregated),
359-
"os_image_hash": hex::encode(&app_info.os_image_hash),
360-
"mr_key_provider": hex::encode(app_info.mr_key_provider),
361-
"compose_hash": hex::encode(&app_info.compose_hash),
362-
"device_id": hex::encode(&app_info.device_id),
363-
"event_log": event_log,
364-
"app_compose": app_compose,
365-
}))
366-
.unwrap_or_default();
367-
Ok(WorkerInfo {
368-
app_name: self.state.config().app_name.clone(),
369-
app_id: app_info.app_id,
370-
instance_id: app_info.instance_id,
371-
device_id: app_info.device_id,
372-
mr_aggregated: app_info.mr_aggregated.to_vec(),
373-
os_image_hash: app_info.os_image_hash.clone(),
374-
mr_key_provider: app_info.mr_key_provider.to_vec(),
375-
key_provider_info: String::from_utf8(app_info.key_provider_info).unwrap_or_default(),
376-
compose_hash: app_info.compose_hash.clone(),
377-
app_cert: self.state.inner.demo_cert.clone(),
378-
tcb_info,
379-
public_logs: self.state.config().public_logs,
380-
public_sysinfo: self.state.config().public_sysinfo,
381-
vm_config: self.state.inner.vm_config.clone(),
382-
})
393+
async fn info(self) -> Result<AppInfo> {
394+
get_info(&self.state, true).await
383395
}
384396

385397
async fn version(self) -> Result<WorkerVersion> {

0 commit comments

Comments
 (0)