mirror of
https://github.com/telemt/telemt.git
synced 2026-11-01 21:31:58 +03:00
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:
@@ -0,0 +1,133 @@
|
||||
use super::*;
|
||||
|
||||
const SOURCE: &str = r#"
|
||||
[access.users]
|
||||
alice = "000102030405060708090a0b0c0d0e0f"
|
||||
[[server.listeners]]
|
||||
ip = "127.0.0.1"
|
||||
port = 18080
|
||||
transport = "web"
|
||||
web_trusted_proxy_cidrs = ["127.0.0.1/32"]
|
||||
[web]
|
||||
enabled = true
|
||||
[[web.vhosts]]
|
||||
host = "proxy.example.com"
|
||||
public_addr = "203.0.113.10:443"
|
||||
[web.vhosts.decoy]
|
||||
mode = "http_upstream"
|
||||
upstream = "http://localhost:18081"
|
||||
resolve = "startup"
|
||||
[[web.vhosts.profiles]]
|
||||
user = "alice"
|
||||
secret_mode = "dd"
|
||||
"#;
|
||||
|
||||
#[tokio::test]
|
||||
async fn decoy_dns_watcher_publishes_dns_only_changes_once() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = dir.path().join("config.toml");
|
||||
std::fs::write(&path, SOURCE).unwrap();
|
||||
let mut loaded = ProxyConfig::load_prepared(path.clone()).await.unwrap();
|
||||
let (config_tx, mut config_rx) = watch::channel(Arc::new(loaded.config.clone()));
|
||||
let (log_tx, _) = watch::channel(loaded.config.general.log_level.clone());
|
||||
let old = config_rx.borrow().clone();
|
||||
let mut state = ReloadState::new(Some(loaded.rendered_hash));
|
||||
Arc::make_mut(&mut loaded.config.web.decoy_dns)
|
||||
.origins
|
||||
.insert(
|
||||
("localhost".to_string(), 18081),
|
||||
vec!["127.0.0.2:18081".parse().unwrap()],
|
||||
);
|
||||
loaded.config.rebuild_runtime_web().unwrap();
|
||||
let applied = prepare_effective_config(&old, &loaded.config).unwrap();
|
||||
reload_config_with_resolver(
|
||||
(loaded.clone(), applied),
|
||||
&config_tx,
|
||||
&log_tx,
|
||||
None,
|
||||
None,
|
||||
&mut state,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(config_rx.has_changed().unwrap());
|
||||
let published = config_rx.borrow_and_update().clone();
|
||||
assert!(!old.web_decoy_endpoints_equal(&published));
|
||||
let applied = prepare_effective_config(&published, &loaded.config).unwrap();
|
||||
reload_config_with_resolver(
|
||||
(loaded, applied),
|
||||
&config_tx,
|
||||
&log_tx,
|
||||
None,
|
||||
None,
|
||||
&mut state,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!config_rx.has_changed().unwrap());
|
||||
assert!(Arc::ptr_eq(&published, &config_rx.borrow()));
|
||||
|
||||
preparation::reload(
|
||||
&path, &config_tx, &log_tx, None, None, &mut state, None, false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!config_rx.has_changed().unwrap());
|
||||
assert!(Arc::ptr_eq(&published, &config_rx.borrow()));
|
||||
|
||||
// Explicit reload bypasses the unchanged source hash and replaces the injected endpoint.
|
||||
preparation::reload(
|
||||
&path, &config_tx, &log_tx, None, None, &mut state, None, true,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(config_rx.has_changed().unwrap());
|
||||
assert!(!published.web_decoy_endpoints_equal(&config_rx.borrow()));
|
||||
|
||||
let current = config_rx.borrow_and_update().clone();
|
||||
std::fs::write(&path, SOURCE.replace("localhost:18081", "localhost:18080")).unwrap();
|
||||
preparation::reload(
|
||||
&path, &config_tx, &log_tx, None, None, &mut state, None, false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!config_rx.has_changed().unwrap());
|
||||
assert!(Arc::ptr_eq(¤t, &config_rx.borrow()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn decoy_dns_unchanged_watcher_source_skips_resolution() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = dir.path().join("config.toml");
|
||||
std::fs::write(
|
||||
&path,
|
||||
SOURCE.replace("localhost", "unresolvable.example.invalid"),
|
||||
)
|
||||
.unwrap();
|
||||
let source = preparation::read_source(&path).await.unwrap();
|
||||
let initial = Arc::new(source.config.clone());
|
||||
let (config_tx, config_rx) = watch::channel(initial.clone());
|
||||
let (log_tx, _) = watch::channel(initial.general.log_level.clone());
|
||||
let mut state = ReloadState::new(Some(source.rendered_hash));
|
||||
assert!(preparation::source_matches_active(&initial, &source));
|
||||
preparation::reload(
|
||||
&path, &config_tx, &log_tx, None, None, &mut state, None, false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!config_rx.has_changed().unwrap());
|
||||
assert!(Arc::ptr_eq(&initial, &config_rx.borrow()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn decoy_dns_watcher_init_ignores_retained_restart_fields() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = dir.path().join("config.toml");
|
||||
std::fs::write(&path, SOURCE).unwrap();
|
||||
let loaded = ProxyConfig::load_prepared(path.clone()).await.unwrap();
|
||||
let mut source = ProxyConfig::parse_source(&path).unwrap();
|
||||
source.config.web.limits.max_static_file_bytes += 1;
|
||||
assert!(preparation::source_matches_active(&loaded.config, &source));
|
||||
source.config.web.conveyor = !source.config.web.conveyor;
|
||||
assert!(!preparation::source_matches_active(&loaded.config, &source));
|
||||
}
|
||||
Reference in New Issue
Block a user