NodeSSPI icon indicating copy to clipboard operation
NodeSSPI copied to clipboard

adding NodeSSPI to a proxy layer

Open FortressDave opened this issue 7 years ago • 4 comments

I have a NodeJS based application (Express) that runs as a proxy on port 80 to other NodeJS applications on the same server (different ports). We have a need to supply user names to the downstream applications so we installed NodeSSPI in the proxy layer and we pull the user name and add it to the header before we proxy the request.

This works fine most of the time but we get 401 responses from the proxy layer some times, typically if a browser is left open and a post is the first action.

Is this a known issue with NodeSSPI? Is it not recommended within a proxy?

FortressDave avatar Jan 16 '18 21:01 FortressDave

Did you set authoritative option to false? Does 401 response contain WWW-Authenticate header?

abbr avatar Jan 17 '18 04:01 abbr

The authoritative option is set to true, should it rather be false in this case since the requests are proxy forward?

When it fails the handshake appears to happen normally only on the final call the message from the server is the 401 (expecting 200) which does have the WWW-Authenticate header but no tokens.

FortressDave avatar Jan 17 '18 17:01 FortressDave

Authoritative option should be either absent or set to true. I think 401 is sent by NodeSSPI. Request is not passed to backend. If you do the HTTP post as initial request, do you get the same error?

abbr avatar Jan 18 '18 04:01 abbr

Okay, I've had that setting absent so it has been defaulting to true. I've verified that the NodeSSPI layer is delivering the 401, the backend application does not receive the request. I am able to reproduce the issue by accessing the site then allowing the browser to sit open for some time then clicking the button which sends the post request. Closing the authentication popup without entering credentials and clicking the post button again will often work (sometimes not).

Also, when the 401 occurs it does take about a minute to come back with the 401 error. Using Fiddler I can see the initial two 401s when the browser and server are performing the hand shake, the problem is that the final response is a 401 rather than the expected 200.

I am a bit lost on how to solve this, is there a way to turn on verbose logging in NodeSSPI so I can see what the communication with the domain is doing? I am currently having my users by pass the proxy layer and go directly to the site which employees NodeSSPI if the authentication headers are not already set by the proxy.

Check out my app.js file for the proxy server below. I did an experiment between NodeSSPI and express-ntlm and both providers had the same symptom which is leading me to believe that I am missing something in my proxy set up:

var httpProxy = require('http-proxy'); var express = require('express'); var nodeSSPI = require('node-sspi') var ntlm = require('express-ntlm'); var app = express();

var proxy = httpProxy.createProxyServer({});

proxy.on('proxyReq', function(proxyReq, req, res, options) {

 try{

  var userData = [];
  if (req.connection.user)
    userData = req.connection.user.split('\\');
  else
    userData = [req.ntlm.DomainName, req.ntlm.UserName];
  proxyReq.setHeader('ntlm-proxy-username', userData[1]);
  proxyReq.setHeader('ntlm-proxy-domain', userData[0]);
  proxyReq.setHeader('ntlm-proxy-workstation', "NA");
  proxyReq.setHeader('connection', 'keep-alive');

} catch(ex){ console.log((new Date()).toLocaleString() + " - error setting headers"); res.sendStatus(500); }

});

proxy.on('error', function(proxyReq, req, res, options) { console.log("General Proxy Error"); res.sendStatus(500); });

app.use(function(req, res, next){

try{ console.log((new Date()).toLocaleString() + " - starting authentication");

if (hosts[req.hostname]){
  req.proxyToo = hosts[req.hostname].location;
  req.authMiddleware = hosts[req.hostname].auth;
}
else{
  req.proxyToo = hosts.Default.location;
  req.authMiddleware = hosts.Default.auth;
}

console.log((new Date()).toLocaleString() + " - found: " + req.proxyToo + " using " + req.authMiddleware);

if (req.authMiddleware == 'node-sspi'){    
    console.log((new Date()).toLocaleString() + " - starting authentication using node-ssip");
    var nodeSSPIObj = new nodeSSPI({
      retrieveGroups: true
    });
    nodeSSPIObj.authenticate(req, res, function(err){
      res.finished || next()
    })
}
else{
  console.log((new Date()).toLocaleString() + " - starting authentication using express-ntlm");
  ntlm({
    debug: function() {
        var args = Array.prototype.slice.apply(arguments);
        console.log.apply(null, args);
    },
    domain: 'CUSTOMER',
    domaincontroller: 'ldap://ldap.customer.com',
  })(req, res, next);
}

} catch(ex){ console.log((new Date()).toLocaleString() + " - error setting headers"); res.sendStatus(500); }

});

app.use('/', function(req, res, next){ try{ if (req.connection && req.connection.user) console.log((new Date()).toLocaleString() + " - forwarding request for " + req.connection.user); else if (req.ntlm) console.log((new Date()).toLocaleString() + " - forwarding request for " + req.ntlm.UserName); else console.log((new Date()).toLocaleString() + " - user not found"); proxy.web(req, res, { target: req.proxyToo }); } catch(ex){ console.log((new Date()).toLocaleString() + " - error setting headers"); res.sendStatus(500); }

});

var hosts = { 'ipa-dev' : {location:'http://localhost:3000', auth:'express-ntlm'}, 'ipa-dev.fortressinv.com' : {location:'http://localhost:3000', auth:'express-ntlm'}, 'ipa-support-dev' : {location:'http://localhost:4000', auth:'express-ntlm'}, 'ipa-dev:3002' : {location:'http://localhost:3002', auth:'express-ntlm'}, 'ipa-qa' : {location:'http://localhost:3002', auth:'express-ntlm'}, 'ipa-qa.fortressinv.com' : {location:'http://localhost:3002', auth:'express-ntlm'}, 'fundingtool-dev': {location:'http://localhost:3003', auth:'node-sspi'}, 'fundingtool-dev.fortressinv.com': {location:'http://localhost:3003', auth:'node-sspi'}, Default : {location:'http://localhost:3000', auth:'express-ntlm'} }

app.listen(80);

FortressDave avatar Jan 18 '18 22:01 FortressDave