mirror of https://github.com/telemt/telemt.git
TLS Fetcher Upstream Selection
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
parent
dd07fa9453
commit
5c0eb6dbe8
|
|
@ -65,6 +65,10 @@ pub(crate) fn default_tls_domain() -> String {
|
||||||
"petrovich.ru".to_string()
|
"petrovich.ru".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn default_tls_fetch_scope() -> String {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn default_mask_port() -> u16 {
|
pub(crate) fn default_mask_port() -> u16 {
|
||||||
443
|
443
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -623,6 +623,7 @@ fn warn_non_hot_changes(old: &ProxyConfig, new: &ProxyConfig, non_hot_changed: b
|
||||||
}
|
}
|
||||||
if old.censorship.tls_domain != new.censorship.tls_domain
|
if old.censorship.tls_domain != new.censorship.tls_domain
|
||||||
|| 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.mask != new.censorship.mask
|
|| old.censorship.mask != new.censorship.mask
|
||||||
|| 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
|
||||||
|
|
|
||||||
|
|
@ -779,6 +779,9 @@ impl ProxyConfig {
|
||||||
config.censorship.mask_host = Some(config.censorship.tls_domain.clone());
|
config.censorship.mask_host = Some(config.censorship.tls_domain.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Normalize optional TLS fetch scope: whitespace-only values disable scoped routing.
|
||||||
|
config.censorship.tls_fetch_scope = config.censorship.tls_fetch_scope.trim().to_string();
|
||||||
|
|
||||||
// Merge primary + extra TLS domains, deduplicate (primary always first).
|
// Merge primary + extra TLS domains, deduplicate (primary always first).
|
||||||
if !config.censorship.tls_domains.is_empty() {
|
if !config.censorship.tls_domains.is_empty() {
|
||||||
let mut all = Vec::with_capacity(1 + config.censorship.tls_domains.len());
|
let mut all = Vec::with_capacity(1 + config.censorship.tls_domains.len());
|
||||||
|
|
@ -2097,6 +2100,59 @@ mod tests {
|
||||||
let _ = std::fs::remove_file(path);
|
let _ = std::fs::remove_file(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tls_fetch_scope_default_is_empty() {
|
||||||
|
let toml = r#"
|
||||||
|
[censorship]
|
||||||
|
tls_domain = "example.com"
|
||||||
|
|
||||||
|
[access.users]
|
||||||
|
user = "00000000000000000000000000000000"
|
||||||
|
"#;
|
||||||
|
let dir = std::env::temp_dir();
|
||||||
|
let path = dir.join("telemt_tls_fetch_scope_default_test.toml");
|
||||||
|
std::fs::write(&path, toml).unwrap();
|
||||||
|
let cfg = ProxyConfig::load(&path).unwrap();
|
||||||
|
assert!(cfg.censorship.tls_fetch_scope.is_empty());
|
||||||
|
let _ = std::fs::remove_file(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tls_fetch_scope_is_trimmed_during_load() {
|
||||||
|
let toml = r#"
|
||||||
|
[censorship]
|
||||||
|
tls_domain = "example.com"
|
||||||
|
tls_fetch_scope = " me "
|
||||||
|
|
||||||
|
[access.users]
|
||||||
|
user = "00000000000000000000000000000000"
|
||||||
|
"#;
|
||||||
|
let dir = std::env::temp_dir();
|
||||||
|
let path = dir.join("telemt_tls_fetch_scope_trim_test.toml");
|
||||||
|
std::fs::write(&path, toml).unwrap();
|
||||||
|
let cfg = ProxyConfig::load(&path).unwrap();
|
||||||
|
assert_eq!(cfg.censorship.tls_fetch_scope, "me");
|
||||||
|
let _ = std::fs::remove_file(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tls_fetch_scope_whitespace_becomes_empty() {
|
||||||
|
let toml = r#"
|
||||||
|
[censorship]
|
||||||
|
tls_domain = "example.com"
|
||||||
|
tls_fetch_scope = " "
|
||||||
|
|
||||||
|
[access.users]
|
||||||
|
user = "00000000000000000000000000000000"
|
||||||
|
"#;
|
||||||
|
let dir = std::env::temp_dir();
|
||||||
|
let path = dir.join("telemt_tls_fetch_scope_blank_test.toml");
|
||||||
|
std::fs::write(&path, toml).unwrap();
|
||||||
|
let cfg = ProxyConfig::load(&path).unwrap();
|
||||||
|
assert!(cfg.censorship.tls_fetch_scope.is_empty());
|
||||||
|
let _ = std::fs::remove_file(path);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_ad_tag_is_disabled_during_load() {
|
fn invalid_ad_tag_is_disabled_during_load() {
|
||||||
let toml = r#"
|
let toml = r#"
|
||||||
|
|
|
||||||
|
|
@ -1308,6 +1308,11 @@ pub struct AntiCensorshipConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub tls_domains: Vec<String>,
|
pub tls_domains: Vec<String>,
|
||||||
|
|
||||||
|
/// Upstream scope used for TLS front metadata fetches.
|
||||||
|
/// Empty value keeps default upstream routing behavior.
|
||||||
|
#[serde(default = "default_tls_fetch_scope")]
|
||||||
|
pub tls_fetch_scope: String,
|
||||||
|
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub mask: bool,
|
pub mask: bool,
|
||||||
|
|
||||||
|
|
@ -1365,6 +1370,7 @@ impl Default for AntiCensorshipConfig {
|
||||||
Self {
|
Self {
|
||||||
tls_domain: default_tls_domain(),
|
tls_domain: default_tls_domain(),
|
||||||
tls_domains: Vec::new(),
|
tls_domains: Vec::new(),
|
||||||
|
tls_fetch_scope: default_tls_fetch_scope(),
|
||||||
mask: default_true(),
|
mask: default_true(),
|
||||||
mask_host: None,
|
mask_host: None,
|
||||||
mask_port: default_mask_port(),
|
mask_port: default_mask_port(),
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,15 @@ pub(crate) async fn bootstrap_tls_front(
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| config.censorship.tls_domain.clone());
|
.unwrap_or_else(|| config.censorship.tls_domain.clone());
|
||||||
let mask_unix_sock = config.censorship.mask_unix_sock.clone();
|
let mask_unix_sock = config.censorship.mask_unix_sock.clone();
|
||||||
|
let tls_fetch_scope = (!config.censorship.tls_fetch_scope.is_empty())
|
||||||
|
.then(|| config.censorship.tls_fetch_scope.clone());
|
||||||
let fetch_timeout = Duration::from_secs(5);
|
let fetch_timeout = Duration::from_secs(5);
|
||||||
|
|
||||||
let cache_initial = cache.clone();
|
let cache_initial = cache.clone();
|
||||||
let domains_initial = tls_domains.to_vec();
|
let domains_initial = tls_domains.to_vec();
|
||||||
let host_initial = mask_host.clone();
|
let host_initial = mask_host.clone();
|
||||||
let unix_sock_initial = mask_unix_sock.clone();
|
let unix_sock_initial = mask_unix_sock.clone();
|
||||||
|
let scope_initial = tls_fetch_scope.clone();
|
||||||
let upstream_initial = upstream_manager.clone();
|
let upstream_initial = upstream_manager.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut join = tokio::task::JoinSet::new();
|
let mut join = tokio::task::JoinSet::new();
|
||||||
|
|
@ -51,6 +54,7 @@ pub(crate) async fn bootstrap_tls_front(
|
||||||
let cache_domain = cache_initial.clone();
|
let cache_domain = cache_initial.clone();
|
||||||
let host_domain = host_initial.clone();
|
let host_domain = host_initial.clone();
|
||||||
let unix_sock_domain = unix_sock_initial.clone();
|
let unix_sock_domain = unix_sock_initial.clone();
|
||||||
|
let scope_domain = scope_initial.clone();
|
||||||
let upstream_domain = upstream_initial.clone();
|
let upstream_domain = upstream_initial.clone();
|
||||||
join.spawn(async move {
|
join.spawn(async move {
|
||||||
match crate::tls_front::fetcher::fetch_real_tls(
|
match crate::tls_front::fetcher::fetch_real_tls(
|
||||||
|
|
@ -59,6 +63,7 @@ pub(crate) async fn bootstrap_tls_front(
|
||||||
&domain,
|
&domain,
|
||||||
fetch_timeout,
|
fetch_timeout,
|
||||||
Some(upstream_domain),
|
Some(upstream_domain),
|
||||||
|
scope_domain.as_deref(),
|
||||||
proxy_protocol,
|
proxy_protocol,
|
||||||
unix_sock_domain.as_deref(),
|
unix_sock_domain.as_deref(),
|
||||||
)
|
)
|
||||||
|
|
@ -100,6 +105,7 @@ pub(crate) async fn bootstrap_tls_front(
|
||||||
let domains_refresh = tls_domains.to_vec();
|
let domains_refresh = tls_domains.to_vec();
|
||||||
let host_refresh = mask_host.clone();
|
let host_refresh = mask_host.clone();
|
||||||
let unix_sock_refresh = mask_unix_sock.clone();
|
let unix_sock_refresh = mask_unix_sock.clone();
|
||||||
|
let scope_refresh = tls_fetch_scope.clone();
|
||||||
let upstream_refresh = upstream_manager.clone();
|
let upstream_refresh = upstream_manager.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
|
|
@ -112,6 +118,7 @@ pub(crate) async fn bootstrap_tls_front(
|
||||||
let cache_domain = cache_refresh.clone();
|
let cache_domain = cache_refresh.clone();
|
||||||
let host_domain = host_refresh.clone();
|
let host_domain = host_refresh.clone();
|
||||||
let unix_sock_domain = unix_sock_refresh.clone();
|
let unix_sock_domain = unix_sock_refresh.clone();
|
||||||
|
let scope_domain = scope_refresh.clone();
|
||||||
let upstream_domain = upstream_refresh.clone();
|
let upstream_domain = upstream_refresh.clone();
|
||||||
join.spawn(async move {
|
join.spawn(async move {
|
||||||
match crate::tls_front::fetcher::fetch_real_tls(
|
match crate::tls_front::fetcher::fetch_real_tls(
|
||||||
|
|
@ -120,6 +127,7 @@ pub(crate) async fn bootstrap_tls_front(
|
||||||
&domain,
|
&domain,
|
||||||
fetch_timeout,
|
fetch_timeout,
|
||||||
Some(upstream_domain),
|
Some(upstream_domain),
|
||||||
|
scope_domain.as_deref(),
|
||||||
proxy_protocol,
|
proxy_protocol,
|
||||||
unix_sock_domain.as_deref(),
|
unix_sock_domain.as_deref(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -394,15 +394,17 @@ async fn connect_tcp_with_upstream(
|
||||||
port: u16,
|
port: u16,
|
||||||
connect_timeout: Duration,
|
connect_timeout: Duration,
|
||||||
upstream: Option<std::sync::Arc<crate::transport::UpstreamManager>>,
|
upstream: Option<std::sync::Arc<crate::transport::UpstreamManager>>,
|
||||||
|
scope: Option<&str>,
|
||||||
) -> Result<TcpStream> {
|
) -> Result<TcpStream> {
|
||||||
if let Some(manager) = upstream {
|
if let Some(manager) = upstream {
|
||||||
if let Some(addr) = resolve_socket_addr(host, port) {
|
if let Some(addr) = resolve_socket_addr(host, port) {
|
||||||
match manager.connect(addr, None, None).await {
|
match manager.connect(addr, None, scope).await {
|
||||||
Ok(stream) => return Ok(stream),
|
Ok(stream) => return Ok(stream),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!(
|
warn!(
|
||||||
host = %host,
|
host = %host,
|
||||||
port = port,
|
port = port,
|
||||||
|
scope = ?scope,
|
||||||
error = %e,
|
error = %e,
|
||||||
"Upstream connect failed, using direct connect"
|
"Upstream connect failed, using direct connect"
|
||||||
);
|
);
|
||||||
|
|
@ -410,12 +412,13 @@ async fn connect_tcp_with_upstream(
|
||||||
}
|
}
|
||||||
} else if let Ok(mut addrs) = tokio::net::lookup_host((host, port)).await {
|
} else if let Ok(mut addrs) = tokio::net::lookup_host((host, port)).await {
|
||||||
if let Some(addr) = addrs.find(|a| a.is_ipv4()) {
|
if let Some(addr) = addrs.find(|a| a.is_ipv4()) {
|
||||||
match manager.connect(addr, None, None).await {
|
match manager.connect(addr, None, scope).await {
|
||||||
Ok(stream) => return Ok(stream),
|
Ok(stream) => return Ok(stream),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!(
|
warn!(
|
||||||
host = %host,
|
host = %host,
|
||||||
port = port,
|
port = port,
|
||||||
|
scope = ?scope,
|
||||||
error = %e,
|
error = %e,
|
||||||
"Upstream connect failed, using direct connect"
|
"Upstream connect failed, using direct connect"
|
||||||
);
|
);
|
||||||
|
|
@ -537,6 +540,7 @@ async fn fetch_via_raw_tls(
|
||||||
sni: &str,
|
sni: &str,
|
||||||
connect_timeout: Duration,
|
connect_timeout: Duration,
|
||||||
upstream: Option<std::sync::Arc<crate::transport::UpstreamManager>>,
|
upstream: Option<std::sync::Arc<crate::transport::UpstreamManager>>,
|
||||||
|
scope: Option<&str>,
|
||||||
proxy_protocol: u8,
|
proxy_protocol: u8,
|
||||||
unix_sock: Option<&str>,
|
unix_sock: Option<&str>,
|
||||||
) -> Result<TlsFetchResult> {
|
) -> Result<TlsFetchResult> {
|
||||||
|
|
@ -572,7 +576,7 @@ async fn fetch_via_raw_tls(
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
let _ = unix_sock;
|
let _ = unix_sock;
|
||||||
|
|
||||||
let stream = connect_tcp_with_upstream(host, port, connect_timeout, upstream).await?;
|
let stream = connect_tcp_with_upstream(host, port, connect_timeout, upstream, scope).await?;
|
||||||
fetch_via_raw_tls_stream(stream, sni, connect_timeout, proxy_protocol).await
|
fetch_via_raw_tls_stream(stream, sni, connect_timeout, proxy_protocol).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -675,6 +679,7 @@ async fn fetch_via_rustls(
|
||||||
sni: &str,
|
sni: &str,
|
||||||
connect_timeout: Duration,
|
connect_timeout: Duration,
|
||||||
upstream: Option<std::sync::Arc<crate::transport::UpstreamManager>>,
|
upstream: Option<std::sync::Arc<crate::transport::UpstreamManager>>,
|
||||||
|
scope: Option<&str>,
|
||||||
proxy_protocol: u8,
|
proxy_protocol: u8,
|
||||||
unix_sock: Option<&str>,
|
unix_sock: Option<&str>,
|
||||||
) -> Result<TlsFetchResult> {
|
) -> Result<TlsFetchResult> {
|
||||||
|
|
@ -710,7 +715,7 @@ async fn fetch_via_rustls(
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
let _ = unix_sock;
|
let _ = unix_sock;
|
||||||
|
|
||||||
let stream = connect_tcp_with_upstream(host, port, connect_timeout, upstream).await?;
|
let stream = connect_tcp_with_upstream(host, port, connect_timeout, upstream, scope).await?;
|
||||||
fetch_via_rustls_stream(stream, host, sni, proxy_protocol).await
|
fetch_via_rustls_stream(stream, host, sni, proxy_protocol).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -726,6 +731,7 @@ pub async fn fetch_real_tls(
|
||||||
sni: &str,
|
sni: &str,
|
||||||
connect_timeout: Duration,
|
connect_timeout: Duration,
|
||||||
upstream: Option<std::sync::Arc<crate::transport::UpstreamManager>>,
|
upstream: Option<std::sync::Arc<crate::transport::UpstreamManager>>,
|
||||||
|
scope: Option<&str>,
|
||||||
proxy_protocol: u8,
|
proxy_protocol: u8,
|
||||||
unix_sock: Option<&str>,
|
unix_sock: Option<&str>,
|
||||||
) -> Result<TlsFetchResult> {
|
) -> Result<TlsFetchResult> {
|
||||||
|
|
@ -735,6 +741,7 @@ pub async fn fetch_real_tls(
|
||||||
sni,
|
sni,
|
||||||
connect_timeout,
|
connect_timeout,
|
||||||
upstream.clone(),
|
upstream.clone(),
|
||||||
|
scope,
|
||||||
proxy_protocol,
|
proxy_protocol,
|
||||||
unix_sock,
|
unix_sock,
|
||||||
)
|
)
|
||||||
|
|
@ -753,6 +760,7 @@ pub async fn fetch_real_tls(
|
||||||
sni,
|
sni,
|
||||||
connect_timeout,
|
connect_timeout,
|
||||||
upstream,
|
upstream,
|
||||||
|
scope,
|
||||||
proxy_protocol,
|
proxy_protocol,
|
||||||
unix_sock,
|
unix_sock,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue