Comments (3)
Summarizing what I understand from your descriptions:
iex> RemoteIp.from([{"x-forwarded-for", "97.126.47.5, 172.33.29.126,::ffff:172.33.253.26, 34.228.243.32"}])
{34, 228, 243, 32}
IPs are processed right-to-left to prevent spoofing. 34.228.243.32
is not a known proxy, so it's presumed to be the client immediately. None of the other IPs are considered.
iex> RemoteIp.from([{"x-forwarded-for", "97.126.47.5, 34.97.78.79,::ffff:10.116.4.32, 35.245.31.68"}], proxies: ~w[34.97.78.79])
{35, 245, 31, 68}
Again, the rightmost IP in the header is not a known proxy, so it is returned immediately.
iex> RemoteIp.from([{"x-forwarded-for", "97.126.47.5, 34.97.78.79,::ffff:10.116.4.32, 35.245.31.68"}], proxies: ~w[34.97.78.79 35.245.31.68])
{0, 0, 0, 0, 0, 65535, 2676, 1056}
Here, the rightmost IP is a known proxy, so it's skipped. The next IP is ::ffff:10.116.4.32
, which is not recognized as a reserved IP or as a proxy, so it's presumed to be the client.
So:
Does the algorithm account for private ranges in this notation format?
It does not. The current list of IPs is given by
Lines 262 to 269 in 4826fc4
This permalink is circa v1.0.0, but the list has been the same for the life of the remote_ip library. The base IPs are parsed via :inet.parse_strict_address/1
, which will take the IPv4 notation and produce an IPv4-style tuple of 4 integers, which won't compare the same to an IPv6 address in mapped-IPv4 format:
> :inet.parse_strict_address('10.0.0.0')
{:ok, {10, 0, 0, 0}}
> :inet.parse_strict_address('::ffff:10.116.4.32')
{:ok, {0, 0, 0, 0, 0, 65535, 2676, 1056}}
You can add a CIDR for the mapped-IPv4 range to your known proxies to prevent the IPv6 block from being considered.
Is it just a fluke that the first configuration works?
The first configuration works because you aren't saying that the rightmost IP is a known proxy, so it's presumed to be the client.
thanks much
Thanks for the thorough write-up! Hope I've cleared up any confusion. :)
from remote_ip.
@ajvondrak thank you for your response. I can confirm that whitelisting ::ffff:10.112.0.0/12
in my second case does in fact solve the problem. However I wish to clarify something about my issue.
...
iex> RemoteIp.from([{"x-forwarded-for", "97.126.47.5, 172.33.29.126,::ffff:172.33.253.26, 34.228.243.32"}])
{34, 228, 243, 32}
...
IPs are processed right-to-left to prevent spoofing. 34.228.243.32 is not a known proxy, so it's presumed to be the client immediately. None of the other IPs are considered.
In this particular case, 34.228.243.32 is not returned, the desired IP 97.126.47.5
is returned. This has worked consistently in a production environment for some time, and was the impetus for my filing this issue because in attempting to move to a new network topology (described in my second example) I realized I didn't understand why it works.
What's more, I have observed inconsistent behavior in parsing with the new topology as well. I have upgraded to v1.0.0 in the interim in order to make use of the new runtime configuration feature. The following header returns the leftmost IP:
"67.168.104.21, 172.33.148.54,::ffff:172.33.13.200, 3.84.201.74, 167.82.224.22"
# ^remote --^internal host - ^internal proxy host - ^host (unknown) -- ^known-proxy
I would expect 3.84.201.74
to be returned because it is not whitelisted. However 67.168.104.21
is returned.
But this example:
"67.168.104.21, 172.33.207.160,::ffff:172.33.129.250, 3.84.201.210, 157.52.99.83"
# ^remote --^internal host - ^internal proxy host - ^host (unknown) -- ^known-proxy
correctly returns
RemoteIp.from([{"x-forwarded-for", "67.168.104.21, 172.33.207.160,::ffff:172.33.129.250, 3.89.201.210, 157.52.99.83"}], proxies: [<very-long-proxy-list>])
{3, 89, 201, 210}
These scenarios are leaving me genuinely confused about what to expect.
from remote_ip.
I would expect
3.84.201.74
to be returned because it is not whitelisted. However67.168.104.21
is returned.
That sounds strange, indeed. Would it be helpful to use the debugger? I imagine it'd be noisy in production, but maybe on a canary deploy or something?
These scenarios are leaving me genuinely confused about what to expect.
It really just depends on what's in the list of :clients
and :proxies
. The algorithm doesn't involve much: scan IPs right-to-left, return the first client. The distinction between "client" vs "non-client" has a couple moving pieces. In order:
- Is the IP in one of the CIDRs in
:clients
? Then it's a client. - Is the IP in one of the CIDRs in
:proxies
? Then it's not a client. - Is the IP in one of the CIDRs hard-coded for reserved addresses? Then it's not a client.
- Otherwise, the IP is assumed to be a client.
In code:
Lines 255 to 257 in 4826fc4
Lines 271 to 282 in 4826fc4
Beyond that, it might be that remote_ip isn't seeing the headers you think it's seeing. For example, maybe it's pulling in some non-XFF header when you're using the default :headers
option (which includes a bunch of different values). The debugger can give more insight into the parsing steps.
from remote_ip.
Related Issues (16)
- Add inet_cidr to included_applications HOT 5
- @reserved is incorrect HOT 1
- problem with rewritten IP HOT 2
- :combine should not be in :included_applications
- Dialyzer warning HOT 5
- Export `RemoteIp.Block` as its own package? HOT 8
- X-Forwarded-For is parsed incorrectly! HOT 3
- Good article on `x-forwarded-for` parsing HOT 10
- Unsure of Implementation HOT 5
- Support for `Fly-Client-IP`? HOT 7
- Plug.Conn also has get_peer_data, which returns the original ip HOT 3
- Parse X-Forwarded-Port and X-Forwarded-Proto HOT 3
- Doesn't Work Running Server in Docker Container HOT 5
- Any way to pass runtime information to `init` HOT 1
- RFC1918 IPs shouldn't be discarded by default HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from remote_ip.