55 lines
1.8 KiB
Rust
55 lines
1.8 KiB
Rust
use std::net::IpAddr;
|
|
|
|
use rocket::http::Status;
|
|
use rocket::request::{FromRequest, Outcome, Request};
|
|
|
|
#[derive(Debug)]
|
|
pub struct ClientIP(pub String);
|
|
|
|
#[derive(Debug)]
|
|
pub struct TrustedProxies(pub Vec<IpAddr>);
|
|
|
|
#[rocket::async_trait]
|
|
impl<'r> FromRequest<'r> for ClientIP {
|
|
type Error = ();
|
|
|
|
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
|
// fetch configured trusted proxies list (if any)
|
|
let trusted_proxies = &req.rocket().state::<TrustedProxies>().unwrap().0;
|
|
|
|
// fetch the actual remote IP address
|
|
let remote_ip = match req.remote() {
|
|
Some(socket_addr) => socket_addr.ip(),
|
|
None => {
|
|
return Outcome::Failure((Status::InternalServerError, ()));
|
|
}
|
|
};
|
|
|
|
// if the request does not come from one trusted proxy, don't trust any HTTP header
|
|
if !trusted_proxies.contains(&remote_ip) {
|
|
return Outcome::Success(ClientIP(remote_ip.to_string()));
|
|
}
|
|
|
|
// fetch valid IP addresses from X-FORWARDED-FOR header(s)
|
|
let xff_ip_addresses: Vec<IpAddr> = req
|
|
.headers()
|
|
.get("x-forwarded-for")
|
|
.flat_map(|h| h.split(", "))
|
|
.flat_map(str::parse)
|
|
.collect::<Vec<_>>();
|
|
|
|
// fetch Rocket's "client IP", which first inspects "X-REAL-IP" header
|
|
let client_ip = req.client_ip().unwrap();
|
|
|
|
// find first (last) X-FORWARDED-FOR address that does not match one of the trusted proxies
|
|
Outcome::Success(ClientIP(
|
|
xff_ip_addresses
|
|
.iter()
|
|
.rev()
|
|
.find(|xff_ip_address| !trusted_proxies.contains(xff_ip_address))
|
|
.unwrap_or_else(|| xff_ip_addresses.first().unwrap_or(&client_ip))
|
|
.to_string(),
|
|
))
|
|
}
|
|
}
|