actix_remote_ip/
lib.rs

1//! #actix-remote-ip
2//!
3//! A tiny crate to determine the Real IP address of a client, taking account
4//! of an eventual reverse proxy
5
6use crate::ip_utils::{match_ip, parse_ip};
7use actix_web::dev::Payload;
8use actix_web::web::Data;
9use actix_web::{Error, FromRequest, HttpRequest};
10use futures_util::future::{ready, Ready};
11use std::fmt::Display;
12use std::net::IpAddr;
13
14mod ip_utils;
15
16/// Remote IP retrieval configuration
17///
18/// This configuration must be built and set as Actix web data
19#[derive(Debug, Clone, Eq, PartialEq, Default)]
20pub struct RemoteIPConfig {
21    /// The IP address of the proxy. This address can ends with a star '*'
22    pub proxy: Option<String>,
23}
24
25impl RemoteIPConfig {
26    /// Initiate a new RemoteIPConfig configuration instance, that
27    /// can be set as [actix_web::Data] structure
28    pub fn with_proxy_ip<D: Display>(proxy: D) -> Self {
29        Self {
30            proxy: Some(proxy.to_string()),
31        }
32    }
33}
34
35/// Get the remote IP address
36pub fn get_remote_ip(req: &HttpRequest) -> IpAddr {
37    let proxy = req
38        .app_data::<Data<RemoteIPConfig>>()
39        .map(|c| c.proxy.as_ref())
40        .unwrap_or_default();
41    log::trace!("Proxy IP: {:?}", proxy);
42
43    let mut ip = req.peer_addr().unwrap().ip();
44
45    // We check if the request comes from a trusted reverse proxy
46    if let Some(proxy) = proxy.as_ref() {
47        if match_ip(proxy, &ip.to_string()) {
48            if let Some(header) = req.headers().get("X-Forwarded-For") {
49                let header = header.to_str().unwrap();
50
51                let remote_ip = if let Some((upstream_ip, _)) = header.split_once(',') {
52                    upstream_ip
53                } else {
54                    header
55                };
56
57                if let Some(upstream_ip) = parse_ip(remote_ip) {
58                    ip = upstream_ip;
59                }
60            }
61        }
62    }
63
64    ip
65}
66
67#[derive(Copy, Clone, Debug, Eq, PartialEq)]
68pub struct RemoteIP(pub IpAddr);
69
70impl From<RemoteIP> for IpAddr {
71    fn from(i: RemoteIP) -> Self {
72        i.0
73    }
74}
75
76impl FromRequest for RemoteIP {
77    type Error = Error;
78    type Future = Ready<Result<Self, Error>>;
79
80    #[inline]
81    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
82        ready(Ok(RemoteIP(get_remote_ip(req))))
83    }
84}
85
86#[cfg(test)]
87mod test {
88    use std::net::{IpAddr, SocketAddr};
89    use std::str::FromStr;
90
91    use crate::{get_remote_ip, RemoteIPConfig};
92    use actix_web::test::TestRequest;
93    use actix_web::web::Data;
94
95    #[test]
96    fn test_get_remote_ip() {
97        let req = TestRequest::default()
98            .peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
99            .to_http_request();
100        assert_eq!(
101            get_remote_ip(&req),
102            "192.168.1.1".parse::<IpAddr>().unwrap()
103        );
104    }
105
106    #[test]
107    fn test_get_remote_ip_from_proxy() {
108        let req = TestRequest::default()
109            .peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
110            .insert_header(("X-Forwarded-For", "1.1.1.1"))
111            .app_data(Data::new(RemoteIPConfig::with_proxy_ip("192.168.1.1")))
112            .to_http_request();
113
114        assert_eq!(get_remote_ip(&req), "1.1.1.1".parse::<IpAddr>().unwrap());
115    }
116
117    #[test]
118    fn test_get_remote_ip_from_proxy_2() {
119        let req = TestRequest::default()
120            .peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
121            .insert_header(("X-Forwarded-For", "1.1.1.1, 1.2.2.2"))
122            .app_data(Data::new(RemoteIPConfig::with_proxy_ip("192.168.1.1")))
123            .to_http_request();
124        assert_eq!(get_remote_ip(&req), "1.1.1.1".parse::<IpAddr>().unwrap());
125    }
126
127    #[test]
128    fn test_get_remote_ip_from_proxy_ipv6() {
129        let req = TestRequest::default()
130            .peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
131            .insert_header(("X-Forwarded-For", "10::1, 1.2.2.2"))
132            .app_data(Data::new(RemoteIPConfig::with_proxy_ip("192.168.1.1")))
133            .to_http_request();
134
135        assert_eq!(get_remote_ip(&req), "10::".parse::<IpAddr>().unwrap());
136    }
137
138    #[test]
139    fn test_get_remote_ip_from_no_proxy() {
140        let req = TestRequest::default()
141            .peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
142            .insert_header(("X-Forwarded-For", "1.1.1.1, 1.2.2.2"))
143            .to_http_request();
144
145        assert_eq!(
146            get_remote_ip(&req),
147            "192.168.1.1".parse::<IpAddr>().unwrap()
148        );
149    }
150
151    #[test]
152    fn test_get_remote_ip_from_other_proxy() {
153        let req = TestRequest::default()
154            .peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
155            .insert_header(("X-Forwarded-For", "1.1.1.1, 1.2.2.2"))
156            .app_data(Data::new(RemoteIPConfig::with_proxy_ip("192.168.1.2")))
157            .to_http_request();
158
159        assert_eq!(
160            get_remote_ip(&req),
161            "192.168.1.1".parse::<IpAddr>().unwrap()
162        );
163    }
164}