aster_forge_actix_middleware/
client_ip.rs1use std::net::IpAddr;
8
9use actix_web::http::header::HeaderMap;
10use ipnet::IpNet;
11
12#[must_use]
17pub fn real_ip_from_headers(
18 headers: &HeaderMap,
19 peer: IpAddr,
20 trusted_proxies: &[String],
21) -> IpAddr {
22 let trusted = aster_forge_utils::net::parse_trusted_proxies(trusted_proxies);
23 real_ip_from_trusted_headers(headers, peer, &trusted)
24}
25
26#[must_use]
28pub fn real_ip_from_trusted_headers(
29 headers: &HeaderMap,
30 peer: IpAddr,
31 trusted: &[IpNet],
32) -> IpAddr {
33 let x_forwarded_for = headers
34 .get("x-forwarded-for")
35 .and_then(|value| value.to_str().ok());
36 aster_forge_utils::net::real_ip_from_forwarded_for(x_forwarded_for, peer, trusted)
37}
38
39#[cfg(test)]
40mod tests {
41 use std::net::IpAddr;
42
43 use actix_web::test as actix_test;
44 use aster_forge_utils::net::{is_trusted_proxy, parse_trusted_proxies};
45
46 use super::{real_ip_from_headers, real_ip_from_trusted_headers};
47
48 #[test]
49 fn parses_trusted_proxies_as_cidr_and_single_ip() {
50 let trusted = parse_trusted_proxies(&["10.0.0.0/8".to_string(), "192.168.1.1".to_string()]);
51
52 assert!(is_trusted_proxy("10.0.0.5".parse().unwrap(), &trusted));
53 assert!(is_trusted_proxy("192.168.1.1".parse().unwrap(), &trusted));
54 assert!(!is_trusted_proxy("203.0.113.1".parse().unwrap(), &trusted));
55 }
56
57 #[test]
58 fn real_ip_uses_leftmost_forwarded_value_only_for_trusted_peer() {
59 let trusted = parse_trusted_proxies(&["10.0.0.0/8".to_string()]);
60 let req = actix_test::TestRequest::default()
61 .insert_header(("X-Forwarded-For", "203.0.113.10, 198.51.100.2"))
62 .to_srv_request();
63
64 assert_eq!(
65 real_ip_from_trusted_headers(
66 req.headers(),
67 "10.0.0.5".parse::<IpAddr>().unwrap(),
68 &trusted,
69 ),
70 "203.0.113.10".parse::<IpAddr>().unwrap()
71 );
72 assert_eq!(
73 real_ip_from_trusted_headers(
74 req.headers(),
75 "198.51.100.2".parse::<IpAddr>().unwrap(),
76 &trusted,
77 ),
78 "198.51.100.2".parse::<IpAddr>().unwrap()
79 );
80 }
81
82 #[test]
83 fn real_ip_falls_back_to_peer_for_invalid_header() {
84 let req = actix_test::TestRequest::default()
85 .insert_header(("X-Forwarded-For", "not-an-ip"))
86 .to_srv_request();
87
88 assert_eq!(
89 real_ip_from_headers(
90 req.headers(),
91 "10.0.0.5".parse::<IpAddr>().unwrap(),
92 &["10.0.0.0/8".to_string()],
93 ),
94 "10.0.0.5".parse::<IpAddr>().unwrap()
95 );
96 }
97
98 #[test]
99 fn real_ip_accepts_forwarded_values_with_ports() {
100 let trusted = parse_trusted_proxies(&["10.0.0.0/8".to_string()]);
101
102 for (forwarded, expected) in [
103 ("203.0.113.10:54321, 10.0.0.5", "203.0.113.10"),
104 ("[2001:db8::1]:443, 10.0.0.5", "2001:db8::1"),
105 ] {
106 let req = actix_test::TestRequest::default()
107 .insert_header(("X-Forwarded-For", forwarded))
108 .to_srv_request();
109
110 assert_eq!(
111 real_ip_from_trusted_headers(
112 req.headers(),
113 "10.0.0.5".parse::<IpAddr>().unwrap(),
114 &trusted,
115 ),
116 expected.parse::<IpAddr>().unwrap()
117 );
118 }
119 }
120}