mirror of
https://github.com/telemt/telemt.git
synced 2026-06-18 17:08:29 +03:00
Add dynamic SNI mask target mode
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -620,6 +620,7 @@ fn warn_non_hot_changes(old: &ProxyConfig, new: &ProxyConfig, non_hot_changed: b
|
|||||||
|| old.censorship.tls_domains != new.censorship.tls_domains
|
|| old.censorship.tls_domains != new.censorship.tls_domains
|
||||||
|| old.censorship.tls_fetch_scope != new.censorship.tls_fetch_scope
|
|| old.censorship.tls_fetch_scope != new.censorship.tls_fetch_scope
|
||||||
|| old.censorship.mask != new.censorship.mask
|
|| old.censorship.mask != new.censorship.mask
|
||||||
|
|| old.censorship.mask_dynamic != new.censorship.mask_dynamic
|
||||||
|| old.censorship.mask_host != new.censorship.mask_host
|
|| old.censorship.mask_host != new.censorship.mask_host
|
||||||
|| old.censorship.mask_port != new.censorship.mask_port
|
|| old.censorship.mask_port != new.censorship.mask_port
|
||||||
|| old.censorship.exclusive_mask != new.censorship.exclusive_mask
|
|| old.censorship.exclusive_mask != new.censorship.exclusive_mask
|
||||||
|
|||||||
@@ -372,6 +372,7 @@ const CENSORSHIP_CONFIG_KEYS: &[&str] = &[
|
|||||||
"tls_fetch_scope",
|
"tls_fetch_scope",
|
||||||
"tls_fetch",
|
"tls_fetch",
|
||||||
"mask",
|
"mask",
|
||||||
|
"mask_dynamic",
|
||||||
"mask_host",
|
"mask_host",
|
||||||
"mask_port",
|
"mask_port",
|
||||||
"exclusive_mask",
|
"exclusive_mask",
|
||||||
@@ -2047,11 +2048,6 @@ impl ProxyConfig {
|
|||||||
*mask_host = normalize_mask_host_to_ascii(mask_host, "censorship.mask_host")?;
|
*mask_host = normalize_mask_host_to_ascii(mask_host, "censorship.mask_host")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default mask_host to tls_domain if not set and no unix socket configured.
|
|
||||||
if config.censorship.mask_host.is_none() && config.censorship.mask_unix_sock.is_none() {
|
|
||||||
config.censorship.mask_host = Some(config.censorship.tls_domain.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (domain, target) in &config.censorship.exclusive_mask {
|
for (domain, target) in &config.censorship.exclusive_mask {
|
||||||
if !is_valid_tls_domain_name(domain) {
|
if !is_valid_tls_domain_name(domain) {
|
||||||
return Err(ProxyError::Config(format!(
|
return Err(ProxyError::Config(format!(
|
||||||
|
|||||||
@@ -1726,6 +1726,10 @@ pub struct AntiCensorshipConfig {
|
|||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub mask: bool,
|
pub mask: bool,
|
||||||
|
|
||||||
|
/// Use the ClientHello SNI as the mask TCP target for configured TLS domains.
|
||||||
|
#[serde(default = "default_true")]
|
||||||
|
pub mask_dynamic: bool,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub mask_host: Option<String>,
|
pub mask_host: Option<String>,
|
||||||
|
|
||||||
@@ -1861,6 +1865,7 @@ impl Default for AntiCensorshipConfig {
|
|||||||
tls_fetch_scope: default_tls_fetch_scope(),
|
tls_fetch_scope: default_tls_fetch_scope(),
|
||||||
tls_fetch: TlsFetchConfig::default(),
|
tls_fetch: TlsFetchConfig::default(),
|
||||||
mask: default_true(),
|
mask: default_true(),
|
||||||
|
mask_dynamic: default_true(),
|
||||||
mask_host: None,
|
mask_host: None,
|
||||||
mask_port: default_mask_port(),
|
mask_port: default_mask_port(),
|
||||||
exclusive_mask: HashMap::new(),
|
exclusive_mask: HashMap::new(),
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ mod tls_domain_mask_host_tests {
|
|||||||
let mut config = ProxyConfig::default();
|
let mut config = ProxyConfig::default();
|
||||||
config.censorship.tls_domain = "a.com".to_string();
|
config.censorship.tls_domain = "a.com".to_string();
|
||||||
config.censorship.tls_domains = vec!["b.com".to_string(), "c.com".to_string()];
|
config.censorship.tls_domains = vec!["b.com".to_string(), "c.com".to_string()];
|
||||||
config.censorship.mask_host = Some("a.com".to_string());
|
config.censorship.mask_host = None;
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -419,6 +419,15 @@ mod tls_domain_mask_host_tests {
|
|||||||
assert_eq!(mask_host_for_initial_data(&config, &initial_data), "b.com");
|
assert_eq!(mask_host_for_initial_data(&config, &initial_data), "b.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mask_host_uses_primary_domain_when_dynamic_masking_is_disabled() {
|
||||||
|
let mut config = config_with_tls_domains();
|
||||||
|
config.censorship.mask_dynamic = false;
|
||||||
|
let initial_data = client_hello_with_sni("b.com");
|
||||||
|
|
||||||
|
assert_eq!(mask_host_for_initial_data(&config, &initial_data), "a.com");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exclusive_mask_target_overrides_only_matching_sni() {
|
fn exclusive_mask_target_overrides_only_matching_sni() {
|
||||||
let mut config = config_with_tls_domains();
|
let mut config = config_with_tls_domains();
|
||||||
@@ -577,24 +586,32 @@ fn default_mask_tcp_target_for_initial_data<'a>(
|
|||||||
.as_deref()
|
.as_deref()
|
||||||
.unwrap_or(&config.censorship.tls_domain);
|
.unwrap_or(&config.censorship.tls_domain);
|
||||||
|
|
||||||
if !configured_mask_host.eq_ignore_ascii_case(&config.censorship.tls_domain) {
|
if config.censorship.mask_host.is_none() && config.censorship.mask_dynamic {
|
||||||
|
let extracted_sni = if sni.is_none() {
|
||||||
|
tls::extract_sni_from_client_hello(initial_data)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if let Some(host) = sni
|
||||||
|
.or(extracted_sni.as_deref())
|
||||||
|
.and_then(|sni| matching_tls_domain_for_sni(config, sni))
|
||||||
|
{
|
||||||
|
return MaskTcpTarget {
|
||||||
|
host,
|
||||||
|
port: config.censorship.mask_port,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mask_host) = config.censorship.mask_host.as_deref() {
|
||||||
return MaskTcpTarget {
|
return MaskTcpTarget {
|
||||||
host: configured_mask_host,
|
host: mask_host,
|
||||||
port: config.censorship.mask_port,
|
port: config.censorship.mask_port,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let extracted_sni = if sni.is_none() {
|
|
||||||
tls::extract_sni_from_client_hello(initial_data)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let host = sni
|
|
||||||
.or(extracted_sni.as_deref())
|
|
||||||
.and_then(|sni| matching_tls_domain_for_sni(config, sni))
|
|
||||||
.unwrap_or(configured_mask_host);
|
|
||||||
MaskTcpTarget {
|
MaskTcpTarget {
|
||||||
host,
|
host: configured_mask_host,
|
||||||
port: config.censorship.mask_port,
|
port: config.censorship.mask_port,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user