# ipware [![Crates.io](https://img.shields.io/crates/v/ipware.svg)](https://crates.io/crates/ipware) [![Documentation](https://docs.rs/ipware/badge.svg)](https://docs.rs/ipware) [![License](https://img.shields.io/github/license/orhanbalci/ipware.svg)](https://github.com/orhanbalci/ipware/blob/master/LICENSE) This library aims to extract ip address of http request clients by using different http-header values. Ported from [python-ipware](https://github.com/un33k/python-ipware) developped by [@un33k](https://github.com/un33k) ### ⚠️ Warning This library uses unstable rust API. ```rust ignore ![feature(ip)] ```` ### 📦 Cargo.toml ```toml [dependencies] ipware = "0.1" ``` ### 🔧 Example ```rust use http::{HeaderMap, HeaderName}; use ipware::{IpWare, IpWareConfig, IpWareProxy}; let ipware = IpWare::new( IpWareConfig::new( vec![ HeaderName::from_static("http_x_forwarded_for"), HeaderName::from_static("x_forwarded_for"), ], true, ), IpWareProxy::default(), ); let mut headers = HeaderMap::new(); headers.insert( "HTTP_X_FORWARDED_FOR", "177.139.233.139, 198.84.193.157, 198.84.193.158" .parse() .unwrap(), ); headers.insert( "X_FORWARDED_FOR", "177.139.233.138, 198.84.193.157, 198.84.193.158" .parse() .unwrap(), ); headers.insert("REMOTE_ADDR", "177.139.233.133".parse().unwrap()); let (ip_addr, trusted_route) = ipware.get_client_ip(&headers, false); println!("{} {}", ip_addr.unwrap(), trusted_route); ``` ### 🖨️ Output ```text 177.139.233.139 false ``` ### ⚙️ Configuration | Params | Description | | --------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `proxy_count` | : Total number of expected proxies (pattern: `client, proxy1, ..., proxy2`)
: if `proxy_count = 0` then `client`
: if `proxy_count = 1` then `client, proxy1`
: if `proxy_count = 2` then `client, proxy1, proxy2`
: if `proxy_count = 3` then `client, proxy1, proxy2 proxy3` | | `proxy_list` | : List of trusted proxies (pattern: `client, proxy1, ..., proxy2`)
: if `proxy_list = ['10.1.']` then `client, 10.1.1.1` OR `client, proxy1, 10.1.1.1`
: if `proxy_list = ['10.1', '10.2.']` then `client, 10.1.1.1` OR `client, proxy1, 10.2.2.2`
: if `proxy_list = ['10.1', '10.2.']` then `client, 10.1.1.1 10.2.2.2` OR `client, 10.1.1.1 10.2.2.2` | | `leftmost` | : `leftmost = True` is default for de-facto standard.
: `leftmost = False` for rare legacy networks that are configured with the `rightmost` pattern.
: It converts `client, proxy1 proxy2` to `proxy2, proxy1, client` | | Output | Description | | ----------------: | :------------------------------------------------------------------------------------------- | | `ip` | : Client IP address object of type IPv4Addr or IPv6Addr | | `trusted_route` | : If proxy `proxy_count` and/or `proxy_list` were provided and matched, `true`, else `false` | #### 🔢 Http Header Precedence Order The client IP address can be found in one or more request headers attributes. The lookup order is top to bottom and the default attributes are as follow. ```rust pub use http::HeaderName; let request_headers_precedence = vec![ HeaderName::from_static("x_forwarded_for"), /* Load balancers or proxies such as AWS ELB (default client is `left-most` [`, , `]), */ HeaderName::from_static("http_x_forwarded_for"), // Similar to X_FORWARDED_TO HeaderName::from_static("http_client_ip"), /* Standard headers used by providers such as Amazon EC2, Heroku etc. */ HeaderName::from_static("http_x_real_ip"), /* Standard headers used by providers such as Amazon EC2, Heroku etc. */ HeaderName::from_static("http_x_forwarded"), // Squid and others HeaderName::from_static("http_x_cluster_client_ip"), /* Rackspace LB and Riverbed Stingray */ HeaderName::from_static("http_forwarded_for"), // RFC 7239 HeaderName::from_static("http_forwarded"), // RFC 7239 HeaderName::from_static("http_via"), // Squid and others HeaderName::from_static("x-real-ip"), // NGINX HeaderName::from_static("x-cluster-client-ip"), // Rackspace Cloud Load Balancers HeaderName::from_static("x_forwarded"), // Squid HeaderName::from_static("forwarded_for"), // RFC 7239 HeaderName::from_static("cf-connecting-ip"), // CloudFlare HeaderName::from_static("true-client-ip"), // CloudFlare Enterprise, HeaderName::from_static("fastly-client-ip"), // Firebase, Fastly HeaderName::from_static("forwarded"), // RFC 7239 HeaderName::from_static("client-ip"), /* Akamai and Cloudflare: True-Client-IP and Fastly: Fastly-Client-IP */ HeaderName::from_static("remote_addr"), // Default ]; ``` You can customize the order by providing your own list using IpWareConfig. ```rust no_run use ipware::IpWareConfig; use http::HeaderName; // specific header name IpWareConfig::new(vec![HeaderName::from_static("http_x_forwarded_for")],true); // multiple header names IpWareConfig::new( vec![ HeaderName::from_static("http_x_forwarded_for"), HeaderName::from_static("x_forwarded_for"), ], true, ); ``` #### 🤝 Trusted Proxies If your http server is behind one or more known proxy server(s), you can filter out unwanted requests by providing a `trusted proxy list`, or a known proxy `count`. You can customize the proxy IP prefixes by providing your own list by using IpWareProxy. You can pass your custom list on every call, when calling the proxy-aware api to fetch the ip. ```rust no_run // In the above scenario, use your load balancer IP address as a way to filter out unwanted requests. use std::net::IpAddr; use ipware::IpWare; use ipware::IpWareConfig; use ipware::IpWareProxy; use http::HeaderMap; let headers = HeaderMap::new(); // replace this with your own headers let proxies = vec![ "198.84.193.157".parse::().unwrap(), "198.84.193.158".parse::().unwrap(), ]; let ipware = IpWare::new(IpWareConfig::default(), IpWareProxy::new(0, &proxies)); // usage: non-strict mode (X-Forwarded-For: , , , ) // The request went through our and , then our server // We choose the ip address to the left our and ignore other ips let (ip, trusted_route) = ipware.get_client_ip(&headers, false); // usage: strict mode (X-Forwarded-For: , , ) // The request went through our and , then our server // Total ip address are total trusted proxies + client ip // We don't allow far-end proxies, or fake addresses (exact or None) let (ip, trusted_route) = ipware.get_client_ip(&headers, true); ``` #### Proxy Count If your http server is behind a `known` number of proxies, but you deploy on multiple providers and don't want to track proxy IPs, you still can filter out unwanted requests by providing proxy `count`. You can customize the proxy count by providing your `proxy_count` using IpWareProxy. ```rust no_run use ipware::*; use std::net::IpAddr; // In the above scenario, the total number of proxies can be used as a way to filter out unwanted requests. // enforce proxy count let headers = HeaderMap::new(); // replace this with your own headers let proxies = vec![]; let ipware = IpWare::new(IpWareConfig::default(), IpWareProxy::new(1, &proxies)); // enforce proxy count and trusted proxies let proxies = vec!["198.84.193.157".parse::().unwrap()]; let ipware = IpWare::new(IpWareConfig::default(), IpWareProxy::new(1, &proxies)); // usage: non-strict mode (X-Forwarded-For: , , , ) // total number of ip addresses are greater than the total count let (ip, trusted_route) = ipware.get_client_ip(&headers, false); // usage: strict mode (X-Forwarded-For: , , ) // total number of ip addresses are exactly equal to client ip + proxy_count let (ip, trusted_route) = ipware.get_client_ip(&headers, true); ``` #### Support for IPv4, Ipv6, and IP:Port patterns and encapsulation ```text - Library looks for an IpAddr in header values. If this fails algorithm tries to parse a SocketAddr (This on contains ports in addition to IpAddr) - get_client_ip call returns an IpAddr enum. User can match for V4 or V6 variants. If a V6 ip is retrieved user can utilize `to_ipv4_mapped` to retrieve wrapped V4 address if available. ``` #### Originating Request ```test Please note that the [de-facto](https:#developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) standard for the originating client IP address is the `leftmost`as per`client, proxy1, proxy2`, and the `rightmost` proxy is the most trusted proxy. However, in rare cases your network has a `custom` configuration where the `rightmost` IP address is that of the originating client. If that is the case, then indicate it when creating: ``` ```rust no_run use ipware::*; let ipware = IpWare::new( IpWareConfig::default().leftmost(false), IpWareProxy::default(), ); ``` ## 📝 License Licensed under MIT License ([LICENSE](LICENSE)). ### 🚧 Contributions Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the MIT license, shall be licensed as above, without any additional terms or conditions.