-
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
add basic ip centric access control on routes #442
Conversation
Oops, forgot to link this to #439 |
79d7e03
to
c4469f9
Compare
route/access_rules.go
Outdated
|
||
// currently only one function - more may be added in the future | ||
return t.denyByIP(net.ParseIP(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 have mixed feelings about this section. In the event that fabio is deployed behind another load balancer you would want to check the XFF
header but then what about the actual source? Would it be better to check both the XFF
(if present and different) as well as the actual RemoteAddr
and return true
(deny) if either of those checks return true
on their own?
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, I definitely don't like this. It's an open exploit as is since there is no validation on who is allowed to pass an XFF
header to fabio.
Example with allow=ip:172.16.0.0/12
and a client that is not sourcing from that network.
$ curl -H 'X-Forwarded-For: 172.16.1.1' https://test-site/foo
{}
$ curl https://test-site/foo
access denied
I think it needs to check both XFF
and RemoteAddr
or it's basically useless.
c4469f9
to
02dd9a1
Compare
It seems the tests are randomly failing with a timeout around some consul interaction. I've seen the different go versions succeed and fail at different times with the same code by just amending the commit and doing force push. |
55f1011
to
6d757dd
Compare
6d757dd
to
eb57449
Compare
I've fixed the security hole in the latest commit. |
@magiconair I think that's it unless you have other suggestions or requests. |
I haven't fully nailed down the test failures. I'm punting by running on travis and codeship and if one of them is green I'm ok with that :-/ I'll have a look at the PR later. |
@microadam are you able to test and verify? |
I will see if I can give it a go! Not a go developer, but will give compiling it a shot |
@microadam I could post a binary at my fork of the repo if that makes it easier. Not sure how comfortable you would be with that though ... and I completely understand. |
@leprechau yeah that would definitely work! Hitting lots of errors trying to compile (probably just me not knowing how to do it!) |
Linux amd64? |
Perfect! |
Have managed to compile it! will try and test it out |
Okay, awesome ... let me know if you need anything else. |
I assume I must be doing something wrong, but with options of I am on Mac OSX running fabio on a Vagrant box, so there are a few networks to contend with |
Interesting ... can you turn on access logging just to see what IP fabio sees your connection coming from? There is probably a NAT between you and the vbox host. To be really sure you could switch it to |
No joy with the deny all either. I can only assume I have compiled it wrong. Perhaps if you could provide a binary after all? Steps to compile were:
I assume something in there is not quite right! (first time actually trying to compile a GO project...) |
Think I have it compiled correctly now. Getting this error:
My EDIT: Im a muppet, should have been |
@microadam and @leprechau thanks to both of you for working on this! |
@microadam np, thanks for the testing! There are some markdown docs in the commit but I think you have the syntax right now. |
Also, just realized my own example up there was wrong ... sorry about that. At least the error message works 😂 |
Finally got around to doing some tests, apologies for the delay! A few things I have spotted, some are possibly edge cases and not worth worrying about (possibly all).
However it then defaults to allowing all traffic through. As we don't have any concept of "validation" before the server runs, perhaps this should prevent the server from running, or at least, if its an Seems to work in all the other trivial tests I have done! I haven't managed to test it behind another proxy yet, as I don't have that easily setup, but from the looks of the code it should handle that! Thanks a lot for doing this :) |
Thanks for the feedback. Internally the code is using the golang core https://golang.org/pkg/net/#ParseCIDR They support both IPv4 and IPv6 but will not transit boundaries. So, for the first example with The second part is also a side affect of the ParseCIDR function. To stopping server operation and/or ignoring the route I have mixed feelings. The current behavior for invalid options adds the route but just ignores the bad options. I kept his same behavior for the |
Sure. That all makes sense.
Just trying to think if there is a way to make it really obvious that it has failed without checking the logs. There isn’t really a clean way of doing it I suppose. I guess if you do mess it up and don’t notice the logs, you will just not see the change you were expecting I.e person still won’t have access or the traffic you are trying to block will still come through. I guess that is the best that can be done!
Definitely a nice addition to this project, certainly covers my use case :)
… On 13 Feb 2018, at 18:14, Aaron Hurt ***@***.***> wrote:
@microadam are you able to test and verify?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
Awesome, thank you for the feedback and testing. @magiconair any other thoughts or concerns? What do you think about skipping the route on failure for allow/deny processing? Just putting a |
I’ll have a look at the code later after coffee. One thought I had about the addresses is whether to automatically change 1.2.3.4 to 1.2.3.4/32. Also, could we add an Can you do |
You could check for a string contains and add the I’m not sure an I tried to explain that in the added documentation as well. |
I added some code to add a |
Should we do this for v6 addresses without netmask as well?
—
Frank Schröder
… On 15. Feb 2018, at 14:22, Aaron Hurt ***@***.***> wrote:
I added some code to add a /32 prefix to value strings that do not contain a / ... invalid or incomplete addresses will still error out in the ParseCIDR function.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
That's a good question ... would require a bit more pre-parsing but I think it's doable and probably good to be consistent. |
Latest commit should properly add a |
Last commit added equal test coverage for IPv4 and IPv6 addresses. |
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 we add a simple integration test as well which sets the appropriate header to spoof the source ip?
docs/content/cfg/_index.md
Outdated
`register=name` | Register fabio as new service `name`. Useful for registering hostnames for host specific routes. | ||
Option | Description | ||
------------------------------------------ | ----------- | ||
`allow=ip:10.0.0.0/8,ip:192.168.0.0/24` | Restrict access to source addresses within the `10.0.0.0/8` or `192.168.0.0/24` CIDR mask. All other requests will be 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.
Pls add an IPv6 example as well
|
||
fabio supports basic ip centric access control per route. You may | ||
specify one of `allow` or `deny` options per route to control access. | ||
Currently only source ip control is available. |
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.
Pls add an ipv6 example and explain how the source ip is determined. Also add a comment that /32
and /128
is added automatically.
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.
done!
@@ -84,6 +84,11 @@ func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
return | |||
} | |||
|
|||
if t.AccessDeniedHTTP(r) { | |||
http.Error(w, "access denied", http.StatusForbidden) |
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'm pretty sure that someone will ask for a different status code here but we can leave it for now.
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 can see that being a request but not sure there is much more appropraite than forbidden for a denied request. Would you want that to be a config option at some point?
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.
yeah, if someone asks for this.
proxy/tcp/sni_proxy.go
Outdated
@@ -78,6 +78,11 @@ func (p *SNIProxy) ServeTCP(in net.Conn) error { | |||
} | |||
addr := t.URL.Host | |||
|
|||
if t.AccessDeniedTCP(in) { | |||
log.Print("[INFO] route rules denied access to ", t.URL.String()) |
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 should also log who was denied access.
proxy/tcp/tcp_proxy.go
Outdated
@@ -48,6 +48,11 @@ func (p *Proxy) ServeTCP(in net.Conn) error { | |||
} | |||
addr := t.URL.Host | |||
|
|||
if t.AccessDeniedTCP(in) { | |||
log.Print("[INFO] route rules denied access to ", t.URL.String()) |
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.
same here
I had to export the I also moved the deny logging into the |
@@ -84,6 +84,11 @@ func (p *HTTPProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
return | |||
} | |||
|
|||
if t.AccessDeniedHTTP(r) { | |||
http.Error(w, "access denied", http.StatusForbidden) |
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.
yeah, if someone asks for this.
The source ip used for validation against the defined ruleset is | ||
taken from information available in the request. | ||
|
||
For `HTTP` requests the client `RemoteAddr` is always validated |
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 think if the inbound connection uses the PROXY protocol then this is mapped into the RemoteAddr
field of the underlying connection. This should be the case for both TCP and HTTP since this is on the connection itself.
I'll update the docs before merging.
hrmpf, I've merged this but put the additional commit into the branch. This way github won't auto-close the PR ... This is on master now. Thanks a lot for @leprechau and @microadam for working on this! |
I've verified and tested this locally with good results. Running it in our development environment for testing as well.