websocket_relay/
security.rs

1use anyhow::{Result, anyhow};
2use ipnet::IpNet;
3use std::net::IpAddr;
4
5/// Parses the original client IP from X-Forwarded-For header
6/// Format: "client, proxy1, proxy2, ..." - returns the leftmost (original client) IP
7#[must_use]
8pub fn parse_original_client_ip(xff_header: &str) -> Option<String> {
9    xff_header
10        .split(',')
11        .next()
12        .map(|ip| ip.trim().to_string())
13        .filter(|ip| !ip.is_empty())
14}
15
16/// Checks if a proxy IP address is allowed based on the configured allowlist
17/// Returns true if no allowlist is configured (allow all) or if IP matches any entry
18pub fn is_proxy_ip_allowed(proxy_ip: IpAddr, allowed_ips: Option<&Vec<String>>) -> Result<bool> {
19    let Some(allowed_list) = allowed_ips else {
20        return Ok(true); // No restrictions configured
21    };
22
23    for allowed_entry in allowed_list {
24        // Try parsing as individual IP address first
25        if let Ok(allowed_ip) = allowed_entry.parse::<IpAddr>() {
26            if proxy_ip == allowed_ip {
27                return Ok(true);
28            }
29        }
30        // Try parsing as CIDR subnet
31        else if let Ok(allowed_net) = allowed_entry.parse::<IpNet>() {
32            if allowed_net.contains(&proxy_ip) {
33                return Ok(true);
34            }
35        } else {
36            return Err(anyhow!(
37                "Invalid IP address or CIDR in allowed_proxy_ips: {}",
38                allowed_entry
39            ));
40        }
41    }
42
43    Ok(false)
44}