简体   繁体   English

来自 %Plug.Conn{} 远程 IP 的 Phoenix 解析请求主机

[英]Phoenix Resolve Request Host from %Plug.Conn{} remote ip

A little summary of what I'm trying to do.我正在尝试做的一些总结。 I have a Phoenix app running on an AWS Elastic Beanstalk instance and I'm sending REST API requests containing PDFs for manipulation (splitting, merging etc).我有一个在 AWS Elastic Beanstalk 实例上运行的 Phoenix 应用程序,我正在发送包含用于操作(拆分、合并等)的 PDF 的 REST API 请求。 Each Request is saved in the database.每个请求都保存在数据库中。 This is what my requests schema looks like:这是我的requests架构的样子:

 schema "requests" do
    field :body, :string
    field :endpoint, :string
    field :method, :string
    field :request_host, :string
    field :response_body, :string
    field :response_code, :integer
    field :work_group_id, :integer
    field :identifier, :string
    field :responded_at, :utc_datetime

    timestamps()
  end

Through Graphql, I'm making a request from a separate RAILS server and displaying the hostname, inserted_at field, and response_code field of all the requests.通过Graphql,我正在从独立的Rails服务器的请求,并显示该主机名, inserted_at领域, response_code所有要求的领域。

I'm experiencing problems trying to resolve the client's host name.我在尝试解析客户端的主机名时遇到问题。 This is the Erlang method I'm using where the method argument remote_ip is relatively conn.remote_ip :这是我使用的 Erlang 方法,其中方法参数 remote_ip 相对conn.remote_ip

 {:ok, {:hostent, request_host, _, _, _, _}} = :inet.gethostbyaddr(remote_ip)

This method returns my Phoenix app's request host and not the clients'.此方法返回我的 Phoenix 应用程序的请求主机,而不是客户端的。
What am I doing wrong here?我在这里做错了什么?
Thanks in advance :)提前致谢 :)

As described in the docs :文档中所述:

[The remote_ip ] field is meant to be overwritten by plugs that understand eg the X-Forwarded-For header or HAProxy's PROXY protocol. [the remote_ip ] 字段旨在被理解例如X-Forwarded-For标头或 HAProxy 的 PROXY 协议的插件覆盖。 It defaults to peer's IP.它默认为对等方的 IP。

This is elaborated upon here :这在此处详细说明:

When your app is running behind a proxy like Nginx, then the request will look like it's coming from Nginx, ie the IP will be 127.0.0.1.当您的应用在 Nginx 等代理后运行时,请求看起来像是来自 Nginx,即 IP 将为 127.0.0.1。 Similarly, If Nginx is behind a CDN, then all the requests will come from the IP of the CDN.同样,如果 Nginx 在 CDN 后面,那么所有请求都将来自 CDN 的 IP。

So you can write a plug to overwrite the remote_ip field of the Plug.Conn .所以你可以写一个插件来覆盖Plug.Connremote_ip字段。 Following is an example of such a plug.以下是此类插头的示例。 This example is copied from this blog post .这个例子是从这篇博文中复制

