Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions channelserver/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,39 @@ fn main() {
};
let msettings = settings.clone();
let mut trusted_list: Vec<ipnet::IpNet> = Vec::new();
// Add the known private networks to the trusted proxy list
trusted_list.push("10.0.0.0/8".parse().unwrap());
trusted_list.push("172.16.0.0/12".parse().unwrap());
trusted_list.push("192.168.0.0/16".parse().unwrap());

// Add the list of trusted proxies.
if settings.trusted_proxy_list.len() > 0 {
for mut proxy in settings.trusted_proxy_list.split(",") {
proxy = proxy.trim();
if proxy.len() > 0 {
let addr: ipnet::IpNet = proxy.parse().unwrap();
trusted_list.push(addr);
// ipnet::IpNet only wants CIDRs. Normally that's not a problem, but the
// user may specify a single address. In that case, force the single
// into a CIDR by giving it a single address scope.
let mut fixed = proxy.to_owned();
if !proxy.contains("/") {
fixed = format!("{}/32", proxy);
debug!(log.log, "Fixing single address {}", fixed);
}
match fixed.parse::<ipnet::IpNet>() {
Ok(addr) => trusted_list.push(addr),
Err(err) => {
error!(logger.log, "Ignoring unparsable IP address \"{}\"", proxy);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: don't forget raw strings

Suggested change
error!(logger.log, "Ignoring unparsable IP address \"{}\"", proxy);
error!(logger.log, r#"Ignoring unparsable IP address "{}""#, proxy);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

honestly, I do. Frequently. Thanks!

}
};
}
}
}
debug!(logger.log, "Trusted Proxies: {:?}", trusted_list);
// check that the maxmind db is where it should be.
if !Path::new(&settings.mmdb_loc).exists() {
slog_error!(
error!(
logger.log,
"Cannot find geoip database: {}",
settings.mmdb_loc
"Cannot find geoip database: {}", settings.mmdb_loc
);
return;
};
Expand Down
52 changes: 28 additions & 24 deletions channelserver/src/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,10 @@ fn get_ua(
None
}

fn is_trusted_proxy(proxy_list: &[IpNet], host: &str) -> Result<bool, HandlerError> {
fn is_trusted_proxy(proxy_list: &[IpNet], host: &IpAddr) -> Result<bool, HandlerError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for a Result now, this can just return a bool (this could also be a one liner: proxy_list.iter().any(|range| range.contains(host)))

// Return if an address is NOT part of the allow list
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did I read this comment wrong or is it describing the opposite of what's happening?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sigh, no, you read it right. It's wrong. Thanks!

let test_addr: IpAddr = match host.parse() {
Ok(addr) => addr,
Err(e) => return Err(HandlerErrorKind::BadRemoteAddrError(format!("{:?}", e)).into()),
};
for proxy_range in proxy_list {
if proxy_range.contains(&test_addr) {
if proxy_range.contains(host) {
return Ok(true);
}
}
Expand All @@ -145,6 +141,7 @@ fn get_remote(
peer: &Option<SocketAddr>,
headers: &http::HeaderMap,
proxy_list: &[IpNet],
log: &logging::MozLogger,
) -> Result<String, HandlerError> {
// Actix determines the connection_info.remote() from the first entry in the
// Forwarded then X-Fowarded-For, Forwarded-For, then peer name. The problem is that any
Expand All @@ -156,31 +153,36 @@ fn get_remote(
if peer.is_none() {
return Err(HandlerErrorKind::BadRemoteAddrError("Peer is unspecified".to_owned()).into());
}
let peer_ip = peer.unwrap().ip().to_string();
let peer_ip = peer.unwrap().ip();
// if the peer is not a known proxy, ignore the X-Forwarded-For headers
if !is_trusted_proxy(proxy_list, &peer_ip)? {
return Ok(peer_ip);
return Ok(peer_ip.to_string());
}

// The peer is a known proxy, so take rightmost X-Forwarded-For that is not a trusted proxy.
match headers.get(HeaderName::from_lowercase("x-forwarded-for".as_bytes()).unwrap()) {
Some(header) => {
match header.to_str() {
Ok(hstr) => {
info!(log.log, "Remote IP List: {:?}", hstr);
// successive proxies are appeneded to this header.
let mut host_list: Vec<&str> = hstr.split(',').collect();
host_list.reverse();
for host_str in host_list {
let host = host_str.trim().to_owned();
if !is_trusted_proxy(proxy_list, &host)? {
return Ok(host.to_owned());
match host_str.trim().parse::<IpAddr>() {
Ok(addr) => {
if !addr.is_loopback() &&
!is_trusted_proxy(proxy_list, &addr)? {
return Ok(addr.to_string());
}
},
Err(err) => {
return Err(HandlerErrorKind::BadRemoteAddrError("Bad IP Specified".to_owned()).into());
}
}
}
Err(HandlerErrorKind::BadRemoteAddrError(format!(
"Could not find remote IP in X-Forwarded-For"
))
.into())
}
};
Err(HandlerErrorKind::BadRemoteAddrError("Only proxies specified".to_owned()).into())
},
Err(err) => Err(HandlerErrorKind::BadRemoteAddrError(format!(
"Unknown address in X-Forwarded-For: {:?}",
err
Expand Down Expand Up @@ -302,6 +304,7 @@ impl From<HttpRequest<WsChannelSessionState>> for SenderData {
&req.peer_addr(),
&req.headers(),
&req.state().trusted_proxy_list,
&log,
) {
Ok(addr) => Some(addr),
Err(err) => {
Expand Down Expand Up @@ -485,30 +488,31 @@ mod test {

let true_remote: SocketAddr = "1.2.3.4:0".parse().unwrap();
let proxy_server: SocketAddr = "192.168.0.4:0".parse().unwrap();
let log = logging::MozLogger::new_human();

bad_headers.insert(
http::header::HeaderName::from_lowercase("x-forwarded-for".as_bytes()).unwrap(),
"".parse().unwrap(),
);

// Proxy only, no XFF header
let remote = get_remote(&Some(proxy_server), &empty_headers, &proxy_list);
let remote = get_remote(&Some(proxy_server), &empty_headers, &proxy_list, &log);
assert!(remote.is_err());

// Proxy only, bad XFF header
let remote = get_remote(&Some(proxy_server), &bad_headers, &proxy_list);
let remote = get_remote(&Some(proxy_server), &bad_headers, &proxy_list, &log);
assert!(remote.is_err());

// Proxy only, crap XFF header
bad_headers.insert(
http::header::HeaderName::from_lowercase("x-forwarded-for".as_bytes()).unwrap(),
"invalid".parse().unwrap(),
);
let remote = get_remote(&Some(proxy_server), &bad_headers, &proxy_list);
let remote = get_remote(&Some(proxy_server), &bad_headers, &proxy_list, &log);
assert!(remote.is_err());

// Peer only, no header
let remote = get_remote(&Some(true_remote), &empty_headers, &proxy_list);
let remote = get_remote(&Some(true_remote), &empty_headers, &proxy_list, &log);
assert_eq!(remote.unwrap(), "1.2.3.4".to_owned());

headers.insert(
Expand All @@ -517,7 +521,7 @@ mod test {
);

// Peer proxy, fetch from XFF header
let remote = get_remote(&Some(proxy_server), &headers, &proxy_list);
let remote = get_remote(&Some(proxy_server), &headers, &proxy_list, &log);
assert_eq!(remote.unwrap(), "1.2.3.4".to_owned());

// Peer proxy, ensure right most XFF client fetched
Expand All @@ -526,7 +530,7 @@ mod test {
"1.2.3.4, 2.3.4.5".parse().unwrap(),
);

let remote = get_remote(&Some(proxy_server), &headers, &proxy_list);
let remote = get_remote(&Some(proxy_server), &headers, &proxy_list, &log);
assert_eq!(remote.unwrap(), "2.3.4.5".to_owned());

// Peer proxy, ensure right most non-proxy XFF client fetched
Expand All @@ -535,7 +539,7 @@ mod test {
"1.2.3.4, 2.3.4.5, 192.168.0.10".parse().unwrap(),
);

let remote = get_remote(&Some(proxy_server), &headers, &proxy_list);
let remote = get_remote(&Some(proxy_server), &headers, &proxy_list, &log);
assert_eq!(remote.unwrap(), "2.3.4.5".to_owned());
}
}