mirror of
https://github.com/telemt/telemt.git
synced 2026-09-11 13:04:08 +03:00
084834f5ec
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
264 lines
8.6 KiB
Rust
264 lines
8.6 KiB
Rust
use super::*;
|
|
|
|
fn test_listener(port: u16) -> crate::config::ListenerConfig {
|
|
crate::config::ListenerConfig {
|
|
ip: "127.0.0.1".parse().unwrap(),
|
|
transport: crate::config::ListenerTransport::Mtproxy,
|
|
port: Some(port),
|
|
client_mss: None,
|
|
synlimit: crate::config::SynLimitMode::Off,
|
|
synlimit_seconds: 60,
|
|
synlimit_hitcount: 48,
|
|
synlimit_burst: 24,
|
|
synlimit_ios_seconds: 1,
|
|
synlimit_ios_hitcount: 12,
|
|
synlimit_ios_burst: 24,
|
|
synlimit_hashlimit_expire_ms: 60_000,
|
|
synlimit_hashlimit_size: 32_768,
|
|
announce: None,
|
|
announce_ip: None,
|
|
proxy_protocol: None,
|
|
reuse_allow: false,
|
|
web_client_ip_source: crate::config::WebClientIpSource::XForwardedFor,
|
|
web_trusted_proxy_cidrs: Vec::new(),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn process_socket_and_logging_changes_are_deferred() {
|
|
let old = ProxyConfig::default();
|
|
let mut new = old.clone();
|
|
new.server.listen_backlog = new.server.listen_backlog.saturating_add(1);
|
|
new.general.disable_colors = !new.general.disable_colors;
|
|
|
|
let fields = deferred_process_fields(&old, &new);
|
|
assert!(fields.contains(&"server.listeners".to_string()));
|
|
assert!(fields.contains(&"general.disable_colors".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn global_mss_profiles_are_deferred_with_the_listener_socket_group() {
|
|
let old = ProxyConfig::default();
|
|
let mut desired = old.clone();
|
|
desired.server.client_mss = Some("92".to_string());
|
|
desired.server.client_mss_bulk = Some("1400".to_string());
|
|
|
|
let resolved = resolve_reload_config(&old, &desired);
|
|
|
|
assert_eq!(
|
|
resolved.deferred_process_fields,
|
|
vec!["server.listeners".to_string()]
|
|
);
|
|
assert_eq!(resolved.effective.server.client_mss, old.server.client_mss);
|
|
assert_eq!(
|
|
resolved.effective.server.client_mss_bulk,
|
|
old.server.client_mss_bulk
|
|
);
|
|
assert!(!resolved.runtime_changed);
|
|
}
|
|
|
|
#[test]
|
|
fn mixed_reload_retains_process_state_and_applies_runtime_state() {
|
|
let old = ProxyConfig::default();
|
|
let mut desired = old.clone();
|
|
desired.server.client_mss = Some("92".to_string());
|
|
desired.censorship.tls_domain = "reload.example".to_string();
|
|
|
|
let resolved = resolve_reload_config(&old, &desired);
|
|
|
|
assert_eq!(resolved.effective.server.client_mss, old.server.client_mss);
|
|
assert_eq!(
|
|
resolved.effective.censorship.tls_domain,
|
|
desired.censorship.tls_domain
|
|
);
|
|
assert!(resolved.runtime_changed);
|
|
assert_eq!(
|
|
resolved.deferred_process_fields,
|
|
vec!["server.listeners".to_string()]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn listener_announcement_is_runtime_owned_when_bind_identity_is_stable() {
|
|
let mut old = ProxyConfig::default();
|
|
old.server.listeners.push(crate::config::ListenerConfig {
|
|
ip: "0.0.0.0".parse().unwrap(),
|
|
transport: crate::config::ListenerTransport::Mtproxy,
|
|
port: Some(443),
|
|
client_mss: None,
|
|
synlimit: crate::config::SynLimitMode::Off,
|
|
synlimit_seconds: 60,
|
|
synlimit_hitcount: 48,
|
|
synlimit_burst: 24,
|
|
synlimit_ios_seconds: 1,
|
|
synlimit_ios_hitcount: 12,
|
|
synlimit_ios_burst: 24,
|
|
synlimit_hashlimit_expire_ms: 60_000,
|
|
synlimit_hashlimit_size: 32_768,
|
|
announce: None,
|
|
announce_ip: None,
|
|
proxy_protocol: None,
|
|
reuse_allow: false,
|
|
web_client_ip_source: crate::config::WebClientIpSource::XForwardedFor,
|
|
web_trusted_proxy_cidrs: Vec::new(),
|
|
});
|
|
let mut desired = old.clone();
|
|
desired.server.listeners[0].announce = Some("proxy.example".to_string());
|
|
|
|
let resolved = resolve_reload_config(&old, &desired);
|
|
|
|
assert!(resolved.deferred_process_fields.is_empty());
|
|
assert_eq!(
|
|
resolved.effective.server.listeners[0].announce.as_deref(),
|
|
Some("proxy.example")
|
|
);
|
|
assert!(resolved.runtime_changed);
|
|
}
|
|
|
|
#[test]
|
|
fn process_field_labels_are_stable_ordered_and_unique() {
|
|
let old = ProxyConfig::default();
|
|
let mut desired = old.clone();
|
|
desired.server.listen_backlog = desired.server.listen_backlog.saturating_add(1);
|
|
desired.server.api.enabled = !desired.server.api.enabled;
|
|
desired.server.api.runtime_edge_events_capacity = desired
|
|
.server
|
|
.api
|
|
.runtime_edge_events_capacity
|
|
.saturating_add(1);
|
|
desired.general.disable_colors = !desired.general.disable_colors;
|
|
|
|
let resolved = resolve_reload_config(&old, &desired);
|
|
|
|
assert_eq!(
|
|
resolved.deferred_process_fields,
|
|
vec![
|
|
"server.listeners".to_string(),
|
|
"server.api.listen".to_string(),
|
|
"server.api.runtime_edge_events_capacity".to_string(),
|
|
"general.disable_colors".to_string(),
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn runtime_only_change_does_not_require_process_rebind() {
|
|
let old = ProxyConfig::default();
|
|
let mut new = old.clone();
|
|
new.censorship.tls_domain = "reload.example".to_string();
|
|
assert!(deferred_process_fields(&old, &new).is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn web_allocation_limits_are_deferred_until_restart() {
|
|
let mut old = ProxyConfig::default();
|
|
old.rebuild_runtime_user_auth().unwrap();
|
|
old.rebuild_runtime_web().unwrap();
|
|
let mut desired = old.clone();
|
|
desired.web.limits.max_sessions_global += 1;
|
|
|
|
let resolved = resolve_reload_config(&old, &desired);
|
|
|
|
assert_eq!(
|
|
resolved.deferred_process_fields,
|
|
vec!["web.limits".to_string()]
|
|
);
|
|
assert_eq!(
|
|
resolved.effective.web.limits.max_sessions_global,
|
|
old.web.limits.max_sessions_global
|
|
);
|
|
assert!(!resolved.runtime_changed);
|
|
}
|
|
|
|
#[test]
|
|
fn web_debug_prefix_dependent_on_new_capacity_is_deferred_with_limits() {
|
|
let mut old = ProxyConfig::default();
|
|
old.rebuild_runtime_user_auth().unwrap();
|
|
old.rebuild_runtime_web().unwrap();
|
|
let mut desired = old.clone();
|
|
desired.web.limits.max_body_bytes = 4 * 1024 * 1024;
|
|
desired.web.debug.body_prefix_bytes = 3 * 1024 * 1024;
|
|
|
|
let resolved = resolve_reload_config(&old, &desired);
|
|
|
|
assert_eq!(
|
|
resolved.deferred_process_fields,
|
|
vec!["web.limits".to_string(), "web.debug".to_string()]
|
|
);
|
|
assert_eq!(
|
|
resolved.effective.web.debug.body_prefix_bytes,
|
|
old.web.debug.body_prefix_bytes
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn strict_middle_proxy_requires_a_prepared_pool() {
|
|
assert!(strict_middle_proxy_unavailable(true, false, false));
|
|
assert!(!strict_middle_proxy_unavailable(true, false, true));
|
|
assert!(!strict_middle_proxy_unavailable(true, true, false));
|
|
assert!(!strict_middle_proxy_unavailable(false, false, false));
|
|
}
|
|
|
|
#[test]
|
|
fn endpoint_only_listener_move_is_runtime_rebindable() {
|
|
let mut old = ProxyConfig::default();
|
|
old.server.listeners = vec![test_listener(443)];
|
|
let mut desired = old.clone();
|
|
desired.server.listeners[0].port = Some(8443);
|
|
|
|
let resolved = resolve_reload_config(&old, &desired);
|
|
|
|
assert!(resolved.deferred_process_fields.is_empty());
|
|
assert_eq!(resolved.effective.server.listeners[0].port, Some(8443));
|
|
assert!(resolved.runtime_changed);
|
|
}
|
|
|
|
#[test]
|
|
fn synlimited_endpoint_move_remains_restart_only() {
|
|
let mut old = ProxyConfig::default();
|
|
old.server.listeners = vec![test_listener(443)];
|
|
old.server.listeners[0].synlimit = crate::config::SynLimitMode::Nftables;
|
|
let mut desired = old.clone();
|
|
desired.server.listeners[0].port = Some(8443);
|
|
|
|
let resolved = resolve_reload_config(&old, &desired);
|
|
|
|
assert_eq!(
|
|
resolved.deferred_process_fields,
|
|
vec!["server.listeners".to_string()]
|
|
);
|
|
assert_eq!(resolved.effective.server.listeners[0].port, Some(443));
|
|
assert!(!resolved.runtime_changed);
|
|
}
|
|
|
|
#[test]
|
|
fn deferred_listener_identity_cannot_create_an_effective_decoy_loop() {
|
|
let mut old = ProxyConfig::default();
|
|
old.server.listeners = vec![test_listener(18080)];
|
|
old.server.listeners[0].transport = crate::config::ListenerTransport::Web;
|
|
let mut desired = old.clone();
|
|
desired.server.listeners[0].port = Some(18081);
|
|
desired.server.listen_backlog = desired.server.listen_backlog.saturating_add(1);
|
|
desired.web.vhosts = vec![
|
|
serde_json::from_value(serde_json::json!({
|
|
"host": "proxy.example",
|
|
"public_addr": "203.0.113.10:443",
|
|
"decoy": {
|
|
"mode": "http_upstream",
|
|
"upstream": "http://127.0.0.1:18080"
|
|
},
|
|
"profiles": []
|
|
}))
|
|
.unwrap(),
|
|
];
|
|
|
|
assert!(desired.validate_web_decoy_listener_separation().is_ok());
|
|
let resolved = resolve_reload_config(&old, &desired);
|
|
assert!(
|
|
resolved
|
|
.effective
|
|
.validate_web_decoy_listener_separation()
|
|
.is_err()
|
|
);
|
|
}
|