mirror of
https://github.com/telemt/telemt.git
synced 2026-10-30 12:30:34 +03:00
Hardened listener reload + config persistence + SYN Limit startup safety
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -27,6 +27,8 @@ pub(super) fn preprocess_includes(
|
||||
base_dir: &Path,
|
||||
depth: u8,
|
||||
source_files: &mut BTreeSet<PathBuf>,
|
||||
source_contents: &mut BTreeMap<PathBuf, String>,
|
||||
source_overrides: &BTreeMap<PathBuf, String>,
|
||||
) -> Result<String> {
|
||||
if depth > 10 {
|
||||
return Err(ProxyError::Config("Include depth > 10".into()));
|
||||
@@ -39,15 +41,23 @@ pub(super) fn preprocess_includes(
|
||||
if let Some(rest) = rest.strip_prefix('=') {
|
||||
let path_str = rest.trim().trim_matches('"');
|
||||
let resolved = base_dir.join(path_str);
|
||||
source_files.insert(normalize_config_path(&resolved));
|
||||
let included = std::fs::read_to_string(&resolved)
|
||||
let normalized = normalize_config_path(&resolved);
|
||||
source_files.insert(normalized.clone());
|
||||
let included = source_overrides
|
||||
.get(&normalized)
|
||||
.cloned()
|
||||
.map(Ok)
|
||||
.unwrap_or_else(|| std::fs::read_to_string(&resolved))
|
||||
.map_err(|e| ProxyError::Config(e.to_string()))?;
|
||||
source_contents.insert(normalized, included.clone());
|
||||
let included_dir = resolved.parent().unwrap_or(base_dir);
|
||||
output.push_str(&preprocess_includes(
|
||||
&included,
|
||||
included_dir,
|
||||
depth + 1,
|
||||
source_files,
|
||||
source_contents,
|
||||
source_overrides,
|
||||
)?);
|
||||
output.push('\n');
|
||||
continue;
|
||||
|
||||
@@ -3,7 +3,9 @@ use tracing::warn;
|
||||
|
||||
use crate::error::{ProxyError, Result};
|
||||
|
||||
use super::super::types::{LoggingConfig, LoggingDestination, NetworkConfig, UpstreamType};
|
||||
use super::super::types::{
|
||||
LoggingConfig, LoggingDestination, NetworkConfig, SynLimitMode, UpstreamType,
|
||||
};
|
||||
use super::ProxyConfig;
|
||||
|
||||
pub(super) fn validate_network_cfg(net: &mut NetworkConfig) -> Result<()> {
|
||||
@@ -90,6 +92,77 @@ pub(super) fn validate_upstreams(config: &ProxyConfig) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn validate_listener_runtime_profiles(config: &ProxyConfig) -> Result<()> {
|
||||
for (index, listener) in config.server.listeners.iter().enumerate() {
|
||||
let supported = if cfg!(target_os = "linux") {
|
||||
matches!(
|
||||
listener.synlimit,
|
||||
SynLimitMode::Off | SynLimitMode::Iptables | SynLimitMode::Nftables
|
||||
)
|
||||
} else if cfg!(target_os = "freebsd") {
|
||||
matches!(listener.synlimit, SynLimitMode::Off | SynLimitMode::Pf)
|
||||
} else {
|
||||
listener.synlimit == SynLimitMode::Off
|
||||
};
|
||||
if !supported {
|
||||
let backend = match listener.synlimit {
|
||||
SynLimitMode::Off => "off",
|
||||
SynLimitMode::Iptables => "iptables",
|
||||
SynLimitMode::Nftables => "nftables",
|
||||
SynLimitMode::Pf => "pf",
|
||||
};
|
||||
let supported = if cfg!(target_os = "linux") {
|
||||
"off, iptables, nftables"
|
||||
} else if cfg!(target_os = "freebsd") {
|
||||
"off, pf"
|
||||
} else {
|
||||
"off"
|
||||
};
|
||||
return Err(ProxyError::Config(format!(
|
||||
"server.listeners[{index}].synlimit backend {backend} is unsupported on this platform; supported values: {supported}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
let Some(bulk_mss) = config
|
||||
.server
|
||||
.client_mss_bulk_value()
|
||||
.map_err(|error| ProxyError::Config(format!("server.client_mss_bulk {error}")))?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
if !cfg!(target_os = "linux") {
|
||||
return Err(ProxyError::Config(
|
||||
"server.client_mss_bulk is supported only on Linux".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mut participants = 0usize;
|
||||
for (index, listener) in config.server.listeners.iter().enumerate() {
|
||||
let handshake_mss = listener
|
||||
.effective_client_mss(&config.server)
|
||||
.map_err(|error| {
|
||||
ProxyError::Config(format!("server.listeners[{index}].client_mss {error}"))
|
||||
})?;
|
||||
let Some(handshake_mss) = handshake_mss else {
|
||||
continue;
|
||||
};
|
||||
participants = participants.saturating_add(1);
|
||||
if bulk_mss <= handshake_mss {
|
||||
return Err(ProxyError::Config(format!(
|
||||
"server.client_mss_bulk ({bulk_mss}) must be greater than the effective handshake MSS ({handshake_mss}) for server.listeners[{index}]"
|
||||
)));
|
||||
}
|
||||
}
|
||||
if participants == 0 {
|
||||
return Err(ProxyError::Config(
|
||||
"server.client_mss_bulk requires an effective client_mss on at least one listener"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn normalize_upstream_family_policy(config: &mut ProxyConfig) {
|
||||
for (idx, upstream) in config.upstreams.iter_mut().enumerate() {
|
||||
if matches!(upstream.ipv4, Some(false)) && upstream.prefer == Some(4) {
|
||||
|
||||
Reference in New Issue
Block a user