Skip to content

Review the process of selecting the client/peer IP when the tracker is behind a proxy #163

Open
@josecelano

Description

@josecelano

A comment on this PR originated a discussion and finally this issue.

Introduction

The tracker can be installed behind a reverse proxy:

client          <-> HTTP proxy                       <-> tracker                   <-> Internet
ip:                 header:                              config:                       peer addr:
145.254.214.256     X-Forwarded-For = 145.254.214.256    on_reverse_proxy = true       145.254.214.256

Preconditions

  • The tracker service provider also sets up and handles the HTTP proxy.
  • The HTTP proxy adds a new X-Forwarded-For header with the client IP if the header does not exist yet. If the header exists, it adds the client IP at the enf (rigth) of the header value, containg the list of address.
  • The tracker is configured with the option on_reverse_proxy = true

Actual behavior

The application is taken the rigthmost IP address in the header. That means the IP of the direct client of the owned HTTP proxy. The reason is we do not trust other proxies. They could spoof that header.

https://github.com/torrust/torrust-tracker/blob/develop/src/http/filters.rs#L135-L158

/// Get `PeerAddress` from `RemoteAddress` or Forwarded
fn peer_addr((on_reverse_proxy, remote_addr, x_forwarded_for): (bool, Option<SocketAddr>, Option<String>)) -> WebResult<IpAddr> {
    if !on_reverse_proxy && remote_addr.is_none() {
        return Err(reject::custom(Error::AddressNotFound));
    }

    if on_reverse_proxy && x_forwarded_for.is_none() {
        return Err(reject::custom(Error::AddressNotFound));
    }

    if on_reverse_proxy {
        let mut x_forwarded_for_raw = x_forwarded_for.unwrap();
        // remove whitespace chars
        x_forwarded_for_raw.retain(|c| !c.is_whitespace());
        // get all forwarded ip's in a vec
        let x_forwarded_ips: Vec<&str> = x_forwarded_for_raw.split(',').collect();
        // set client ip to last forwarded ip
        let x_forwarded_ip = *x_forwarded_ips.last().unwrap();

        IpAddr::from_str(x_forwarded_ip).map_err(|_| reject::custom(Error::AddressNotFound))
    } else {
        Ok(remote_addr.unwrap().ip())
    }
}

Consecuence

Since we do not trust proxies, the tracker will have only the rigth client IP if there is exactly one proxy (owned proxy) in the middle.

If the client tries to reach the tracker and there are other proxies in the middle, the tracker will not have the rigth client public IP.

This was the intended behavior.

If we use the leftmost IP address (or the first public IP starting on the left) the tracker could risk to be spammed with false peers.

The UDP tracker uses a conenction ID to avoid it.

Conclusion

  • Should we keep the current behavior?
  • Is there al alternative way to avoid the spam/spoofing?

Links

Initial Proposals

  1. Keep the current behavior.
  2. Take the leftmost (public) IP address and do nothing about false peers. The tracker would contain false peers but peers can go offile or the can change thier IP often so that's should not be a big problem.
  3. Try to connect to the peer to validate it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    - Admin -Enjoyable to Install and Setup our SoftwareNeeds ResearchWe Need to Know More About ThisQuestion / DiscussionCommunity Feedback

    Type

    No type

    Projects

    • Status

      Help Wanted

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions