Search code examples
reverse-proxynginx-location

Using nginx regex location matching to dynamically map URI's to different ports for multiple reverse proxies


I'm using nginx to create a reverse proxy to a web app that may be behind a firewall. For my initial proof of concept I used the following location block to ensure it worked.

    location / {
            proxy_pass https://localhost:2222;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
    }

I open up my reverse tunnel on my web app with ssh -f -N -T -R2222:localhost:443 user@nginx-ip. This works exactly as I like. I type in my nginx-ip into my browser and I get the https traffic from my web app but obfuscated by the nginx-ip, etc.

I want to allow potentially a few thousand reverse tunnels though across a few thousand ports (instead of just 2222 in the above case). Reading up on nginx, I thought to use regular expressions to dynamically use a URI containing the port number to proxy_pass to that specific port.

That is, I'd like https://nginx-ip/2222/ to proxy_pass https://localhost:2222; and I'd like https://nginx-ip/1111/ to proxy_pass https://localhost:1111;.

I've tried quite a few variations, but as far as I've been able to reason, I've landed on thinking this should work:

    location ~* ^/(\d+) {
            proxy_pass https://localhost:$1;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
    }

It doesn't. I get a 502 Bad Gateway in my browser and the error log gives me:

2016/05/31 21:02:36 [error] 6188#0: *3584 no resolver defined to resolve localhost, client: {my-client-ip}, server: localhost, request: "GET /2222/ HTTP/1.1", host: "{nginx-ip}"

When I use 127.0.0.1 instead of localhost I get a 404. The webpage says

Not Found The requested URL /2222/ was not found on this server.

Is what I'm attempting possible with nginx configuration?

To reiterate, I would like to be able to initiate many (thousands) unique reverse tunnels through an nginx web server. My initial thought was to vary the outgoing ports of the nginx server based on what other web app I want to proxy through, and to assign the request to my nginx server to a different port by a port in the URI (extracting it via regex).


Solution

  • To solve my problem, and with the help of @Cirdec I ended up going a different direction.

    Instead of using path in the URI to reference port, I was successful using the port as a subdomain. That is, 1111.my-host-name will send things through the reverse proxy on 127.0.0.1 (localhost) on port 1111. This requires enlisting some DNS wildcarding to help with some of the heavy listing (of note: wildcard DNS matching in /etc/hosts will not work, but you can hard code a few entries to test that it works.

    Excerpt from my /etc/hosts file:

    nginx-box-ip-address     2222.my-host-name
    nginx-box-ip-address     1111.my-host-name
    

    This paired with nginx config:

    server {
        listen 443;
        server_name ~^(?<port_subdomain>[0-9]*).my-host-name$;
    
        # NOTE: omitted extra ssl configuration lines
    
        location / {
                proxy_pass https://127.0.0.1:$port_subdomain;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $remote_addr;
        }
    }
    

    Now I can have my web app open up reverse proxy tunnels to arbitrary ports on my nginx box (my-host-name) and reach them through a subdomain.

    An step by step example:

    • On one of my web app boxes, I open up a reverse tunnel to port 2222 on my nginx box (my-host-name) with ssh -f -N -T -R2222:localhost:443 user@my-host-name
      • On a different web app box I can open up a different tunnel to the same nginx box (my-host-name), say port 1111 by running ssh -f -N -T -R1111:localhost:443 user@my-host-name on the different web app box
    • On my nginx box, I have loaded the config above. I open up chrome and hit 2222.my-host-name and it is as if I'm directly hitting the URL/IP address of my web app box (only it can be behind a firewall thanks to the reverse proxy). Hitting 1111.my-host-name takes me to the other web app box, that is completely separate and through a completely separate tunnel.