refactor: unify client IP extraction into extract_real_ip

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<IpAddr>
and passes it to extract_real_ip, ensuring consistent IP resolution
across rate limiting and address derivation.
This commit is contained in:
2026-08-19 03:13:01 -04:00
parent c957dfff6b
commit 6690343aad

View File

@@ -227,8 +227,19 @@ async fn echo_version() -> impl Responder {
HttpResponse::Ok().body(VERSION) HttpResponse::Ok().body(VERSION)
} }
fn is_valid_ip(ip: &str) -> bool { fn extract_real_ip(req: &actix_web::HttpRequest, trusted_proxy: IpAddr) -> String {
ip.parse::<std::net::IpAddr>().is_ok() 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)] #[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( async fn echo_info(
path: web::Path<String>, path: web::Path<String>,
data: web::Data<AppState>, data: web::Data<AppState>,
proxy: web::Data<IpAddr>,
req: actix_web::HttpRequest, req: actix_web::HttpRequest,
) -> impl Responder { ) -> impl Responder {
let param = path.into_inner(); let param = path.into_inner();
@@ -319,7 +303,7 @@ async fn echo_info(
debug!("network disabled {}", param); debug!("network disabled {}", param);
return HttpResponse::BadRequest().body("error"); 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 { let address = match netconfig.xpub {
false => { false => {
let address = netconfig.address.to_string(); let address = netconfig.address.to_string();