defmodule MyApp.Plug.PublicIp do
  @moduledoc "Get public IP address of request from x-forwarded-for header"
  @behaviour Plug
  @app :my_app

  def init(opts), do: opts

  def call(%{assigns: %{ip: _}} = conn, _opts), do: conn
  def call(conn, _opts) do
    process(conn, Plug.Conn.get_req_header(conn, "x-forwarded-for"))
  end

  def process(conn, []) do
    Plug.Conn.assign(conn, :ip, to_string(:inet.ntoa(get_peer_ip(conn))))
  end
  def process(conn, vals) do
    if Application.get_env(@app, :trust_x_forwarded_for, false) do ip_address = get_ip_address(conn, vals) # Rewrite standard remote_ip field with value from header
      # See https://hexdocs.pm/plug/Plug.Conn.html
      conn = %{conn | remote_ip: ip_address}

      Plug.Conn.assign(conn, :ip, to_string(:inet.ntoa(ip_address)))
    else
      Plug.Conn.assign(conn, :ip, to_string(:inet.ntoa(get_peer_ip(conn))))
    end
  end

  defp get_ip_address(conn, vals)
  defp get_ip_address(conn, []), do: get_peer_ip(conn)
  defp get_ip_address(conn, [val | _]) do
    # Split into multiple values
    comps = val
      |> String.split(~r{\s*,\s*}, trim: true)
      |> Enum.filter(&(&1 != "unknown"))          # Get rid of "unknown" values
      |> Enum.map(&(hd(String.split(&1, ":"))))   # Split IP from port, if any
      |> Enum.filter(&(&1 != ""))                 # Filter out blanks
      |> Enum.map(&(parse_address(&1)))           # Parse address into :inet.ip_address tuple
      |> Enum.filter(&(is_public_ip(&1)))         # Elminate internal IP addreses, e.g. 192.168.1.1

    case comps do
      [] -> get_peer_ip(conn)
      [comp | _] -> comp
    end
  end

  @spec get_peer_ip(Plug.Conn.t) :: :inet.ip_address
  defp get_peer_ip(conn) do
    {ip, _port} = conn.peer
    ip
  end

  @spec parse_address(String.t) :: :inet.ip_address
  defp parse_address(ip) do
    case :inet.parse_ipv4strict_address(to_charlist(ip)) do
      {:ok, ip_address} -> ip_address
      {:error, :einval} -> :einval
    end
  end

  # Whether the input is a valid, public IP address
  # http://en.wikipedia.org/wiki/Private_network
  @spec is_public_ip(:inet.ip_address | atom) :: boolean
  defp is_public_ip(ip_address) do
    case ip_address do
      {10, _, _, _}     -> false
      {192, 168, _, _}  -> false
      {172, second, _, _} when second >= 16 and second <= 31 -> false
      {127, 0, 0, _}    -> false
      {_, _, _, _}      -> true
      :einval           -> false
    end
  end
end

This library is available to simplify handling of IP addresses behind proxies.该库可用于简化代理后 IP 地址的处理。

https://github.com/ajvondrak/remote_ip https://github.com/ajvondrak/remote_ip

The author has a nice writeup of applicable use-cases and why the library is probably a better choice than rolling your own: here作者有一篇很好的文章,介绍了适用的用例以及为什么该库可能比滚动自己的库更好:这里

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Phoenix:如何在控制台中获得conn%Plug.Conn {} - Phoenix: How to get conn %Plug.Conn{} in the console expected:action / 2返回Phoenix控制器中的Plug.Conn错误 - expected :action/2 to return a Plug.Conn error in Phoenix controller 如何将请求参数填充到 Plug.Conn 连接中? - How to populate request parameters into a Plug.Conn connection? 从Plug.Conn获取HTTP_REFERRER - Get HTTP_REFERRER from Plug.Conn Phoenix Framework的Plug.Conn中assign和put_session有什么区别? - What is the difference between assign and put_session in Plug.Conn of the Phoenix Framework? 如何使用Plug.Conn读取phoenix控制器中的小块数据 - How to read small chunks of data in phoenix controller, using Plug.Conn Elixir从控制器获取错误:预期动作/ 2返回Plug.Conn - Elixir getting error from controller: Expected action/2 to return a Plug.Conn **(RuntimeError)预期的操作/ 2返回一个Plug.Conn,所有插件都必须接收一个连接(conn)并返回一个连接 - ** (RuntimeError) expected action/2 to return a Plug.Conn, all plugs must receive a connection (conn) and return a connection Plug.Conn,所有插头必须接收一个连接(conn)并返回一个连接 - Plug.Conn, all plugs must receive a connection (conn) and return a connection (RuntimeError)期望动作/ 2返回一个Plug.Conn,所有插件必须接收连接(conn)并返回连接 - (RuntimeError) expected action/2 to return a Plug.Conn, all plugs must receive a connection (conn) and return a connection
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM