Every Sent log line now carries method and path so noisy traffic (probes, scrapers, redirects) is identifiable without a tracer. RemoteIp plug already sets remote_ip metadata; add method and request_path alongside it and register the extra keys with the default formatter.
59 lines
1.3 KiB
Elixir
59 lines
1.3 KiB
Elixir
defmodule MicrowavepropWeb.Plugs.RemoteIp do
|
|
@moduledoc """
|
|
Extracts the client IP from X-Forwarded-For (set by nginx/dokku proxy)
|
|
and assigns it to conn.remote_ip and Logger metadata.
|
|
"""
|
|
|
|
@behaviour Plug
|
|
|
|
@impl true
|
|
def init(opts), do: opts
|
|
|
|
@impl true
|
|
# Cloudflare sets Cf-Connecting-Ip with the real client IP.
|
|
# Fall back to X-Forwarded-For for non-tunneled requests (dev, direct).
|
|
@ip_headers ["cf-connecting-ip", "x-forwarded-for"]
|
|
|
|
def call(conn, _opts) do
|
|
ip =
|
|
Enum.find_value(@ip_headers, fn header ->
|
|
case Plug.Conn.get_req_header(conn, header) do
|
|
[value | _] ->
|
|
value
|
|
|> String.split(",")
|
|
|> hd()
|
|
|> String.trim()
|
|
|> parse_ip()
|
|
|
|
_ ->
|
|
nil
|
|
end
|
|
end)
|
|
|
|
conn =
|
|
if ip do
|
|
%{conn | remote_ip: ip}
|
|
else
|
|
conn
|
|
end
|
|
|
|
Logger.metadata(
|
|
remote_ip: format_ip(conn.remote_ip),
|
|
method: conn.method,
|
|
request_path: conn.request_path
|
|
)
|
|
|
|
conn
|
|
end
|
|
|
|
defp parse_ip(str) do
|
|
case :inet.parse_address(String.to_charlist(str)) do
|
|
{:ok, ip} -> ip
|
|
_ -> nil
|
|
end
|
|
end
|
|
|
|
defp format_ip({a, b, c, d}), do: "#{a}.#{b}.#{c}.#{d}"
|
|
defp format_ip({a, b, c, d, e, f, g, h}), do: "#{a}:#{b}:#{c}:#{d}:#{e}:#{f}:#{g}:#{h}"
|
|
defp format_ip(_), do: "unknown"
|
|
end
|