1

I want to allow an authenticated client in Express to access to other web applications that are running on the server, but on different ports.

For example, I have express running on http://myDomain and I have another application running on say port 9000. I want to be able to reach the other app through http://myDomain/proxy/9000.

I had a little bit of success using node-http-proxy, for example:

function(req, res) {
  var stripped = req.url.split('/proxy')[1];
  var path = stripped.split('/');
  var port = path.shift();
  var url = path.join('/');

  req.url = url;
  proxy.web(req, res, {
    target: 'http://127.0.0.1:' + port
  });
}

However, the big problem is that when the web app makes GET requests, such as for /js/lib.js, it resolves to http://myDomain/js/lib.js, which is problematic because express is not aware of those assets. The correct request would be to http://myDomain/proxy/9000/js/lib.js. How do I route all these additional requests?

user1027169
  • 2,627
  • 3
  • 27
  • 50
  • In your example, should everything in the /js/ directory be authenticated? – Dan Hlavenka Mar 14 '14 at 20:12
  • Instead of changing the proxy, can you change the other application to produce proxy-friendly URLs? – Matthew Bakaitis Mar 14 '14 at 20:15
  • @Dan, everything in the /js/ directory should also be authenticated. – user1027169 Mar 15 '14 at 07:05
  • @MattBakaitis, that wouldn't be doable since I don't have direct control over these other applications, I'd just like to authenticate users and direct them to where they should go. – user1027169 Mar 15 '14 at 07:06
  • Rather than using something like `/proxy/9000`, why not just route all requests to `/js/*` to port 9000? – Dan Hlavenka Mar 15 '14 at 15:27
  • @Dan, the main issue with that approach is that I actually have multiple instances of the application running, one for each user, so there are multiple ports that need to be proxied. User A gets port 9000, User B gets port 9001, etc. – user1027169 Mar 15 '14 at 16:22
  • How is your application supposed to tell which user should use which port? Do you set a cookie or something? – Dan Hlavenka Mar 15 '14 at 17:13
  • When a new user session is created, a new port is opened up with a new instance of the internal application, and this is sent back to the client in a cookie. For example, if port 9000 is opened, right now the user gets an iframe that goes to `http://mydomain:9000`, but the problem with that is security. I don't keep any of the authentication that was already done, which is my reason to try to proxy it through a route on the main server, e.g. `http://mydomain/proxy/9000`. This way, I can keep track on main server who can access which internal apps. – user1027169 Mar 15 '14 at 17:53

2 Answers2

1

I don't think http://myDomain/proxy/9000 is the correct way to do it. Web pages are going to assume the site's domain to be just myDomain and not myDomain/proxy/9000, because that is what the standard says.

Your use case would be better served by using subdomains like 9000.proxy.myDomain.

Pritam Baral
  • 475
  • 2
  • 7
  • For my use case, new ports are opened up dynamically, so I don't think it would be feasible to generate new subdomains in time. – user1027169 Mar 14 '14 at 20:07
  • You must think "generating" subdomains is more difficult than using a new subfolder. It doesn't have to be. Whatever rules applied to your subfolder set-up before, apply to a subdomain set-up too. If you're worried about registering new subdomains with DNS, use a wildcard like `*.proxy.myDomain`. – Pritam Baral Mar 14 '14 at 20:12
  • Why is your application binding to ports dynamically as it's running? That sounds like an aspect that might be worth reconsidering. If not, it could at least help us understand what it is you really need. – Dan Hlavenka Mar 15 '14 at 03:19
  • The specific application creates new Docker containers depending on the number of users. Each Docker container has different web services that will be opened when the are created and then I would like to direct users to their appropriate containers. – user1027169 Mar 15 '14 at 07:07
  • Another issue is keeping authentication, is there a way to keep pages authenticated when redirecting to a subdomain in express? – user1027169 Mar 19 '14 at 01:29
1

What you need to do is to replace URLs in the initial page with the new URL pattern. What is happening is that the initial page that your reverse proxy returns has a reference to:
/js/lib.js or http://myDomain/js/lib.js
so when the browser makes a second request it has the wrong pattern for your reverse proxy.

Based on the incoming request you know what the pattern should look like. In your example it's http://myDomain/proxy/9000. You then fetch the appropriate page from the other server running on http://127.0.0.1:9000/. You do a string replace on any resources in that file. You'll need to experiment with the pattern but you might look for 'script src="/' or 'href="/' and you might find regex helps with the pattern if, for example, the src attribute isn't the first listed in a script tag.

For example you might find 'scr="/' and then you replace it with 'src="/proxy/9000/' that way when the browser asks for that local resource it will come through with the port that you're looking for. This is going to need experimentation and it's a great algorithm to write unit testing around to get perfect.

Once you've done the replacement you just stream that page to the client. res.send() will do this for you.

Something else that you might find useful is that ExpressJS gives you a way to pull out the port number with a little less hassle than you're doing. Take a look at this example:

app.get('/proxy/:port', function(req, res){
    console.log('port is ' + req.params.port);
});
Guy
  • 65,082
  • 97
  • 254
  • 325
  • Thanks for your response! I have mostly been able to implement this. One thing I am stumped on is do this for a websocket connection. Here is an example of one of the apps that I want to proxy, IPython Notebook (https://github.com/ipython/ipython/blob/master/IPython/html/static/services/kernels/js/kernel.js#L129), which starts initiates the ws connection pointing to location.host, but it would need to point to myDomain/proxy/9000 instead. The code the app serves is minified, so it would be tough to attempt to try to do a rewrite the line of javascript that starts the connection. – user1027169 Mar 19 '14 at 05:53
  • 1
    Can you fork that and use your forked version? If so, you can pass in a second parameter to that function (in addition to the json) which is the proxy host name. An alternative: Can you change location.host's value before this function is called? – Guy Mar 20 '14 at 14:37