Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nginx reverse proxy subdirectory rewrites for sourcegraph

I'm trying to have a self hosted sourcegraph server being served on a subdirectory of my domain using a reverse proxy to add an SSL cert.

The target is to have http://example.org/source serve the sourcegraph server

My rewrites and reverse proxy look like this:

  location /source {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Scheme $scheme;

    rewrite ^/source/?(.*) /$1 break;
    proxy_pass http://localhost:8108;
  }

The problem I am having is that upon calling http://example.org/source I get redirected to http://example.org/sign-in?returnTo=%2F

Is there a way to rewrite the response of sourcegraph to the correct subdirectory?

Additionally, where can I debug the rewrite directive? I would like to follow the changes it does to understand it better.

-- Edit:

I know my approach is probably wrong using rewrite and I'm trying the sub_filter module right now.

I captured the response of sourcegraph using tcpdump and analyzed using wireshark so I am at:

GET /sourcegraph/ HTTP/1.0
Host: 127.0.0.1:8108
Connection: close
Upgrade-Insecure-Requests: 1
DNT: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: https://example.org/
Accept-Encoding: gzip, deflate, br
Accept-Language: de,en-US;q=0.9,en;q=0.8
Cookie: sidebar_collapsed=false; 

HTTP/1.0 302 Found
Cache-Control: no-cache, max-age=0
Content-Type: text/html; charset=utf-8
Location: /sign-in?returnTo=%2Fsourcegraph%2F
Strict-Transport-Security: max-age=31536000
Vary: Cookie
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-Trace: #tracer-not-enabled
X-Xss-Protection: 1; mode=block
Date: Sat, 07 Jul 2018 13:59:06 GMT
Content-Length: 58

<a href="/sign-in?returnTo=%2Fsourcegraph%2F">Found</a>.
like image 263
Cookie Avatar asked Jul 07 '18 12:07

Cookie


2 Answers

Using rewrite here causes extra processing overhead and is totally unnecessary.

proxy_pass works like this:

proxy_pass to a naked url, i.e. nothing at all after domain/ip/port and the full client request uri gets added to the end and passed to the proxy.

Add anything, even just a slash to the proxy_pass and whatever you add replaces the part of the client request uri which matches the uri of that location block.

so if you want to lose the source part of your client request it needs to look like this:

location /source/ {
    proxy_pass http://localhost:8108/;
    .....
}

Now requests will be proxied like this:

example.com/source/ -> localhost:8108/

example.com/source/files/file.txt -> localhost:8108/files/file.txt

It's important to point out that Nginx isn't just dropping /source/ from the request, it's substituting my entire proxy_pass URI, It's not as clear when that's just a trailing slash, so to better illustrate if we change proxy_pass to this:

proxy_pass http://localhost:8108/graph/; then the requests are now processed like this:

example.com/source/ -> localhost:8108/graph/

example.com/source/files/file.txt -> localhost:8108/graph/files/file.txt

If you are wondering what happens if someone requests example.com/source this works providing you have not set the merge_slashes directive to off as Nginx will add the trailing / to proxied requests.

like image 114
miknik Avatar answered Oct 23 '22 00:10

miknik


If you have Nginx in front of another webserver that's running on port 8108 and serve its content by proxy_pass of everything from a subdir, e.g. /subdir, then you might have the issue that the service at port 8108 serves an HTML page that includes resources, calls its own APIs, etc. based on absolute URL's. These calls will omit the /subdir prefix, thus they won't be routed to the service at port 8108 by nginx.

One solution is to make the webserver at port 8108 serve HTML that includes the base href attribute, e.g

<head>
  <base href="https://example.com/subdir">
</head>

which tells a client that all links are relative to that path (see https://www.w3schools.com/tags/att_base_href.asp)

Sometimes this is not an option though - maybe the webserver is something you just spin up provided by an external docker image, or maybe you just don't see a reason why you should need to tamper with a service that runs perfectly as a standalone. A solution that only requires changes to the nginx in front is to use the Referer header to determine if the request was initiated by a resource located at /subdir. If that is the case, you can rewrite the request to be prefixed with /subdir and then redirect the client to that location:

location / {
    if ($http_referer = "https://example.com/subdir/") {
        rewrite ^/(.*) https://example.com/subdir/$1 redirect;
    }

    ...
}

location /subdir/ {
    proxy_pass http://localhost:8108/;
}

Or something like this, if you prefer a regex to let you omit the hostname:

if ($http_referer ~ "^https?://[^/]+/subdir/") {
    rewrite ^/(.*) https://$http_host/subdir/$1 redirect;
}
like image 11
Kent Munthe Caspersen Avatar answered Oct 23 '22 01:10

Kent Munthe Caspersen