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
feature: support requests forwarding #918
Conversation
Thanks for the PR! |
@Traumeel can you also add user documentation in |
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.
PR: code looks good, tests added, developer docs, too
Thanks for the nice PR. I would like to test it before merge.
This extends |
thanks a lot for the nice PR! |
👍 |
@grassator right, dynamic backends would be a nice addition. Though I would still distinguish the backends from the filters in the syntax. Anyway, a POC is always welcome 😄 |
@grassator this discussion #556 is also related to the dynamic backends. |
@aryszka sorry, updated PR. Trying to handle missing scheme case. |
@Traumeel I tried to make https work, but it does not work. It is doing a |
@szuecs let's merge, covers my use case |
Why does the scheme for forwarding need to be set separately at a global level? As I see it, this is a very basic forward proxy scenario being baked into what has so far been a reverse proxy. The fundamental difference with the request meant for a forward proxy is that the RequestURI will contain the full target URI including scheme and host while in normal cases, only path will be present. So, both target host and scheme should be obtained from RequestURI and used to make the forward request. Also, I don't see the utility of handling "CONNECT" to enable tunneling even in future. The main USP of skipper is being able to conditionally modify request/responses in the process of routing. With tunneling, that ability will be removed as all traffic passing through will be encrypted with skipper only maintaining a tunnel. |
@UltraNemesis I agree that skipper’s main purpose and power is reverse proxy, but we also support upgrade headers, such that you can use websocket or spdy (kubectl exec/attach/log with audit logging). Do you see something wrong or missing? |
@szuecs What you say about CONNECT makes perfect sense from that perspective. In fact, if skipper is going be usable as a forward proxy, I think you should also think about having an MITM mode for this as an option so that requests can be intercepted and predicates/filters can be applied before forwarding even in the HTTPS mode. What I don't still understand is the need for the default scheme flag. The RequestURI will have full URI including scheme and in any case, without CONNECT handling, all requests will have to be HTTP. Also, with regard to my own use case, my scenario about was about being able to select the target host from a filter based on headers or body while the requests are still made with skipper as a reverse proxy. I had setup something like this at my work using an F5 BIG IP iRule and evaluating whether I can replace it with skipper and a custom filter. The addition of the "forward" backend type makes this easier, but what we will still need to have is a method like SetTargetHost in the filter context that can be used to set a target host for a request and skipper should make the request to that host. |
@UltraNemesis The following should work with
With the use of Answering inline:
Sounds good to me, CONNECT does not need to be only TCP pass through.
The problem is that the RequestURI only contains path and query, there is no Scheme if you get a Request object passed.
Can you explain your use case in a separate issue?
-> |
@szuecs : The problem in the code you posted is that you are not making a forward proxy compatible HTTP request. Try setting the server as forward proxy
I recommend reading through this https://parsiya.net/blog/2016-07-28-thick-client-proxying---part-6-how-https-proxies-work/ When you make the request to a forward proxy, the specs require that the full Request URI including the scheme is sent instead of just relative URI like in normal cases. Here is what the dump looks like Direct Request
Proxy Request
If skipper is going to act as a forward proxy with this implementation, it should go with the established standards. |
Here is code including a fully functional forward proxy implementation
|
@UltraNemesis thx for the nice read.
So this should hold true for a proxy aware client, in case it is not URIs will be relative. If it is the case we need to reconstruct it. Going to change the PR to handle it |
@Traumeel Yes, a properly implemented forward proxy aware client will always send the request in that format. You can count on it being that way. To be honest, you don't really need to read the scheme at all. Any forward proxy aware client will send a CONNECT request to establish a tunnel if the target URL scheme is HTTPS . So, if you get anything other than a CONNECT, it will be HTTP. |
So, by default forwarding will work for HTTP only. To access HTTPS URLs |
Then, it is no longer a proper forward or reverse proxy scenario. If you configure skipper as a forward proxy, A forward proxy aware client will automatically send a CONNECT request for HTTPS URLs. So, if you have HTTPS endpoint, you will have to manually change the request to HTTP first at the client side before sending it to skipper just so that skipper will accept it then change it HTTPS based on the flag. Further more, you have no control as its a global scheme. It is no good as a reverse proxy case either as you would need to construct a custom HTTP request with Host header set to actual target rather than skipper and then send it to skipper. Such an implementation would screw up any chance to support SNI and Virtual Host based TLS in skipper in future. So, I highly recommend sticking to the specs for out of the box behavior be it as a forward proxy ore reverse proxy. If any custom/overriding behavior like this is required, provision for that should be made as part of the filters framework. In fact, I too have a pretty similar custom requirement that would fit on top of this. In my case, I want to make a request to skipper and I want to decide based on headers or body content which destination I want to send it to. Only difference from your case is that you just want to modify the scheme in the URL rather than whole URI. This is best fit into the filter system. Have a method in filter context which allows setting the Target Backend for the request. |
@UltraNemesis as far as I get @Traumeel, the use case is to have a "malicious" DNS server that responds to all lookups to point to skipper, so clients are not aware to have a proxy MITM. There are multiple options here:
|
I tend to agree with @UltraNemesis that following the standards as the default is more future proof. As I understand, @Traumeel 's use case, it would be required to accept HTTP requests and forward them to HTTPS. Is this correct? For the backend host, I would consider something like this:
For the scheme, I would avoid using a global default scheme, instead I would create a filter that overrides the scheme of the incoming request:
@Traumeel would this approach work for your use case? |
Correct.
The setups works nicely for a predefined set of hosts, but it is cumbersome to update it with new hosts.
If I understand this correctly Thus I was thinking to go with extending @aryszka @szuecs What do you think? This will solve my use case and @UltraNemesis The functionality can optionally be enabled for only a special backend type e.g. |
After an offline discussion with @Traumeel I have the following idea: What do we need to do the call to do the
Wit this implementation you can even write your custom filter and lookup a database to change the final request being made to an arbitrary chosen backend. You also do not need Any drawbacks with this you see @aryszka @UltraNemesis ? |
@szuecs where would be the backend host taken from in this case? From the Host header? |
eskip/doc.go
Outdated
|
||
A network endpoint address example: | ||
HTTP endpoint: |
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 would not change this, because it highlights more the "network", that maps in the code to NetworkBackend
for example and this document is meant for library users that want to understand the code.
filters/filtertest/filtertest.go
Outdated
"github.com/opentracing/opentracing-go" | ||
"github.com/zalando/skipper/filters" | ||
"net/http" |
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.
please drop this change
@Traumeel the filters you implemented should also be registered in |
Signed-off-by: Maksym Aryefyev <maksym.aryefyev@zalando.de>
@szuecs new filters are registered in |
@UltraNemesis maybe you want to check this again, if it's ok for you? |
👍 |
@Traumeel thanks a lot for the PR |
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.
some small changes needed
} | ||
} | ||
|
||
func (f *dynamicBackendFilter) Response(ctx filters.FilterContext) {} |
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.
you don't need the names only the types in an empty function
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.
Do we need to define so many state bag keys? I would say that we should have only one key for setting Backend Url and all other filters should just set this value accordingly.
To set scheme, all you need to do is take current backend url, change the scheme and set the new value back to backend url.
To take backend url form host, you read its value and set the value to Backend url key.
Do we need to define so many state bag keys? I would say that we should have only one key for setting Backend Url and all other filters should just map and set this value accordingly. To set scheme, all you need to do is take current backend url, change the scheme and set the new value back to backend url. To take backend url from host, you read its value and set the value to Backend url key. Every thing can be handled through a single key. |
@aryszka you are welcome :) It almost got through |
about the two statebag keys: @Traumeel @UltraNemesis , I don't have a strong opinion on it, I find both ways fine. |
👍 |
Thanks to all reviewers and @Traumeel for this PR ! |
The #1860 pointed out to a bug golang/go#48362 that leads to false positive testable example due to trailing comment. Moreover #918 changed test input without changing the output and that went unnoticed due to this bug. This change removes trailing comment to fix the example. It also removes the test input instead of adding correct output because the input does not match the final implemenation and thus is misleading. Fixes #1860 Signed-off-by: Alexander Yastrebov <alexander.yastrebov@zalando.de>
The #1860 pointed out a bug golang/go#48362 that leads to the false positive testable example caused by a trailing comment. Moreover #918 changed the test input without changing the output and that went unnoticed due to this bug. This change removes trailing comment to fix the example. It also removes the test input instead of adding a correct output because the input syntax is incorrect therefore misleading. Fixes #1860 Signed-off-by: Alexander Yastrebov <alexander.yastrebov@zalando.de>
The #1860 pointed out a bug golang/go#48362 that leads to the false positive testable example caused by a trailing comment. Moreover #918 changed the test input without changing the output and that went unnoticed due to this bug. This change removes trailing comment to fix the example. It also removes the test input instead of adding a correct output because the input syntax is incorrect therefore misleading. Fixes #1860 Signed-off-by: Alexander Yastrebov <alexander.yastrebov@zalando.de>
Introduces requests forwarding with a new backend
<forward>
Example usage:
* -> <forward>
* -> setRequestHeader("X-Passed-Skipper", "true") -> <forward>;
Fixes #908
TCP tunnelling is not covered.