1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
use rfc7239::{parse, Forwarded, NodeIdentifier, NodeName};
use std::convert::Infallible;
use std::iter::once;
use std::net::{IpAddr, SocketAddr};
use std::str::FromStr;
use warp::filters::addr::remote;
use warp::Filter;
pub fn real_ip(
trusted_proxies: Vec<IpAddr>,
) -> impl Filter<Extract = (Option<IpAddr>,), Error = Infallible> + Clone {
remote().and(get_forwarded_for()).map(
move |addr: Option<SocketAddr>, forwarded_for: Vec<IpAddr>| {
addr.map(|addr| {
let hops = forwarded_for.iter().copied().chain(once(addr.ip()));
for hop in hops.rev() {
if !trusted_proxies.contains(&hop) {
return hop;
}
}
forwarded_for.first().copied().unwrap_or(addr.ip())
})
},
)
}
pub fn get_forwarded_for() -> impl Filter<Extract = (Vec<IpAddr>,), Error = Infallible> + Clone {
warp::header("x-forwarded-for")
.map(|list: CommaSeparated<IpAddr>| list.into_inner())
.or(warp::header("x-real-ip").map(|ip| vec![ip]))
.unify()
.or(warp::header("forwarded").map(|header: String| {
parse(&header)
.filter_map(|forward| match forward {
Ok(Forwarded {
forwarded_for:
Some(NodeIdentifier {
name: NodeName::Ip(ip),
..
}),
..
}) => Some(ip),
_ => None,
})
.collect::<Vec<_>>()
}))
.unify()
.or(warp::any().map(|| vec![]))
.unify()
}
struct CommaSeparated<T>(Vec<T>);
impl<T> CommaSeparated<T> {
pub fn into_inner(self) -> Vec<T> {
self.0
}
}
impl<T: FromStr> FromStr for CommaSeparated<T> {
type Err = T::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let vec = s
.split(',')
.map(str::trim)
.map(T::from_str)
.collect::<Result<Vec<_>, _>>()?;
Ok(CommaSeparated(vec))
}
}