WEB: Resolve opt-in decoy hostnames during config preparation #929

Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
Alexey
2026-10-04 14:32:06 +03:00
parent d3de9865cf
commit bc1c130f38
30 changed files with 1251 additions and 168 deletions
+43
View File
@@ -29,6 +29,8 @@ mod runtime_web;
mod decode;
mod effective;
mod pipeline;
// Candidate-owned DNS preparation is separate from pure runtime reconstruction.
mod decoy_dns;
mod validate_core;
mod validate_me;
mod validate_runtime;
@@ -74,6 +76,19 @@ pub(crate) struct LoadedConfig {
pub(crate) rendered_hash: u64,
}
/// Normalized source configuration that has not acquired runtime DNS or static snapshots.
#[derive(Debug, Clone)]
pub(crate) struct ParsedConfigSource {
/// Source-only configuration; must be prepared before runtime publication.
pub(crate) config: ProxyConfig,
/// Normalized paths in the captured include graph.
pub(crate) source_files: Vec<PathBuf>,
/// Exact source documents used for revisions and atomic mutation checks.
pub(crate) source_contents: BTreeMap<PathBuf, String>,
/// Source-only hash, independent of DNS answers.
pub(crate) rendered_hash: u64,
}
/// Raw recursive source graph captured before typed deserialization.
#[derive(Debug, Clone)]
pub(crate) struct ConfigSourceGraph {
@@ -229,6 +244,33 @@ impl ProxyConfig {
pipeline::load_source_graph(graph)
}
/// Parses source without resolving decoys or creating runtime WEB snapshots.
pub(crate) fn parse_source<P: AsRef<Path>>(path: P) -> Result<ParsedConfigSource> {
Self::parse_source_with_overrides(path, &BTreeMap::new())
}
/// Checks decoy mode-specific keys before a source or API patch loses unknown fields.
pub(crate) fn validate_decoy_source_keys(document: &toml::Value) -> Result<()> {
decoy_dns::validate_mode_keys(document)
}
/// Parses an atomic mutation candidate without performing DNS.
pub(crate) fn parse_source_with_overrides<P: AsRef<Path>>(
path: P,
overrides: &BTreeMap<PathBuf, String>,
) -> Result<ParsedConfigSource> {
pipeline::parse_source_graph(Self::read_source_graph_with_overrides(path, overrides)?)
}
/// Loads and prepares a complete runtime candidate without blocking async workers.
pub(crate) async fn load_prepared(path: PathBuf) -> Result<LoadedConfig> {
let parsed = tokio::task::spawn_blocking(move || Self::parse_source(path))
.await
.map_err(|error| ProxyError::Config(format!("config reader failed: {error}")))??;
parsed.prepare().await
}
/// Rebuilds immutable credential lookup data for this configuration.
pub(crate) fn rebuild_runtime_user_auth(&mut self) -> Result<()> {
let snapshot = UserAuthSnapshot::from_users(&self.access.users)?;
self.runtime_user_auth = Some(Arc::new(snapshot));
@@ -250,6 +292,7 @@ impl ProxyConfig {
validate_web::validate_decoy_listener_separation(self)
}
/// Returns authentication data owned by this configuration generation.
pub(crate) fn runtime_user_auth(&self) -> Option<&UserAuthSnapshot> {
self.runtime_user_auth.as_deref()
}