remote_ip v0.2.0 RemoteIp View Source
A plug to overwrite the Plug.Conn
's remote_ip
based on request headers.
To use, add the RemoteIp
plug to your app's plug pipeline:
defmodule MyApp do
use Plug.Builder
plug RemoteIp
end
Keep in mind the order of plugs in your pipeline and place RemoteIp
as
early as possible. For example, if you were to add RemoteIp
afterthe
Plug Router, your route
action's logic would be executed before the remote_ip
actually gets
modified - not very useful!
There are 3 options that can be passed in:
:headers
- A list of strings naming thereq_headers
to use when deriving theremote_ip
. Order does not matter. Defaults to~w[forwarded x-forwarded-for x-client-ip x-real-ip]
.:proxies
- A list of strings in CIDR notation specifying the IPs of known proxies. Defaults to[]
.Loopback and private IPs are always appended to this list:
- 127.0.0.0/8
- ::1/128
- fc00::/7
- 10.0.0.0/8
- 172.16.0.0/12
192.168.0.0/16
Since these IPs are internal, they often are not the actual client address in production, so we add them by default. To override this behavior, whitelist known client IPs using the
:clients
option.
:clients
- A list of strings in CIDR notation specifying the IPs of known clients. Defaults to[]
.An IP in any of the ranges listed here will never be considered a proxy. This takes precedence over the
:proxies
option, including loopback/private addresses. Any IP that is not covered by:clients
or:proxies
is assumed to be a client IP.
For example, suppose you know:
- you are behind proxies in the 1.2.x.x block
- the proxies use the
X-Foo
,X-Bar
, andX-Baz
headers - but the IP 1.2.3.4 is actually a client, not one of the proxies
Then you could say
defmodule MyApp do
use Plug.Builder
plug RemoteIp,
headers: ~w[x-foo x-bar x-baz],
proxies: ~w[1.2.0.0/16],
clients: ~w[1.2.3.4/32]
end
Note that, due to limitations in the
inet_cidr library used to parse
them, :proxies
and :clients
must be written in full CIDR notation,
even if specifying just a single IP. So instead of "127.0.0.1"
and
"a:b::c:d"
, you would use "127.0.0.1/32"
and "a:b::c:d/128"
.
For more details, refer to the README on GitHub.
Link to this section Summary
Functions
Callback implementation for Plug.call/2
.
Standalone function to extract the remote IP from a list of headers.
Callback implementation for Plug.init/1
.
Link to this section Functions
Callback implementation for Plug.call/2
.
from(req_headers, opts \\ [])
View Sourcefrom([{String.t(), String.t()}], keyword()) :: :inet.ip_address() | nil
Standalone function to extract the remote IP from a list of headers.
It's possible to get a subset of headers without access to a full Plug.Conn
struct. For instance, when using Phoenix
sockets, your socket's
connect/3
callback may only be receiving :x_headers
in the
connect_info
. Such situations make it inconvenient to use RemoteIp
outside of a plug pipeline.
Therefore, this function will fetch the remote IP from a plain list of header
key-value pairs (just as you'd have in the req_headers
of a Plug.Conn
).
You may optionally specify the same options as if you were using RemoteIp
as a plug: they'll be processed by RemoteIp.init/1
each time you call this
function.
If a remote IP cannot be parsed from the given headers (e.g., if the list is
empty), this function will return nil
.
Examples
iex> RemoteIp.from([{"x-forwarded-for", "1.2.3.4"}])
{1, 2, 3, 4}
iex> [{"x-foo", "1.2.3.4"}, {"x-bar", "2.3.4.5"}]
...> |> RemoteIp.from(headers: ~w[x-foo])
{1, 2, 3, 4}
iex> [{"x-foo", "1.2.3.4"}, {"x-bar", "2.3.4.5"}]
...> |> RemoteIp.from(headers: ~w[x-bar])
{2, 3, 4, 5}
iex> [{"x-foo", "1.2.3.4"}, {"x-bar", "2.3.4.5"}]
...> |> RemoteIp.from(headers: ~w[x-baz])
nil
Callback implementation for Plug.init/1
.