簡體   English   中英

來自 %Plug.Conn{} 遠程 IP 的 Phoenix 解析請求主機

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

我正在嘗試做的一些總結。 我有一個在 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 應用程序的請求主機,而不是客戶端的。
我在這里做錯了什么?
提前致謝 :)

文檔中所述:

[the remote_ip ] 字段旨在被理解例如X-Forwarded-For標頭或 HAProxy 的 PROXY 協議的插件覆蓋。 它默認為對等方的 IP。

這在此處詳細說明:

當您的應用在 Nginx 等代理后運行時,請求看起來像是來自 Nginx,即 IP 將為 127.0.0.1。 同樣,如果 Nginx 在 CDN 后面,那么所有請求都將來自 CDN 的 IP。

所以你可以寫一個插件來覆蓋Plug.Connremote_ip字段。 以下是此類插頭的示例。 這個例子是從這篇博文中復制

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

該庫可用於簡化代理后 IP 地址的處理。

https://github.com/ajvondrak/remote_ip

作者有一篇很好的文章,介紹了適用的用例以及為什么該庫可能比滾動自己的庫更好:這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM