Skip to content

Commit 3f41856

Browse files
authored
Merge pull request #933 from Dstack-TEE/codex/fix-gateway-zt-domain-crud
[STACKED on #868] fix(gateway): normalize ZT domain CRUD keys
2 parents bed81df + 5f96b0b commit 3f41856

1 file changed

Lines changed: 27 additions & 21 deletions

File tree

dstack/gateway/src/admin_service.rs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -473,8 +473,9 @@ impl AdminRpc for AdminRpcHandler {
473473
let kv_store = self.state.kv_store();
474474
let cert_resolver = &self.state.cert_resolver;
475475

476+
let domain = normalize_zt_domain(&request.domain)?;
476477
let config = kv_store
477-
.get_zt_domain_config(&request.domain)
478+
.get_zt_domain_config(&domain)
478479
.context("ZT-Domain config not found")?;
479480

480481
Ok(zt_domain_to_proto(config, kv_store, cert_resolver))
@@ -484,13 +485,14 @@ impl AdminRpc for AdminRpcHandler {
484485
let kv_store = self.state.kv_store();
485486
let cert_resolver = &self.state.cert_resolver;
486487

487-
// Check if domain already exists
488-
if kv_store.get_zt_domain_config(&request.domain).is_some() {
489-
bail!("ZT-Domain config already exists: {}", request.domain);
490-
}
491-
492488
let config = proto_to_zt_domain_config(&request, kv_store)?;
493489

490+
// Uniqueness is checked after normalization so wildcard, case, and a
491+
// trailing root dot cannot silently overwrite the same DNS name.
492+
if kv_store.get_zt_domain_config(&config.domain).is_some() {
493+
bail!("ZT-Domain config already exists: {}", config.domain);
494+
}
495+
494496
kv_store.save_zt_domain_config(&config)?;
495497
info!("Added ZT-Domain config: {}", config.domain);
496498

@@ -501,13 +503,13 @@ impl AdminRpc for AdminRpcHandler {
501503
let kv_store = self.state.kv_store();
502504
let cert_resolver = &self.state.cert_resolver;
503505

504-
// Check if config exists
506+
let config = proto_to_zt_domain_config(&request, kv_store)?;
507+
508+
// Check the normalized key rather than the caller's presentation.
505509
kv_store
506-
.get_zt_domain_config(&request.domain)
510+
.get_zt_domain_config(&config.domain)
507511
.context("ZT-Domain config not found")?;
508512

509-
let config = proto_to_zt_domain_config(&request, kv_store)?;
510-
511513
kv_store.save_zt_domain_config(&config)?;
512514
info!("Updated ZT-Domain config: {}", config.domain);
513515

@@ -517,14 +519,14 @@ impl AdminRpc for AdminRpcHandler {
517519
async fn delete_zt_domain(self, request: DeleteZtDomainRequest) -> Result<()> {
518520
let kv_store = self.state.kv_store();
519521

520-
// Check if config exists
522+
let domain = normalize_zt_domain(&request.domain)?;
521523
kv_store
522-
.get_zt_domain_config(&request.domain)
524+
.get_zt_domain_config(&domain)
523525
.context("ZT-Domain config not found")?;
524526

525527
// Delete config (cert data, acme, attestations are kept for historical purposes)
526-
kv_store.delete_zt_domain_config(&request.domain)?;
527-
info!("Deleted ZT-Domain config: {}", request.domain);
528+
kv_store.delete_zt_domain_config(&domain)?;
529+
info!("Deleted ZT-Domain config: {domain}");
528530
Ok(())
529531
}
530532

@@ -797,6 +799,16 @@ fn redact_token(token: &str) -> String {
797799
}
798800
}
799801

802+
fn normalize_zt_domain(domain: &str) -> Result<String> {
803+
let domain = domain.trim().trim_end_matches('.');
804+
let domain = domain
805+
.strip_prefix("*.")
806+
.unwrap_or(domain)
807+
.to_ascii_lowercase();
808+
validate_zt_domain(&domain)?;
809+
Ok(domain)
810+
}
811+
800812
fn validate_zt_domain(domain: &str) -> Result<()> {
801813
if domain.is_empty() || domain.len() > 253 || !domain.is_ascii() {
802814
bail!("domain must be a non-empty ASCII DNS name of at most 253 bytes");
@@ -835,13 +847,7 @@ fn proto_to_zt_domain_config(
835847
.context("specified dns credential not found")?;
836848
}
837849

838-
// Strip wildcard prefix if user entered it
839-
let domain = proto
840-
.domain
841-
.strip_prefix("*.")
842-
.unwrap_or(&proto.domain)
843-
.to_string();
844-
validate_zt_domain(&domain)?;
850+
let domain = normalize_zt_domain(&proto.domain)?;
845851
if proto.port == 0 {
846852
bail!("port must be between 1 and 65535");
847853
}

0 commit comments

Comments
 (0)