ajvondrak / remote_ip

A plug to rewrite the Plug.Conn's remote_ip based on forwarding headers.
MIT License
252 stars 31 forks source link

Plug.Conn also has get_peer_data, which returns the original ip #32

Closed dvic closed 1 year ago

dvic commented 1 year ago

I realised with a LiveView project that overwriting the remote_ip is not enough because Plug.Conn has a get_peer_data function and that's what's being called by LiveView. This value is

@type peer_data :: %{
          address: :inet.ip_address(),
          port: :inet.port_number(),
          ssl_cert: binary | nil
        }

What is the way forward here? Wrap the adapter implementation and wrap the get_peer_data to override the ip?

dvic commented 1 year ago

The relevant calls: https://hexdocs.pm/plug/Plug.Conn.html#get_peer_data/1 and https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#get_connect_info/2

ajvondrak commented 1 year ago

Taking plug_cowboy as our canonical implementation, the way it works:

So ultimately, with a given Plug.Conn, the way we'd override the info returned by Plug.Conn.get_peer_data/1 would have to be adapter-specific. For Cowboy, this would involve manipulating the :peer key of the underlying conn.adapter payload (the Cowboy Req).

I believe clobbering this value would be a bad idea. My impression is that this data is used by Cowboy to actually orchestrate the HTTP response, which is why Plug.Conn exposes the :remote_ip as a separate entity that's meant to be overwritten.

I'm also under the impression that Phoenix LiveView relies on this "raw" data because of the low-level manipulation of sockets that it has to do. I'm unsure about those details.

The general workaround for this is to pass the forwarding headers to RemoteIp.from/2 in your Phoenix sockets, as sketched in the docs: https://hexdocs.pm/remote_ip/RemoteIp.html#module-usage I don't know enough about Phoenix to provide any more in-depth guidance than that. :sweat_smile:

One of the main issues people have bumped into with this is that Phoenix sockets only expose the x- headers, which won't work so well if you're using a header like Forwarded or Fly-Client-IP. There has been some amount of discussion within Phoenix about exposing more data, but I take it there are security concerns:

Given all that, I'm inclined to close this issue, since (a) I don't think we should be clobbering the raw peer data and (b) getting the right information for RemoteIp.from/2 to work (e.g., non-x- headers) would need to be solved on the Phoenix end. Let me know if you think differently, though.

dvic commented 1 year ago

Thanks for the elaborate response!

RemoteIp.from/2 will work for us because we're dealing with the x-forwarded-for header (Google Cloud k8s ingress).

I believe clobbering this value would be a bad idea. My impression is that this data is used by Cowboy to actually orchestrate the HTTP response, which is why Plug.Conn exposes the :remote_ip as a separate entity that's meant to be overwritten.

I did not think of this, then indeed it's a bad idea. We can go ahead and close this issue. Once again, thanks for the explanation 👍