support nested proxies

This commit is contained in:
Robin Appelman 2020-12-03 19:30:20 +01:00
commit a04638e1f6
2 changed files with 51 additions and 14 deletions

View file

@ -1,5 +1,7 @@
use std::convert::Infallible; use std::convert::Infallible;
use std::iter::once;
use std::net::{IpAddr, SocketAddr}; use std::net::{IpAddr, SocketAddr};
use std::str::FromStr;
use warp::filters::addr::remote; use warp::filters::addr::remote;
use warp::Filter; use warp::Filter;
@ -23,23 +25,36 @@ use warp::Filter;
pub fn real_ip( pub fn real_ip(
trusted_proxies: Vec<IpAddr>, trusted_proxies: Vec<IpAddr>,
) -> impl Filter<Extract = (Option<IpAddr>,), Error = Infallible> + Clone { ) -> impl Filter<Extract = (Option<IpAddr>,), Error = Infallible> + Clone {
let forwarded_for = warp::header::<IpAddr>("x-forwarded-for") remote().and(get_forwarded_for()).map(
.or(warp::header("x-real-ip")) move |addr: Option<SocketAddr>, forwarded_for: Vec<IpAddr>| {
.unify()
.map(Some)
.or(warp::any().map(|| None))
.unify();
remote().and(forwarded_for).map(
move |addr: Option<SocketAddr>, forwarded_for: Option<IpAddr>| {
addr.map(|addr| { addr.map(|addr| {
let ip = addr.ip(); let hops = forwarded_for.iter().copied().chain(once(addr.ip()));
if trusted_proxies.contains(&ip) { for hop in hops.rev() {
forwarded_for.unwrap_or(ip) if !trusted_proxies.contains(&hop) {
} else { return hop;
ip
} }
}
// all hops were trusted, return the last one
forwarded_for.first().copied().unwrap_or(addr.ip())
}) })
}, },
) )
} }
/// Creates a `Filter` that extracts the ip addresses from the the "forwarded for" chain
pub fn get_forwarded_for() -> impl Filter<Extract = (Vec<IpAddr>,), Error = Infallible> + Clone {
warp::header::<String>("x-forwarded-for")
.map(|forwarded: String| {
forwarded
.split(',')
.map(str::trim)
.map(IpAddr::from_str)
.filter_map(Result::ok)
.collect::<Vec<IpAddr>>()
})
.or(warp::header("x-real-ip").map(|ip| vec![ip]))
.unify()
.or(warp::any().map(|| vec![]))
.unify()
}

View file

@ -39,3 +39,25 @@ async fn test_trusted() {
.await; .await;
assert_eq!(res.body(), "10.10.10.10"); assert_eq!(res.body(), "10.10.10.10");
} }
#[tokio::test]
async fn test_nested_denied() {
let remote: IpAddr = [1, 2, 3, 4].into();
let res = warp::test::request()
.remote_addr((remote, 80).into())
.header("x-forwarded-for", "10.10.10.10, 11.11.11.11")
.reply(&serve(vec![remote]))
.await;
assert_eq!(res.body(), "11.11.11.11");
}
#[tokio::test]
async fn test_nested_allowed() {
let remote: IpAddr = [1, 2, 3, 4].into();
let res = warp::test::request()
.remote_addr((remote, 80).into())
.header("x-forwarded-for", "10.10.10.10, 11.11.11.11")
.reply(&serve(vec![remote, [10, 10, 10, 10].into()]))
.await;
assert_eq!(res.body(), "11.11.11.11");
}