Bug: registerWebInterface option 'ignoreTrailingSlash' causes redirect to absolute URL, breaking reverse-proxies
vnayar opened this issue · 4 comments
Lines 216 to 228 in fee3872
When an HTTP GET request arrives, e.g. "/configs" that is missing a trailing slash, the code above causes this to be a redirect to "/configs/". However, because the redirect URL is an absolute URL, this causes errors when the server is behind a reverse proxy.
For example:
HTTP GET [browser] https://company.com/configs ==> [proxy] ==> http://service1:8080/configs
[browser] http://service1:8080/configs/ <== [proxy] <== Status 302 :: location -> http://service1:8080/configs/
HTTP GET [browser] http://service1:8080/configs/ ==> [nowhere]
Either, the redirect should use a relative URL, or the redirect step should be entirely skipped and the WebInterface function should simply be called directly.
This problem only seems to occur when a @path
annotation is on both the class itself and the method, e.g.:
@path("/web/checkout")
class CheckoutController {
// Use the /web path so that it does not strictly require JWT before being loaded.
@path("/") // The same thing happens with @path("")
void getCheckout() {
string priceLookupKey = "funnel-service-standard";
render!("checkout.dt", priceLookupKey);
}
If the reverse proxy setup sets the X-Forwarded-For
, X-Forwarded-Port
and X-Forwarded-Proto
headers, the HTTP server will use them to determine the canonical URL of the request. The redirect location should then be correct. But I think it should also be possible to change the implementation of redirect
to attempt to construct a relative path instead of a full URL in cases where that is possible.
Interesting.
I happen to be using Envoy Proxy, and it seems to split the information into a few headers:
- origin :: This contains the original scheme, host, and port, e.g. "http://localhost:8180"
- referer :: This contains the URL that led to the request, e.g. "http://localhost:8180/web/checkout"
- host :: This contains only the host and port, without the scheme, e.g. "localhost:8180"
- x-forwarded-proto :: The scheme used in forwarding, e.g. "http".
According to the docs I can find (https://www.envoyproxy.io/docs/envoy/v1.24.1/configuration/http/http_conn_man/headers#x-forwarded-for), the x-forwarded-for
header is used in the case of a forward proxy, when the proxy is the client of a request you are receiving that came from someone behind a proxy, but I don't think it's present in the reverse-proxy situation, where your proxy is the server rather the client. But perhaps I need to enable the use_remote_address
option and see if that fixes it.
I wasn't aware that vibe could make use of these headers, let me dig further and make sure they are there if possible.
I enabled the use_remote_address
and tried it with my local server. The x-forwarded-for
header is now present, but it merely contains an IP address, which I believe is for the original client rather than the reverse proxy, e.g. 'x-forwarded-for', '192.168.2.33'
. In this case, my reverse proxy is running on port 8180 and forwarding to port 8082, where the target server is running. The response returned by the server looks like this:
vnayar@krootox:admin$ curl -D - -s -o /dev/null http://localhost:8180/admin/checkout/ -H "Authorization: Bearer $ACCESS_TOKEN"
HTTP/1.1 302 Found
server: envoy
date: Fri, 13 Jan 2023 18:05:54 GMT
location: http://localhost:8082/admin/checkout
While debugging, the only information the server running on port 8082 receives, that even hints that the reverse proxy is on 8180 is via the host
header (the origin
header seems to vanish when I enable the use_remote_address
setting.)