Skip to content

Commit b6eca71

Browse files
committed
fix(kms): validate auto bootstrap domain before key onboarding
1 parent 07faea5 commit b6eca71

1 file changed

Lines changed: 52 additions & 19 deletions

File tree

kms/src/onboard_service.rs

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,12 @@ struct Keys {
159159
}
160160

161161
impl Keys {
162-
async fn generate(domain: &str) -> Result<Self> {
162+
async fn generate(domain: &str, quote_enabled: bool) -> Result<Self> {
163163
let tmp_ca_key = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?;
164164
let ca_key = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?;
165165
let rpc_key = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?;
166166
let k256_key = SigningKey::random(&mut rand::rngs::OsRng);
167-
Self::from_keys(tmp_ca_key, ca_key, rpc_key, k256_key, domain).await
167+
Self::from_keys(tmp_ca_key, ca_key, rpc_key, k256_key, domain, quote_enabled).await
168168
}
169169

170170
async fn from_keys(
@@ -173,6 +173,7 @@ impl Keys {
173173
rpc_key: KeyPair,
174174
k256_key: SigningKey,
175175
domain: &str,
176+
quote_enabled: bool,
176177
) -> Result<Self> {
177178
let tmp_ca_cert = CertRequest::builder()
178179
.org_name("Dstack")
@@ -190,20 +191,25 @@ impl Keys {
190191
.key(&ca_key)
191192
.build()
192193
.self_signed()?;
193-
let pubkey = rpc_key.public_key_der();
194-
let report_data = QuoteContentType::RaTlsCert.to_report_data(&pubkey);
195-
let response = app_attest(report_data.to_vec())
196-
.await
197-
.context("Failed to get quote")?;
198-
let attestation = VersionedAttestation::from_scale(&response.attestation)
199-
.context("Invalid attestation")?;
194+
let attestation = if quote_enabled {
195+
let pubkey = rpc_key.public_key_der();
196+
let report_data = QuoteContentType::RaTlsCert.to_report_data(&pubkey);
197+
let response = app_attest(report_data.to_vec())
198+
.await
199+
.context("Failed to get quote")?;
200+
let attestation = VersionedAttestation::from_scale(&response.attestation)
201+
.context("Invalid attestation")?;
202+
Some(attestation)
203+
} else {
204+
None
205+
};
200206

201207
// Sign WWW server cert with KMS cert
202208
let rpc_cert = CertRequest::builder()
203209
.subject(domain)
204210
.alt_names(&[domain.to_string()])
205211
.special_usage("kms:rpc")
206-
.maybe_attestation(Some(&attestation))
212+
.maybe_attestation(attestation.as_ref())
207213
.key(&rpc_key)
208214
.build()
209215
.signed_by(&ca_cert, &ca_key)?;
@@ -308,6 +314,16 @@ impl Keys {
308314
}
309315
}
310316

317+
fn validate_domain(domain: &str, source: &str) -> Result<String> {
318+
let domain = domain.trim();
319+
if domain.is_empty() {
320+
return Err(anyhow::anyhow!(
321+
"invalid domain from {source}: empty or whitespace-only"
322+
));
323+
}
324+
Ok(domain.to_string())
325+
}
326+
311327
pub(crate) async fn update_certs(cfg: &KmsConfig) -> Result<()> {
312328
// Read existing keys
313329
let tmp_ca_key = KeyPair::from_pem(&fs::read_to_string(cfg.tmp_ca_key())?)?;
@@ -318,17 +334,26 @@ pub(crate) async fn update_certs(cfg: &KmsConfig) -> Result<()> {
318334
let k256_key_bytes = fs::read(cfg.k256_key())?;
319335
let k256_key = SigningKey::from_slice(&k256_key_bytes)?;
320336

321-
let domain = if cfg.onboard.auto_bootstrap_domain.is_empty() {
322-
fs::read_to_string(cfg.rpc_domain())?
337+
let domain = if cfg.onboard.auto_bootstrap_domain.trim().is_empty() {
338+
validate_domain(&fs::read_to_string(cfg.rpc_domain())?, "stored rpc_domain")?
323339
} else {
324-
cfg.onboard.auto_bootstrap_domain.clone()
340+
validate_domain(
341+
&cfg.onboard.auto_bootstrap_domain,
342+
"core.onboard.auto_bootstrap_domain",
343+
)?
325344
};
326-
let domain = domain.trim();
327345

328346
// Regenerate certificates using existing keys
329-
let keys = Keys::from_keys(tmp_ca_key, ca_key, rpc_key, k256_key, domain)
330-
.await
331-
.context("Failed to regenerate certificates")?;
347+
let keys = Keys::from_keys(
348+
tmp_ca_key,
349+
ca_key,
350+
rpc_key,
351+
k256_key,
352+
&domain,
353+
cfg.onboard.quote_enabled,
354+
)
355+
.await
356+
.context("Failed to regenerate certificates")?;
332357

333358
// Write the new certificates to files
334359
keys.store_certs(cfg)?;
@@ -347,9 +372,13 @@ pub(crate) async fn auto_onboard_keys(cfg: &KmsConfig) -> Result<()> {
347372
} else {
348373
format!("{source_url}/prpc")
349374
};
375+
let domain = validate_domain(
376+
&cfg.onboard.auto_bootstrap_domain,
377+
"core.onboard.auto_bootstrap_domain",
378+
)?;
350379
let keys = Keys::onboard(
351380
&source_url,
352-
&cfg.onboard.auto_bootstrap_domain,
381+
&domain,
353382
cfg.onboard.quote_enabled,
354383
cfg.pccs_url.clone(),
355384
)
@@ -363,7 +392,11 @@ pub(crate) async fn bootstrap_keys(cfg: &KmsConfig) -> Result<()> {
363392
ensure_self_kms_allowed(cfg)
364393
.await
365394
.context("KMS is not allowed to auto-bootstrap")?;
366-
let keys = Keys::generate(&cfg.onboard.auto_bootstrap_domain)
395+
let domain = validate_domain(
396+
&cfg.onboard.auto_bootstrap_domain,
397+
"core.onboard.auto_bootstrap_domain",
398+
)?;
399+
let keys = Keys::generate(&domain, cfg.onboard.quote_enabled)
367400
.await
368401
.context("Failed to generate keys")?;
369402
keys.store(cfg)?;

0 commit comments

Comments
 (0)