-
-
Notifications
You must be signed in to change notification settings - Fork 159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added remote_ip to request object #1675
Conversation
… name where the IP value comes from. Added new remote_ip method to give you access to the raw string. Fixes #1643
src/lucky/remote_ip_handler.cr
Outdated
@@ -10,17 +10,26 @@ | |||
class Lucky::RemoteIpHandler | |||
include HTTP::Handler | |||
|
|||
Habitat.create do | |||
setting ip_header_names : Array(String) = ["X_FORWARDED_FOR"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not completely sold on this name, but naming is hard....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to my note about performance, there are some security implications to this magic. Remote IP detection isn't "guaranteed" or "safe" by any means, but that doesn't stop tons of project from using it to safeguard their admin dashboards, etc.
A safe load balancer implementation of the forwarded-for paradigm ensures that the header is wiped for all traffic, and then sets it correctly if the remote ip address is usable. Nobody should be able to set the header besides the load balancer.
- If Lucky always (or even by default) searches for these headers, then it becomes trivial to spoof a request address by simply injecting a header to all requests.
- If Lucky checks multiple headers, then correctly configuring a load balancer becomes more difficult. All possible headers need to be wiped, then the one "safe" header can be set.
- If the application isn't actually deployed behind a load balancer, Lucky should not be checking these headers at all.
Instead of being an array, should it just be the single string value for the header? It would make sense that once you figure out which header is being passed back, it'll always be that same one. I've ran in to an issue where the LB config has changed, but I guess in that case, you'd just update the app to use a different header and do a new deploy. |
@jwoertink I think that makes sense. Should this be an opt-in feature as well? |
@robacarp which part are you thinking for the opt-in? The |
My preference would be to have the whole thing be opt-in, but I understand why that might not be a desirable change to make. There are a bunch of ways an incorrectly configured x-forwarded-for system can be abused, and having a web server which reads the header without the proxy server in front of it is one of them. I spent some time poking at what Rack does for this. A notable difference is that it doesn't return just one value, but an array of values. They take the added precaution of filtering out a default list of IP ranges, too. There's a lot more code in this rack module than I expected. |
Ah, ok. I see what you're saying. In that case, we'd need to remove this Then we could add docs on adding it in if you need. I would eventually like to make it more robust like they do with the Rack stuff. IPSpoofing checks and all that... |
Got sent this article: https://adam-p.ca/blog/2022/03/x-forwarded-for/ One interesting thing is we currently have this line: lucky/src/lucky/remote_ip_handler.cr Line 21 in 4cb2108
which uses the first (left most) IP, but according to this article, we should be using the last (right most)
It also mentions to use the last instances of the header, which I think Crystal is doing..
I think maybe we can include a link to this article in the comments. We make the default |
…the header list as per the linked article in the comments.
Purpose
Fixes #1643
Description
If you need access to the client's IP address, using
remote_address
can be troublesome since it casts toSocket::Address
, and not allSocket::Address
subclasses contain anaddress
method. So you usually end up with some code like this:But now you can just do
In addition, I've also added a new habitat config option
ip_header_names
option. This allows you to configure the header names of where the client IP may come from. e.g.X-Real-Ip
,Forwarded-For
, etc...Checklist
crystal tool format spec src
./script/setup
./script/test