From 6690343aadc46d864f177776dda9a746500c4ed8 Mon Sep 17 00:00:00 2001 From: svatantrya Date: Wed, 19 Aug 2026 03:13:01 -0400 Subject: [PATCH] refactor: unify client IP extraction into extract_real_ip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace extract_client_ip (manual header parsing without proxy verification) and is_valid_ip with a single extract_real_ip function that uses the same proxy-checking logic as RealIpKeyExtractor. - If peer IP == trusted proxy: use realip_remote_addr() (Forwarded/ X-Forwarded-For) — safe because peer was verified - Otherwise: use peer_addr() directly (local/direct connection) echo_info now extracts the trusted proxy from web::Data and passes it to extract_real_ip, ensuring consistent IP resolution across rate limiting and address derivation. --- src/bin/bal-server.rs | 46 ++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/src/bin/bal-server.rs b/src/bin/bal-server.rs index c8bac58..5884193 100644 --- a/src/bin/bal-server.rs +++ b/src/bin/bal-server.rs @@ -227,8 +227,19 @@ async fn echo_version() -> impl Responder { HttpResponse::Ok().body(VERSION) } -fn is_valid_ip(ip: &str) -> bool { - ip.parse::().is_ok() +fn extract_real_ip(req: &actix_web::HttpRequest, trusted_proxy: IpAddr) -> String { + let peer_ip = req.peer_addr().map(|socket| socket.ip()); + let connection_info = req.connection_info(); + + let ip = match peer_ip { + Some(peer) if peer == trusted_proxy => { + connection_info.realip_remote_addr().unwrap_or("unknown") + } + _ => connection_info.peer_addr().unwrap_or("unknown"), + }; + + debug!("client IP: {}", ip); + ip.to_string() } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -276,37 +287,10 @@ impl KeyExtractor for RealIpKeyExtractor { } } -fn extract_client_ip(req: &actix_web::HttpRequest) -> String { - if let Some(val) = req.headers().get("X-Real-IP") - && let Ok(s) = val.to_str() - { - let ip = s.split(',').next().unwrap_or(s).trim(); - if is_valid_ip(ip) { - debug!("client IP from X-Real-IP: {}", ip); - return ip.to_string(); - } - } - if let Some(val) = req.headers().get("X-Forwarded-For") - && let Ok(s) = val.to_str() - { - let ip = s.split(',').next().unwrap_or(s).trim(); - if is_valid_ip(ip) { - debug!("client IP from X-Forwarded-For: {}", ip); - return ip.to_string(); - } - } - let fallback = req - .connection_info() - .peer_addr() - .unwrap_or("unknown") - .to_string(); - debug!("client IP from peer_addr fallback: {}", fallback); - fallback -} - async fn echo_info( path: web::Path, data: web::Data, + proxy: web::Data, req: actix_web::HttpRequest, ) -> impl Responder { let param = path.into_inner(); @@ -319,7 +303,7 @@ async fn echo_info( debug!("network disabled {}", param); return HttpResponse::BadRequest().body("error"); } - let remote_addr = extract_client_ip(&req); + let remote_addr = extract_real_ip(&req, **proxy); let address = match netconfig.xpub { false => { let address = netconfig.address.to_string();