Skip to content

Commit 06d89a2

Browse files
committed
kms: enforce self authorization on trusted RPCs
1 parent d3c2ca1 commit 06d89a2

3 files changed

Lines changed: 94 additions & 11 deletions

File tree

kms/src/main_service.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ use ra_tls::{
2222
};
2323
use scale::Decode;
2424
use sha2::Digest;
25+
use tokio::sync::OnceCell;
2526
use tracing::info;
26-
use upgrade_authority::{build_boot_info, BootInfo};
27+
use upgrade_authority::{build_boot_info, local_kms_boot_info, BootInfo};
2728

2829
use crate::{
2930
config::KmsConfig,
@@ -52,6 +53,7 @@ pub struct KmsStateInner {
5253
temp_ca_cert: String,
5354
temp_ca_key: String,
5455
verifier: CvmVerifier,
56+
self_boot_info: OnceCell<BootInfo>,
5557
}
5658

5759
impl KmsState {
@@ -79,6 +81,7 @@ impl KmsState {
7981
temp_ca_cert,
8082
temp_ca_key,
8183
verifier,
84+
self_boot_info: OnceCell::new(),
8285
}),
8386
})
8487
}
@@ -95,6 +98,31 @@ struct BootConfig {
9598
}
9699

97100
impl RpcHandler {
101+
async fn ensure_self_allowed(&self) -> Result<()> {
102+
if !self.state.config.onboard.quote_enabled {
103+
return Ok(());
104+
}
105+
let boot_info = self
106+
.state
107+
.self_boot_info
108+
.get_or_try_init(|| async {
109+
local_kms_boot_info(self.state.config.pccs_url.as_deref()).await
110+
})
111+
.await
112+
.context("Failed to load cached self boot info")?;
113+
let response = self
114+
.state
115+
.config
116+
.auth_api
117+
.is_app_allowed(boot_info, true)
118+
.await
119+
.context("Failed to call self KMS auth check")?;
120+
if !response.is_allowed {
121+
bail!("KMS is not allowed: {}", response.reason);
122+
}
123+
Ok(())
124+
}
125+
98126
fn ensure_attested(&self) -> Result<&VerifiedAttestation> {
99127
let Some(attestation) = &self.attestation else {
100128
bail!("No attestation provided");
@@ -214,6 +242,9 @@ impl KmsRpc for RpcHandler {
214242
if request.api_version > 1 {
215243
bail!("Unsupported API version: {}", request.api_version);
216244
}
245+
self.ensure_self_allowed()
246+
.await
247+
.context("KMS self authorization failed")?;
217248
let BootConfig {
218249
boot_info,
219250
gateway_app_id,
@@ -254,6 +285,9 @@ impl KmsRpc for RpcHandler {
254285
}
255286

256287
async fn get_app_env_encrypt_pub_key(self, request: AppId) -> Result<PublicKeyResponse> {
288+
self.ensure_self_allowed()
289+
.await
290+
.context("KMS self authorization failed")?;
257291
let secret = kdf::derive_dh_secret(
258292
&self.state.root_ca.key,
259293
&[&request.app_id[..], "env-encrypt-key".as_bytes()],
@@ -320,6 +354,9 @@ impl KmsRpc for RpcHandler {
320354
}
321355

322356
async fn get_kms_key(self, request: GetKmsKeyRequest) -> Result<KmsKeyResponse> {
357+
self.ensure_self_allowed()
358+
.await
359+
.context("KMS self authorization failed")?;
323360
if self.state.config.onboard.quote_enabled {
324361
let _info = self.ensure_kms_allowed(&request.vm_config).await?;
325362
}
@@ -333,6 +370,9 @@ impl KmsRpc for RpcHandler {
333370
}
334371

335372
async fn get_temp_ca_cert(self) -> Result<GetTempCaCertResponse> {
373+
self.ensure_self_allowed()
374+
.await
375+
.context("KMS self authorization failed")?;
336376
Ok(GetTempCaCertResponse {
337377
temp_ca_cert: self.state.inner.temp_ca_cert.clone(),
338378
temp_ca_key: self.state.inner.temp_ca_key.clone(),
@@ -341,6 +381,9 @@ impl KmsRpc for RpcHandler {
341381
}
342382

343383
async fn sign_cert(self, request: SignCertRequest) -> Result<SignCertResponse> {
384+
self.ensure_self_allowed()
385+
.await
386+
.context("KMS self authorization failed")?;
344387
let csr = match request.api_version {
345388
1 => {
346389
let csr = CertSigningRequestV1::decode(&mut &request.csr[..])

kms/src/main_service/upgrade_authority.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44

55
use crate::config::AuthApi;
66
use anyhow::{bail, Context, Result};
7+
use dstack_guest_agent_rpc::{
8+
dstack_guest_client::DstackGuestClient, AttestResponse, RawQuoteArgs,
9+
};
10+
use http_client::prpc::PrpcClient;
711
use ra_tls::attestation::AttestationMode;
812
use ra_tls::attestation::VerifiedAttestation;
13+
use ra_tls::attestation::VersionedAttestation;
914
use serde::de::DeserializeOwned;
1015
use serde::{Deserialize, Serialize};
1116
use serde_human_bytes as hex_bytes;
@@ -67,6 +72,20 @@ pub(crate) fn build_boot_info(
6772
})
6873
}
6974

75+
pub(crate) async fn local_kms_boot_info(pccs_url: Option<&str>) -> Result<BootInfo> {
76+
let response = app_attest(pad64([0u8; 32]))
77+
.await
78+
.context("Failed to get local KMS attestation")?;
79+
let attestation = VersionedAttestation::from_scale(&response.attestation)
80+
.context("Failed to decode local KMS attestation")?
81+
.into_inner();
82+
let verified = attestation
83+
.verify(pccs_url)
84+
.await
85+
.context("Failed to verify local KMS attestation")?;
86+
build_boot_info(&verified, false, "")
87+
}
88+
7089
#[derive(Debug, Serialize, Deserialize)]
7190
#[serde(rename_all = "camelCase")]
7291
pub(crate) struct BootResponse {
@@ -168,3 +187,20 @@ fn url_join(url: &str, path: &str) -> String {
168187
url.push_str(path);
169188
url
170189
}
190+
191+
fn dstack_client() -> DstackGuestClient<PrpcClient> {
192+
let address = dstack_types::dstack_agent_address();
193+
let http_client = PrpcClient::new(address);
194+
DstackGuestClient::new(http_client)
195+
}
196+
197+
async fn app_attest(report_data: Vec<u8>) -> Result<AttestResponse> {
198+
dstack_client().attest(RawQuoteArgs { report_data }).await
199+
}
200+
201+
fn pad64(hash: [u8; 32]) -> Vec<u8> {
202+
let mut padded = Vec::with_capacity(64);
203+
padded.extend_from_slice(&hash);
204+
padded.resize(64, 0);
205+
padded
206+
}

kms/src/onboard_service.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ use ra_tls::{
2828
};
2929
use safe_write::safe_write;
3030

31-
use crate::{config::KmsConfig, main_service::upgrade_authority::build_boot_info};
31+
use crate::{
32+
config::KmsConfig,
33+
main_service::upgrade_authority::{build_boot_info, local_kms_boot_info},
34+
};
3235

3336
#[derive(Clone)]
3437
pub struct OnboardState {
@@ -400,17 +403,18 @@ async fn app_attest(report_data: Vec<u8>) -> Result<AttestResponse> {
400403
}
401404

402405
async fn ensure_self_kms_allowed(cfg: &KmsConfig) -> Result<()> {
403-
let response = app_attest(pad64([0u8; 32]))
406+
let boot_info = local_kms_boot_info(cfg.pccs_url.as_deref())
404407
.await
405-
.context("Failed to get local KMS attestation")?;
406-
let attestation = VersionedAttestation::from_scale(&response.attestation)
407-
.context("Failed to decode local KMS attestation")?
408-
.into_inner();
409-
let verified = attestation
410-
.verify(cfg.pccs_url.as_deref())
408+
.context("Failed to build local KMS boot info")?;
409+
let response = cfg
410+
.auth_api
411+
.is_app_allowed(&boot_info, true)
411412
.await
412-
.context("Failed to verify local KMS attestation")?;
413-
ensure_kms_allowed(cfg, &verified).await
413+
.context("Failed to call KMS auth check")?;
414+
if !response.is_allowed {
415+
bail!("Boot denied: {}", response.reason);
416+
}
417+
Ok(())
414418
}
415419

416420
async fn ensure_remote_kms_allowed(

0 commit comments

Comments
 (0)