[英]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.Conn
的remote_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.