Compare commits
29 Commits
32bed1c2f4
...
1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 00b73c80b6 | |||
| 3a35a2df41 | |||
| 5240a871ad | |||
| 10de58c421 | |||
| 5f1ad75126 | |||
| f0bca5ecdf | |||
| ed7c0b090b | |||
| 67f922fa0a | |||
| f2292ad425 | |||
| f29656cc63 | |||
| acb82262ed | |||
| d762d97832 | |||
| b5c421f896 | |||
| 632c91ece7 | |||
| 40d4db1ee7 | |||
| a48b6bb854 | |||
| 3354e7ca0e | |||
| f97624d48d | |||
| c1afcc21f6 | |||
| 51ac9290bd | |||
| ec43657c2a | |||
| 956bfdaf73 | |||
| 1eba754e86 | |||
| ab7a6f5f3c | |||
| 614752132b | |||
| 89865dbb28 | |||
| 4ddb88805c | |||
| 29c68e8153 | |||
| dff56b187e |
916
Cargo.lock
generated
916
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-remote-ip"
|
name = "actix-remote-ip"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
authors = ["Pierre HUBERT <pierre.git@communiquons.org>"]
|
authors = ["Pierre HUBERT <pierre.git@communiquons.org>"]
|
||||||
description = "Tiny extractor to get real client IP address, parsing X-Forwarded-For header"
|
description = "Tiny extractor to get real client IP address, parsing X-Forwarded-For header"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -11,6 +11,7 @@ repository = "https://gitea.communiquons.org/pierre/actix-remote-ip"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.21"
|
log = "0.4.29"
|
||||||
actix-web = "4.5.1"
|
actix-web = "4.13.0"
|
||||||
futures-util = "0.3.30"
|
futures-util = "0.3.32"
|
||||||
|
ipnet = "2.12.0"
|
||||||
@@ -14,9 +14,7 @@ Configure it when you configure your Actix server:
|
|||||||
```rust
|
```rust
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.app_data(web::Data::new(RemoteIPConfig {
|
.app_data(web::Data::new(RemoteIPConfig::parse_opt(Some("IP"))))
|
||||||
proxy: Some("IP".to_string())
|
|
||||||
}))
|
|
||||||
// ...
|
// ...
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
"extends": ["local>renovate/presets"]
|
||||||
}
|
}
|
||||||
@@ -1,30 +1,5 @@
|
|||||||
use std::net::{IpAddr, Ipv6Addr};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
/// Parse an IP address
|
|
||||||
pub fn parse_ip(ip: &str) -> Option<IpAddr> {
|
|
||||||
let mut ip = match IpAddr::from_str(ip) {
|
|
||||||
Ok(ip) => ip,
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("Failed to parse an IP address: {}", e);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// In case of IPv6 address, we skip the 8 last octets
|
|
||||||
if let IpAddr::V6(ipv6) = &mut ip {
|
|
||||||
let mut octets = ipv6.octets();
|
|
||||||
for o in octets.iter_mut().skip(8) {
|
|
||||||
*o = 0;
|
|
||||||
}
|
|
||||||
ip = IpAddr::V6(Ipv6Addr::from(octets));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(ip)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if two ips matches
|
/// Check if two ips matches
|
||||||
pub fn match_ip(pattern: &str, ip: &str) -> bool {
|
pub(crate) fn legacy_match_ip(pattern: &str, ip: &str) -> bool {
|
||||||
if pattern.eq(ip) {
|
if pattern.eq(ip) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -38,8 +13,19 @@ pub fn match_ip(pattern: &str, ip: &str) -> bool {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::ip_utils::parse_ip;
|
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// Parse an IP address
|
||||||
|
fn parse_ip(ip: &str) -> Option<IpAddr> {
|
||||||
|
match IpAddr::from_str(ip) {
|
||||||
|
Ok(ip) => Some(ip),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Failed to parse an IP address: {e}");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_bad_ip() {
|
fn parse_bad_ip() {
|
||||||
@@ -58,19 +44,21 @@ mod test {
|
|||||||
let ip = parse_ip("2a00:1450:4007:813::200e").unwrap();
|
let ip = parse_ip("2a00:1450:4007:813::200e").unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ip,
|
ip,
|
||||||
IpAddr::V6(Ipv6Addr::new(0x2a00, 0x1450, 0x4007, 0x813, 0, 0, 0, 0))
|
IpAddr::V6(Ipv6Addr::new(
|
||||||
|
0x2a00, 0x1450, 0x4007, 0x813, 0, 0, 0, 0x200e
|
||||||
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_ip_v6_address_2() {
|
fn parse_ip_v6_address_2() {
|
||||||
let ip = parse_ip("::1").unwrap();
|
let ip = parse_ip("::1").unwrap();
|
||||||
assert_eq!(ip, IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)));
|
assert_eq!(ip, IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_ip_v6_address_3() {
|
fn parse_ip_v6_address_3() {
|
||||||
let ip = parse_ip("a::1").unwrap();
|
let ip = parse_ip("a::1").unwrap();
|
||||||
assert_eq!(ip, IpAddr::V6(Ipv6Addr::new(0xa, 0, 0, 0, 0, 0, 0, 0)));
|
assert_eq!(ip, IpAddr::V6(Ipv6Addr::new(0xa, 0, 0, 0, 0, 0, 0, 1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
354
src/lib.rs
354
src/lib.rs
@@ -3,64 +3,133 @@
|
|||||||
//! A tiny crate to determine the Real IP address of a client, taking account
|
//! A tiny crate to determine the Real IP address of a client, taking account
|
||||||
//! of an eventual reverse proxy
|
//! of an eventual reverse proxy
|
||||||
|
|
||||||
use crate::ip_utils::{match_ip, parse_ip};
|
use crate::legacy_ip_utils::legacy_match_ip;
|
||||||
use actix_web::dev::Payload;
|
use actix_web::dev::Payload;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use actix_web::{Error, FromRequest, HttpRequest};
|
use actix_web::{Error, FromRequest, HttpRequest};
|
||||||
use futures_util::future::{ready, Ready};
|
use futures_util::future::{Ready, ready};
|
||||||
|
use ipnet::AddrParseError;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::net::IpAddr;
|
use std::net::{IpAddr, Ipv6Addr};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
mod ip_utils;
|
/// Legacy IP support
|
||||||
|
pub(crate) mod legacy_ip_utils;
|
||||||
|
|
||||||
/// Remote IP retrieval configuration
|
/// Remote IP retrieval configuration
|
||||||
///
|
///
|
||||||
/// This configuration must be built and set as Actix web data
|
/// This configuration must be built and set as Actix web data
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||||
pub struct RemoteIPConfig {
|
pub enum RemoteIPConfig {
|
||||||
/// The IP address of the proxy. This address can ends with a star '*'
|
/// Legacy IP format
|
||||||
pub proxy: Option<String>,
|
Legacy(String),
|
||||||
|
/// Modern IP format
|
||||||
|
Modern(Vec<ipnet::IpNet>),
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoteIPConfig {
|
impl RemoteIPConfig {
|
||||||
/// Initiate a new RemoteIPConfig configuration instance, that
|
/// Initiate a new RemoteIPConfig configuration instance, that
|
||||||
/// can be set as [actix_web::Data] structure
|
/// can be set as [actix_web::Data] structure
|
||||||
pub fn with_proxy_ip<D: Display>(proxy: D) -> Self {
|
pub fn parse_opt<D: Display>(proxy: Option<D>) -> Self {
|
||||||
Self {
|
match proxy {
|
||||||
proxy: Some(proxy.to_string()),
|
None => Self::None,
|
||||||
|
Some(p) => Self::parse(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initiate a new RemoteIPConfig configuration instance, that
|
||||||
|
/// can be set as [actix_web::Data] structure
|
||||||
|
pub fn parse<D: Display>(proxy: D) -> Self {
|
||||||
|
Self::try_parse(proxy).expect("failed to parse given proxy ip address!")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initiate a new RemoteIPConfig configuration instance, that
|
||||||
|
/// can be set as [actix_web::Data] structure
|
||||||
|
pub fn try_parse<D: Display>(proxy: D) -> Result<Self, AddrParseError> {
|
||||||
|
let proxy_ip = proxy.to_string();
|
||||||
|
|
||||||
|
// If nothing was specified...
|
||||||
|
if proxy_ip.trim().is_empty() {
|
||||||
|
return Ok(Self::None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle legacy format
|
||||||
|
if (proxy_ip.contains("*") || !proxy_ip.contains("/")) && !proxy_ip.contains(",") {
|
||||||
|
return Ok(Self::Legacy(proxy_ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ips = vec![];
|
||||||
|
|
||||||
|
for ip in proxy_ip.split(",") {
|
||||||
|
let ip = ip.trim();
|
||||||
|
if ip.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ips.push(ipnet::IpNet::from_str(ip)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self::Modern(ips))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check out whether an IP address is a trusted IP address or not
|
||||||
|
pub fn is_trusted_proxy_ip(&self, ip: IpAddr) -> bool {
|
||||||
|
match self {
|
||||||
|
RemoteIPConfig::Legacy(proxy_ip) => legacy_match_ip(proxy_ip, &ip.to_string()),
|
||||||
|
|
||||||
|
RemoteIPConfig::Modern(networks) => networks.iter().any(|n| n.contains(&ip)),
|
||||||
|
|
||||||
|
// No proxy
|
||||||
|
RemoteIPConfig::None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the remote IP address
|
/// Get the remote IP address
|
||||||
pub fn get_remote_ip(req: &HttpRequest) -> IpAddr {
|
pub fn get_remote_ip(req: &HttpRequest) -> IpAddr {
|
||||||
let proxy = req
|
let config = req.app_data::<Data<RemoteIPConfig>>().map(|c| c.as_ref());
|
||||||
.app_data::<Data<RemoteIPConfig>>()
|
|
||||||
.map(|c| c.proxy.as_ref())
|
|
||||||
.unwrap_or_default();
|
|
||||||
log::trace!("Proxy IP: {:?}", proxy);
|
|
||||||
|
|
||||||
let mut ip = req.peer_addr().unwrap().ip();
|
log::trace!("Proxy IP config: {:?}", config);
|
||||||
|
|
||||||
|
let mut peer_ip = req.peer_addr().unwrap().ip();
|
||||||
|
|
||||||
// We check if the request comes from a trusted reverse proxy
|
// We check if the request comes from a trusted reverse proxy
|
||||||
if let Some(proxy) = proxy.as_ref() {
|
if let Some(config) = config.as_ref()
|
||||||
if match_ip(proxy, &ip.to_string()) {
|
&& config.is_trusted_proxy_ip(peer_ip)
|
||||||
if let Some(header) = req.headers().get("X-Forwarded-For") {
|
&& let Some(header) = req.headers().get("X-Forwarded-For")
|
||||||
let header = header.to_str().unwrap();
|
{
|
||||||
|
let header = header.to_str().unwrap();
|
||||||
|
|
||||||
let remote_ip = if let Some((upstream_ip, _)) = header.split_once(',') {
|
let remote_ip = if let Some((upstream_ip, _)) = header.split_once(',') {
|
||||||
upstream_ip
|
upstream_ip
|
||||||
} else {
|
} else {
|
||||||
header
|
header
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(upstream_ip) = parse_ip(remote_ip) {
|
match IpAddr::from_str(remote_ip) {
|
||||||
ip = upstream_ip;
|
Ok(ip) => peer_ip = ip,
|
||||||
}
|
Err(e) => {
|
||||||
|
log::warn!("Failed to parse an IP address: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generalize_ip(peer_ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generalize IPv6 to consider IP inside a network as a single IP (for bruteforce protection)
|
||||||
|
fn generalize_ip(mut ip: IpAddr) -> IpAddr {
|
||||||
|
// In case of IPv6 address, we skip the 8 last octets
|
||||||
|
if let IpAddr::V6(ipv6) = &mut ip {
|
||||||
|
let mut octets = ipv6.octets();
|
||||||
|
for o in octets.iter_mut().skip(8) {
|
||||||
|
*o = 0;
|
||||||
|
}
|
||||||
|
ip = IpAddr::V6(Ipv6Addr::from(octets));
|
||||||
|
}
|
||||||
|
|
||||||
ip
|
ip
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,9 +157,98 @@ mod test {
|
|||||||
use std::net::{IpAddr, SocketAddr};
|
use std::net::{IpAddr, SocketAddr};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::{get_remote_ip, RemoteIPConfig};
|
use crate::{RemoteIPConfig, get_remote_ip};
|
||||||
use actix_web::test::TestRequest;
|
use actix_web::test::TestRequest;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_config() {
|
||||||
|
assert_eq!(
|
||||||
|
RemoteIPConfig::parse_opt::<String>(None),
|
||||||
|
RemoteIPConfig::None
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
RemoteIPConfig::parse_opt(Some("192.168.1.5")),
|
||||||
|
RemoteIPConfig::Legacy("192.168.1.5".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
RemoteIPConfig::parse("192.168.1.5"),
|
||||||
|
RemoteIPConfig::Legacy("192.168.1.5".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
RemoteIPConfig::try_parse("192.168.1.5"),
|
||||||
|
Ok(RemoteIPConfig::Legacy("192.168.1.5".to_string()))
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
RemoteIPConfig::parse("10.0.0.0/8"),
|
||||||
|
RemoteIPConfig::Modern(vec![IpNet::V4(Ipv4Net::from_str("10.0.0.0/8").unwrap())])
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
RemoteIPConfig::parse("10.0.0.0/8,192.168.0.0/16"),
|
||||||
|
RemoteIPConfig::Modern(vec![
|
||||||
|
IpNet::V4(Ipv4Net::from_str("10.0.0.0/8").unwrap()),
|
||||||
|
IpNet::V4(Ipv4Net::from_str("192.168.0.0/16").unwrap())
|
||||||
|
])
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
RemoteIPConfig::parse("10.0.0.0/8,192.168.0.0/16,2001:d445:b62e:dd2a::/64"),
|
||||||
|
RemoteIPConfig::Modern(vec![
|
||||||
|
IpNet::V4(Ipv4Net::from_str("10.0.0.0/8").unwrap()),
|
||||||
|
IpNet::V4(Ipv4Net::from_str("192.168.0.0/16").unwrap()),
|
||||||
|
IpNet::V6(Ipv6Net::from_str("2001:d445:b62e:dd2a::/64").unwrap())
|
||||||
|
])
|
||||||
|
);
|
||||||
|
assert_eq!(RemoteIPConfig::parse(""), RemoteIPConfig::None);
|
||||||
|
|
||||||
|
assert!(RemoteIPConfig::try_parse("10.0.0.0/a,192.168.0.0/16").is_err());
|
||||||
|
assert!(RemoteIPConfig::try_parse("10.0.0.0/a,192:.168.0.0/16").is_err());
|
||||||
|
assert!(RemoteIPConfig::try_parse("10.0.0.0/a,192:*.168.0.0/16").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trusted_ip() {
|
||||||
|
let none = RemoteIPConfig::None;
|
||||||
|
assert!(!none.is_trusted_proxy_ip(IpAddr::from_str("0.0.0.0").unwrap()));
|
||||||
|
assert!(!none.is_trusted_proxy_ip(IpAddr::from_str("10.20.30.40").unwrap()));
|
||||||
|
assert!(!none.is_trusted_proxy_ip(IpAddr::from_str("192.168.1.0").unwrap()));
|
||||||
|
assert!(!none.is_trusted_proxy_ip(IpAddr::from_str("192.169.1.0").unwrap()));
|
||||||
|
assert!(
|
||||||
|
!none.is_trusted_proxy_ip(IpAddr::from_str("2001:d445:b62e:dd2a:2:2:2:0").unwrap())
|
||||||
|
);
|
||||||
|
|
||||||
|
let legacy = RemoteIPConfig::Legacy("10.*".to_string());
|
||||||
|
assert!(legacy.is_trusted_proxy_ip(IpAddr::from_str("10.20.30.40").unwrap()));
|
||||||
|
assert!(!legacy.is_trusted_proxy_ip(IpAddr::from_str("192.168.1.0").unwrap()));
|
||||||
|
assert!(!legacy.is_trusted_proxy_ip(IpAddr::from_str("192.169.1.0").unwrap()));
|
||||||
|
assert!(
|
||||||
|
!legacy.is_trusted_proxy_ip(IpAddr::from_str("2001:d445:b62e:dd2a:2:2:2:0").unwrap())
|
||||||
|
);
|
||||||
|
|
||||||
|
let legacyv6 = RemoteIPConfig::Legacy("2001:d445:b62e:dd2a*".to_string());
|
||||||
|
assert!(!legacyv6.is_trusted_proxy_ip(IpAddr::from_str("10.20.30.40").unwrap()));
|
||||||
|
assert!(!legacyv6.is_trusted_proxy_ip(IpAddr::from_str("192.168.1.0").unwrap()));
|
||||||
|
assert!(!legacyv6.is_trusted_proxy_ip(IpAddr::from_str("192.169.1.0").unwrap()));
|
||||||
|
assert!(
|
||||||
|
legacyv6.is_trusted_proxy_ip(IpAddr::from_str("2001:d445:b62e:dd2a:2:2:2:0").unwrap())
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!legacyv6.is_trusted_proxy_ip(IpAddr::from_str("2001:a445:b62e:dd2a:2:2:2:0").unwrap())
|
||||||
|
);
|
||||||
|
|
||||||
|
let modern = RemoteIPConfig::Modern(vec![
|
||||||
|
IpNet::V4(Ipv4Net::from_str("10.0.0.0/8").unwrap()),
|
||||||
|
IpNet::V4(Ipv4Net::from_str("192.168.0.0/16").unwrap()),
|
||||||
|
IpNet::V6(Ipv6Net::from_str("2001:d445:b62e:dd2a::/64").unwrap()),
|
||||||
|
]);
|
||||||
|
assert!(modern.is_trusted_proxy_ip(IpAddr::from_str("10.20.30.40").unwrap()));
|
||||||
|
assert!(modern.is_trusted_proxy_ip(IpAddr::from_str("192.168.1.0").unwrap()));
|
||||||
|
assert!(!modern.is_trusted_proxy_ip(IpAddr::from_str("192.169.1.0").unwrap()));
|
||||||
|
assert!(
|
||||||
|
modern.is_trusted_proxy_ip(IpAddr::from_str("2001:d445:b62e:dd2a:2:2:2:0").unwrap())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_remote_ip() {
|
fn test_get_remote_ip() {
|
||||||
@@ -104,32 +262,114 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_remote_ip_from_proxy() {
|
fn test_get_remote_ip_v6() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.peer_addr(
|
||||||
|
SocketAddr::from_str("[2001:c0c3:f0d2:562d:193d:a5f6:1668:3cba]:1000").unwrap(),
|
||||||
|
)
|
||||||
|
.to_http_request();
|
||||||
|
assert_eq!(
|
||||||
|
get_remote_ip(&req),
|
||||||
|
"2001:c0c3:f0d2:562d:0:0:0:0".parse::<IpAddr>().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_remote_ip_from_proxy_legacy() {
|
||||||
let req = TestRequest::default()
|
let req = TestRequest::default()
|
||||||
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
||||||
.insert_header(("X-Forwarded-For", "1.1.1.1"))
|
.insert_header(("X-Forwarded-For", "1.1.1.1"))
|
||||||
.app_data(Data::new(RemoteIPConfig::with_proxy_ip("192.168.1.1")))
|
.app_data(Data::new(RemoteIPConfig::parse("192.168.1.1")))
|
||||||
.to_http_request();
|
.to_http_request();
|
||||||
|
|
||||||
assert_eq!(get_remote_ip(&req), "1.1.1.1".parse::<IpAddr>().unwrap());
|
assert_eq!(get_remote_ip(&req), "1.1.1.1".parse::<IpAddr>().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_remote_ip_from_proxy_2() {
|
fn test_get_remote_ip_from_proxy_legacy_2() {
|
||||||
let req = TestRequest::default()
|
let req = TestRequest::default()
|
||||||
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
||||||
.insert_header(("X-Forwarded-For", "1.1.1.1, 1.2.2.2"))
|
.insert_header(("X-Forwarded-For", "1.1.1.1, 1.2.2.2"))
|
||||||
.app_data(Data::new(RemoteIPConfig::with_proxy_ip("192.168.1.1")))
|
.app_data(Data::new(RemoteIPConfig::parse("192.168.1.1")))
|
||||||
.to_http_request();
|
.to_http_request();
|
||||||
assert_eq!(get_remote_ip(&req), "1.1.1.1".parse::<IpAddr>().unwrap());
|
assert_eq!(get_remote_ip(&req), "1.1.1.1".parse::<IpAddr>().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_remote_ip_from_proxy_ipv6() {
|
fn test_get_remote_ip_from_proxy_modern() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
||||||
|
.insert_header(("X-Forwarded-For", "1.1.1.1"))
|
||||||
|
.app_data(Data::new(RemoteIPConfig::parse("192.168.1.0/24")))
|
||||||
|
.to_http_request();
|
||||||
|
|
||||||
|
assert_eq!(get_remote_ip(&req), "1.1.1.1".parse::<IpAddr>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_remote_ip_from_proxy_modern_multiple() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.peer_addr(SocketAddr::from_str("10.10.1.1:1000").unwrap())
|
||||||
|
.insert_header(("X-Forwarded-For", "1.1.1.1"))
|
||||||
|
.app_data(Data::new(RemoteIPConfig::parse(
|
||||||
|
"192.168.1.0/24,10.10.0.0/16",
|
||||||
|
)))
|
||||||
|
.to_http_request();
|
||||||
|
|
||||||
|
assert_eq!(get_remote_ip(&req), "1.1.1.1".parse::<IpAddr>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_remote_ip_from_proxy_modern_untrusted() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.peer_addr(SocketAddr::from_str("10.10.1.1:1000").unwrap())
|
||||||
|
.insert_header(("X-Forwarded-For", "1.1.1.1"))
|
||||||
|
.app_data(Data::new(RemoteIPConfig::parse("192.168.1.0/24")))
|
||||||
|
.to_http_request();
|
||||||
|
|
||||||
|
assert_eq!(get_remote_ip(&req), "10.10.1.1".parse::<IpAddr>().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_remote_ip_from_proxy_modern_untrusted_ipv6() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.peer_addr(
|
||||||
|
SocketAddr::from_str("[2001:d445:b62e:dd2a:1ab8:3a21:c200:69ce]:1000").unwrap(),
|
||||||
|
)
|
||||||
|
.insert_header(("X-Forwarded-For", "1.1.1.1"))
|
||||||
|
.app_data(Data::new(RemoteIPConfig::parse("192.168.1.0/24")))
|
||||||
|
.to_http_request();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_remote_ip(&req),
|
||||||
|
"2001:d445:b62e:dd2a::".parse::<IpAddr>().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_remote_ip_from_proxy_modern_trusted_ipv6() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.peer_addr(
|
||||||
|
SocketAddr::from_str("[2001:d445:b62e:dd2a:1ab8:3a21:c200:69ce]:1000").unwrap(),
|
||||||
|
)
|
||||||
|
.insert_header(("X-Forwarded-For", "2001:129c:4dee:1f22:ddcb:d2ae:cdcb:dd22"))
|
||||||
|
.app_data(Data::new(RemoteIPConfig::parse(
|
||||||
|
"192.168.1.0/24,2001:d445:b62e:dd2a:1ab8:3a21:c200:69ce/128",
|
||||||
|
)))
|
||||||
|
.to_http_request();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_remote_ip(&req),
|
||||||
|
"2001:129c:4dee:1f22::".parse::<IpAddr>().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_remote_ip_from_proxy_ipv6_legacy() {
|
||||||
let req = TestRequest::default()
|
let req = TestRequest::default()
|
||||||
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
||||||
.insert_header(("X-Forwarded-For", "10::1, 1.2.2.2"))
|
.insert_header(("X-Forwarded-For", "10::1, 1.2.2.2"))
|
||||||
.app_data(Data::new(RemoteIPConfig::with_proxy_ip("192.168.1.1")))
|
.app_data(Data::new(RemoteIPConfig::parse("192.168.1.1")))
|
||||||
.to_http_request();
|
.to_http_request();
|
||||||
|
|
||||||
assert_eq!(get_remote_ip(&req), "10::".parse::<IpAddr>().unwrap());
|
assert_eq!(get_remote_ip(&req), "10::".parse::<IpAddr>().unwrap());
|
||||||
@@ -149,11 +389,51 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_remote_ip_from_other_proxy() {
|
fn test_get_remote_ip_from_other_proxy_legacy() {
|
||||||
let req = TestRequest::default()
|
let req = TestRequest::default()
|
||||||
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
||||||
.insert_header(("X-Forwarded-For", "1.1.1.1, 1.2.2.2"))
|
.insert_header(("X-Forwarded-For", "1.1.1.1, 1.2.2.2"))
|
||||||
.app_data(Data::new(RemoteIPConfig::with_proxy_ip("192.168.1.2")))
|
.app_data(Data::new(RemoteIPConfig::parse("192.168.1.2")))
|
||||||
|
.to_http_request();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_remote_ip(&req),
|
||||||
|
"192.168.1.1".parse::<IpAddr>().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_remote_ip_from_other_proxy_modern() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
||||||
|
.insert_header(("X-Forwarded-For", "1.1.1.1, 1.2.2.2"))
|
||||||
|
.app_data(Data::new(RemoteIPConfig::parse("192.168.1.2/32")))
|
||||||
|
.to_http_request();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_remote_ip(&req),
|
||||||
|
"192.168.1.1".parse::<IpAddr>().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_remote_ip_config_without_proxy_legacy() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
||||||
|
.app_data(Data::new(RemoteIPConfig::parse("10.0.0.*")))
|
||||||
|
.to_http_request();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
get_remote_ip(&req),
|
||||||
|
"192.168.1.1".parse::<IpAddr>().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_remote_ip_config_without_proxy_modern() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.peer_addr(SocketAddr::from_str("192.168.1.1:1000").unwrap())
|
||||||
|
.app_data(Data::new(RemoteIPConfig::parse("10.0.0.1/32")))
|
||||||
.to_http_request();
|
.to_http_request();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
Reference in New Issue
Block a user