Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to get remote_ip from socket in phoenix-framework?

How to get remote_ip from socket in phoenixframework? I can get it from conn in View, but not in Channel.

Many thanks for help!

like image 852
czinkipuczczum karamba Avatar asked Oct 22 '15 08:10

czinkipuczczum karamba


3 Answers

Copy of the answer provided here: https://elixirforum.com/t/phoenix-socket-channels-security-ip-identification/1463/3 (all the credit goes to https://elixirforum.com/u/arjan)

Phoenix 1.4 update:

Since Phoenix 1.4, you can get connection information from the underlying transport. What kind of information you get is transport dependent, but with the WebSocket transport it is possible to retrieve the peer info (ip address) and a list of x- headers (for x-forwarded-for resolving).

Configure your socket like this in your endpoint.ex:

socket("/socket", MyApp.Web.UserSocket,
  websocket: [connect_info: [:peer_data, :x_headers]],
  longpoll: [connect_info: [:peer_data, :x_headers]]
)

And then your UserSocket module must expose a connect/3 function like this:

def connect(_params, socket, connect_info) do
  {:ok, socket}
end

On connect, the connect_info parameter now contains info from the transport:

info: %{
  peer_data: %{address: {127, 0, 0, 1}, port: 52372, ssl_cert: nil},
  x_headers: []
}

UPDATE

If your Phoenix app is not handling traffic directly and receives it from reverse proxy like nginx then peer_data will have nginx IP address, not the client's. To fix this you can tell nginx (or whatever proxy you use) to pass original IP in the headers and then read it later.

So your phoenix location should look something like this:

location /phoenix/ {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://phoenix/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
}

and your socket code should have this:

defp get_ip_address(%{x_headers: headers_list}) do
  header = Enum.find(headers_list, fn {key, _val} -> key == "x-real-ip" end)

  case header do
    nil ->
      nil

    {_key, value} ->
      value

    _ ->
      nil
  end
end

defp get_ip_address(_) do
  nil
end

and change connect to something like this

def connect(params, socket, connect_info) do
  socket = assign(socket, :ip_address, get_ip_address(connect_info))
  {:ok, socket}      
end
like image 103
Victor Ivanov Avatar answered Nov 19 '22 02:11

Victor Ivanov


The answer right now is: you can't. You can't access the connection in channels because channels are transport agnostic. Open up an issue in Phoenix detailing your user case so the Phoenix team can act on it.

like image 4
José Valim Avatar answered Nov 19 '22 01:11

José Valim


Good news! as of LiveView 0.17.7 it's available out of the box:

see https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html

to summarize:

in endpoint.ex find the socket definition and add

socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [:peer_data, session: @session_options]]

in the socket mount() function

def mount(_params, _session, socket) do
    peer_data = get_connect_info(socket, :peer_data)
    {:ok, socket}
end

note: it's only available on mount() and terminate()

like image 1
MoVod Avatar answered Nov 19 '22 03:11

MoVod