mirror of
https://github.com/telemt/telemt.git
synced 2026-05-01 01:14:11 +03:00
This commit introduces a comprehensive set of improvements to enhance the security, reliability, and configurability of the proxy server, specifically targeting adversarial resilience and high-load concurrency. Security & Cryptography: - Zeroize MTProto cryptographic key material (`dec_key`, `enc_key`) immediately after use to prevent memory leakage on early returns. - Move TLS handshake replay tracking after full policy/ALPN validation to prevent cache poisoning by unauthenticated probes. - Add `proxy_protocol_trusted_cidrs` configuration to restrict PROXY protocol headers to trusted networks, rejecting spoofed IPs. Adversarial Resilience & DoS Mitigation: - Implement "Tiny Frame Debt" tracking in the middle-relay to prevent CPU exhaustion from malicious 0-byte or 1-byte frame floods. - Add `mask_relay_max_bytes` to strictly bound unauthenticated fallback connections, preventing the proxy from being abused as an open relay. - Add a 5ms prefetch window (`mask_classifier_prefetch_timeout_ms`) to correctly assemble and classify fragmented HTTP/1.1 and HTTP/2 probes (e.g., `PRI * HTTP/2.0`) before routing them to masking heuristics. - Prevent recursive masking loops (FD exhaustion) by verifying the mask target is not the proxy's own listener via local interface enumeration. Concurrency & Reliability: - Eliminate executor waker storms during quota lock contention by replacing the spin-waker task with inline `Sleep` and exponential backoff. - Roll back user quota reservations (`rollback_me2c_quota_reservation`) if a network write fails, preventing Head-of-Line (HoL) blocking from permanently burning data quotas. - Recover gracefully from idle-registry `Mutex` poisoning instead of panicking, ensuring isolated thread failures do not break the proxy. - Fix `auth_probe_scan_start_offset` modulo logic to ensure bounds safety. Testing: - Add extensive adversarial, timing, fuzzing, and invariant test suites for both the client and handshake modules.
93 lines
2.3 KiB
Rust
93 lines
2.3 KiB
Rust
use super::*;
|
|
|
|
#[test]
|
|
fn full_http2_preface_classified_as_http_probe() {
|
|
let preface = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
|
assert!(
|
|
is_http_probe(preface),
|
|
"HTTP/2 connection preface must be classified as HTTP probe"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn partial_http2_preface_3_bytes_classified() {
|
|
assert!(
|
|
is_http_probe(b"PRI"),
|
|
"3-byte HTTP/2 preface prefix must be classified"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn partial_http2_preface_2_bytes_classified() {
|
|
assert!(
|
|
is_http_probe(b"PR"),
|
|
"2-byte HTTP/2 preface prefix must be classified"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn existing_http1_methods_unaffected() {
|
|
for prefix in [
|
|
b"GET / HTTP/1.1\r\n".as_ref(),
|
|
b"POST /api HTTP/1.1\r\n".as_ref(),
|
|
b"CONNECT example.com:443 HTTP/1.1\r\n".as_ref(),
|
|
b"TRACE / HTTP/1.1\r\n".as_ref(),
|
|
b"PATCH / HTTP/1.1\r\n".as_ref(),
|
|
] {
|
|
assert!(is_http_probe(prefix));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn non_http_data_not_classified() {
|
|
for data in [
|
|
b"\x16\x03\x01\x00\xf1".as_ref(),
|
|
b"SSH-2.0-OpenSSH_8.9\r\n".as_ref(),
|
|
b"\x00\x01\x02\x03".as_ref(),
|
|
b"".as_ref(),
|
|
b"P".as_ref(),
|
|
] {
|
|
assert!(!is_http_probe(data));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn light_fuzz_non_http_prefixes_not_misclassified() {
|
|
// Deterministic pseudo-fuzz to exercise classifier edges while avoiding
|
|
// known HTTP method and partial windows.
|
|
let mut x = 0x1234_5678u32;
|
|
for _ in 0..1024 {
|
|
x = x.wrapping_mul(1664525).wrapping_add(1013904223);
|
|
let len = 4 + ((x >> 8) as usize % 12);
|
|
let mut data = vec![0u8; len];
|
|
for byte in &mut data {
|
|
x = x.wrapping_mul(1664525).wrapping_add(1013904223);
|
|
*byte = (x & 0xFF) as u8;
|
|
}
|
|
|
|
if [
|
|
b"GET ".as_ref(),
|
|
b"POST".as_ref(),
|
|
b"HEAD".as_ref(),
|
|
b"PUT ".as_ref(),
|
|
b"DELETE".as_ref(),
|
|
b"OPTIONS".as_ref(),
|
|
b"CONNECT".as_ref(),
|
|
b"TRACE".as_ref(),
|
|
b"PATCH".as_ref(),
|
|
b"PRI ".as_ref(),
|
|
]
|
|
.iter()
|
|
.any(|m| data.starts_with(m))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
assert!(
|
|
!is_http_probe(&data),
|
|
"non-http pseudo-fuzz input misclassified: {:?}",
|
|
&data[..data.len().min(8)]
|
|
);
|
|
}
|
|
}
|