【问题标题】:Phoenix Resolve Request Host from %Plug.Conn{} remote ipPhoenix 从 %Plug.Conn{} 远程 ip 解析请求主机
【发布时间】:2019-01-18 11:23:49
【问题描述】:

我正在尝试做的一个小总结。我有一个在 AWS Elastic Beanstalk 实例上运行的 Phoenix 应用程序,我正在发送包含用于操作(拆分、合并等)的 PDF 的 REST API 请求。每个请求都保存在数据库中。这就是我的 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

通过 Graphql,我从单独的 RAILS 服务器发出请求,并显示所有请求的主机名、inserted_at 字段和 response_code 字段。

我在尝试解析客户端的主机名时遇到问题。这是我正在使用的 Erlang 方法,其中方法参数 remote_ip 相对conn.remote_ip

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

此方法返回我的 Phoenix 应用程序的请求主机,而不是客户端的。
我在这里做错了什么?
在此先感谢:)

【问题讨论】:

  • 在所描述的过程中根本没有任何“客户”的概念
  • 目前我正在通过 REST CLIENT(insomnia) 将请求发送到服务器。
  • 它并没有让事情变得更清楚。你希望抓住谁的 IP?
  • remote_ip - 客户端的 IP,例如:{151, 236, 219, 228}。该字段旨在被理解的插件覆盖,例如X-Forwarded-For 标头或 HAProxy 的 PROXY 协议。它默认为对等方的 IP。 (这是对我尝试使用的 conn.remote_ip 的解释)
  • Elastic Beanstalk 是否配置了反向代理 nginx?它可能会在 conn 上设置 ip 地址。有几个插件包可以从 X-forwarded-for 标头设置 conn.remote_ip。

标签: elixir phoenix-framework hostname


【解决方案1】:

该库可用于简化代理背后 IP 地址的处理。

https://github.com/ajvondrak/remote_ip

作者对适用的用例进行了很好的描述,以及为什么该库可能是比自己编写库更好的选择:here

【讨论】:

    【解决方案2】:

    docs中所述:

    [remote_ip] 字段旨在被理解的插件覆盖,例如X-Forwarded-For 标头或 HAProxy 的代理协议。它默认为对等方的 IP。

    这个在here详细说明:

    当您的应用在 Nginx 之类的代理后面运行时,请求看起来像是来自 Nginx,即 IP 将是 127.0.0.1。同样,如果 Nginx 在 CDN 后面,那么所有请求都来自 CDN 的 IP。

    因此您可以编写一个插件来覆盖Plug.Connremote_ip 字段。以下是此类插头的示例。这个例子是从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
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-07
      • 2011-01-10
      • 2017-11-15
      • 2022-01-24
      • 2019-03-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多