Reputation: 326
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!
Upvotes: 12
Views: 2673
Reputation: 1121
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()
Upvotes: 1
Reputation: 130
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
Upvotes: 5
Reputation: 51369
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.
Upvotes: 4