-
Notifications
You must be signed in to change notification settings - Fork 617
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
handle indeterminate length proxy chains - fixes #449 #453
Conversation
@magiconair What are your thoughts on this as a solution for handling layered proxies? |
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.
This looks sane but I want to let the problem sink in a bit more. Both of you have discussed this in detail and I don't think there is anything to add right now.
As a general rule, Fabio should do the 'right thing' if possible and should pick the 'right' default if there are multiple options.
If I can't think of a glaring omission I'll merge this.
route/access_rules.go
Outdated
// ensure we only get the ip string | ||
xip = strings.TrimSpace(xip) | ||
// only continue if xip differs from host | ||
if xip != host { |
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.
I'd prefer not to nest the ifs.
if xip == host {
continue
}
if ip = net.ParseIP(xip); ip == nil {
log.Printf("[WARN] failed to parse xff address %s", xip)
continue
}
if t.denyByIP(ip) {
return true
}
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.
Can do.
I think this is the right approach. The chain of ips you have to validate is For this to work you have to trust a chain of proxies in front of you. The end of your trust chain contains the ip address of the inbound connection you care about and it cannot be spoofed. If someone sends a bogus xff header from an ip that is not in the list then this ip will be added to the xff header by your trusted proxy and the other trusted proxies in front of you will pass on that information to fabio which will see the ip and deny access. |
continue | ||
} | ||
if ip = net.ParseIP(xip); ip == nil { | ||
log.Printf("[WARN] failed to parse xff address %s", xip) |
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.
don't you need to continue here?
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.
The behavior of denyByIP
is to immediately return false when passed a nil
... so it is effectively doing a continue. However, I can see the point that adding a continue would be more explicit and be more future proof should additional code be added between this statement and the denyByIP
call.
With that in mind I wouldn't call this a work around. This is how this should work I think |
341b488
to
a1d7b54
Compare
I updated the commit description and PR title to reflect the discussion. |
Yes, exactly which is why I settled on validating all the elements contained within the XFF header. If some client wants to be clever and inject a bogus XFF header, including a header of an allowed netblock, they will still be denied if any member of the header fails to match. |
@magiconair anything else that is still needed with this one? |
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.
Two small comments and then we're good. In the test itself you're not handling the error from url.Parse
. In picker_test.go
there is a mustParse
function which panics which you could use.
target: &Target{ | ||
Opts: map[string]string{"allow": "ip:10.0.0.0/8,ip:192.168.0.0/24"}, | ||
}, | ||
xff: "1.1.1.2, 10.11.12.13, 10.11.12.14", | ||
xff: "10.11.12.13, 1.1.1.2, 10.11.12.14", |
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.
Is there a reason the order changed?
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 particularly, the result is the same regardless of the order. I can’t remember why it was changed.
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.
Ohh, the old code only checked the first element. I moved it to ensure it was looping over the elements and still denying on a middle element.
@@ -183,13 +183,13 @@ func TestAccessRules_AccessDeniedHTTP(t *testing.T) { | |||
denied: true, | |||
}, | |||
{ | |||
desc: "allowed xff and allowed remote addr", | |||
desc: "single allowed xff and allowed remote addr", |
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.
This comment doesn't match the result since the behavior changed. Can you double check?
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.
Only a single element of the XFF is allowed. The others are denied.
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 the old code this was allowed as it only checked the first element.
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.
Ah, I thought that because the XFF address and the remote address are allowed the request should be allowed. But 1.2.3.4 isn't whitelisted. This wasn't obvious to me. I'll clean up the comments after the merge.
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.
Yes, that was part of the new behavior. If any one of the addresses presented in the XFF fail to match an allow/whitelist rule the request is denied. If any one of the addresses match a deny/blacklist rule the request is denied.
No description provided.