Modify headers of proxied request - node.js

I'm IP restricting a pure client-side CORs demo application which interacts with an authenticated third-party API. I've got a "middleware" server running which I use to proxy requests from the CORs app to the third-party API, but I am having trouble injecting Basic Authentication credentials into those proxied requests.
isAllowed = (req, res, next) -> # Do IP check here.
base64Encode = (unencoded) -> new Buffer(unencoded or '').toString 'base64'
app.all "/demoproxy/*", isAllowed, (req, res) ->
req.url = "/" + req.url.split("/").slice(2).join("/")
userPass = base64Encode "#{process.env.DEMO_USERNAME}:#{process.env.DEMO_PASSWORD}"
# This doesn't work.
# res.setHeader 'Authorization', "Basic #{userPass}"
# This doesn't work either.
###res.oldWriteHead = res.writeHead
res.writeHead = (statusCode, headers) ->
headers = { }
headers['Authorization'] = "Basic #{userPass}"
res.oldWriteHead statusCode, headers###
proxy = new httpProxy.HttpProxy
target:
host: 'remote-api.com'
port: 80
proxy.proxyRequest req, res
What is the proper way to do this?

I think you want to set the authorization header on the request (req) object in this case, not the response (res). If remote-api.com is what needs to be authenticated against then it needs to know that with the request you send to it. Maybe try the following before making the proxy.proxyRequest request
req.headers["authorization"] = "Basic #{userPass}"
With the req object there isn't a setHeader function, the headers property is just a javascript object/map. Hope that helps out...

Here is some code that works for me, as an example:
# Demo server requiring basic authentication
servAuth = require("http").createServer (req, res) ->
if auth = req.headers?.authorization
res.statusCode = 200
res.end "Good job, you sent '#{auth}'"
else
res.statusCode = 401
res.end "How about you authenticate first?"
servAuth.listen(8090)
# Proxy server which checks the IP address and then proxies the request
servProxy = require("http-proxy").createServer (req, res, proxy) ->
checkIP req, (err, isOK) ->
# something wrong happened even with the IP address checking
if err
res.statusCode = 500
res.end "Sorry, everything got fargled", "ascii"
# IP address not allowed
else if not isOK
res.statusCode = 403
res.end "You ain't from around here, are you?", "ascii"
# all good, proxy the request with basic auth added
else
userPass = new Buffer("#{process.env.USERNAME}:#{process.env.PASSWORD}", "ascii")
userPass = userPass.toString("base64")
req.headers.authorization = "Basic #{userPass}"
proxy.proxyRequest req, res, {
host: "localhost"
port: 8090
}
servProxy.listen(8080)
# asynchronous IP address checking
checkIP = (req, done) ->
# TODO: implement whatever custom IP checking
# this example just says everything is OK
done( null, true )

Related

How to res.send to a new URL in Node.js/Express?

Given a GET request to URL X, how should you define res.send such that it provides a response to a completely separate URL Y?
i.e.
app.get('/', function (req, res) {
res.send to a new URL external of the app ('Success')
});
Thanks and apologies in advance for ignorance on the topic!
You want to redirect the request by setting the status code and providing a location header. 302 indicates a temporary redirect, 301 is a permanent redirect.
app.get('/', function (req, res) {
res.statusCode = 302;
res.setHeader("Location", "http://www.url.com/page");
res.end();
});
You can only send response to the request source, which is the client. There is no such thing as sending response to another "external-url" (or another server).
However, you CAN make a request to another server, wait for it's response, and respond to our client.
app.get('/', (req, res) => {
var requestOptions = {
hostname: 'serverB.com', // url or ip address
port: 8080, // default to 80 if not provided
path: '/take-request',
method: 'POST' // HTTP Method
};
var externalRequest = http.request(requestOptions, (externalResponse) => {
// ServerB done responding
externalResponse.on('end', () => {
// Response to client
res.end('data was send to serverB');
});
});
// Free to send anthing to serverB
externalRequest.write(req.data);
externalRequest.end();
});

Check if request is in state of re-direct

There is a way with express check if the request is in status redirect (302) ,I use the( req,res ) I use the following ?
var http = require('http'),
httpProxy = require('http-proxy');
var proxy = httpProxy.createProxyServer({});
proxy.on('proxyReq', function(proxyReq, req, res, options) {
proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
});
proxy.on('proxyRes', function (proxyRes, req, res) {
console.log('RAW Response from the target', JSON.stringify(proxyRes.headers, true, 2));
});
https://github.com/nodejitsu/node-http-proxy
Maybe I don't understand the question, but you can easily do the following:
proxy.on('proxyRes', function(proxyRes, req, res) {
if (proxyRes.statusCode === 301 || proxyRes.statusCode === 302) {
proxyRes.headers['location'] = fixUrl(proxyRes.headers['location']);
}
});
Where fixUrl() does any necessary transform on the location response header.
You can listen for proxyRes event and check its status code (status code for redirection is 30x). ProxyRes is a raw response from your target, so you should be able to catch and modify the response headers before the proxy send this response to the client.
Alternatively, you can also set the proxy options that handle 30x redirects. Either setting the autoRewrite to true or explicitly state the location in the hostRewrite and protocolRewrite should do the trick. https://github.com/nodejitsu/node-http-proxy#options

proxying requests from a secure application

im trying to proxy requests from a secure web application(https) in node (its an internal application) and im not quite sure how to do it...
the below is my code which works when i try it from an non secured app (http).
It just strips out a page name and uses it in another app. I read the docs but still not sure how to do it. Do i need to have the ssl info from my application for this to work?
var http = require('http');
var httpProxy = require('http-proxy');
var request = require('request');
var app = require('express.io')();
app.http().io();
//
// Create proxy server
//
var proxy = httpProxy.createProxyServer({target:'http://localhost:9000'}).listen(9085);
// Send the client html.
app.get('/', function(req, res) {
res.sendfile(__dirname + '/client1.html');
})
proxy.on('error', function (err, req, res) {
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Something went wrong!');
});
app.all('/Domain/*', function(req, res) {
console.log(req.url);
if (req.url.indexOf("Page.do") > -1) {
// URL to Atlas
var otherAppURL = "http://myotherapp/pages/";
var temp = req.url.split("Page.do")[0].split("/");
var pageName = temp[temp.length - 1];;
app.io.broadcast('update', {
url: atlasURL + pageName + '.html'
});
};
// This doesnt work
//var url = "https://mysecureapp:9044" + req.url;
// This works
var url = "http://localhost:9080" + req.url;
req.pipe(request(url)).pipe(res);
})
app.listen(9000);
Yes, you do need an SSL Certificate for an HTTPS connection. According to the website https://www.globalsign.com/en/ssl-information-center/what-is-an-ssl-certificate/ :
The standard HTTP is changed to HTTPS, automatically telling the browser that the connection between the server and the browser must be secured using SSL.
This means that with an HTTPS connection, you need to have the server secured with SSL.
For connecting with HTTPS in Node, this website might help you:
http://gaboesquivel.com/blog/2014/nodejs-https-and-ssl-certificate-for-development/
Ok i might be mistaking but this line doesn't make sense ,
req.pipe(request(url)).pipe(res);
by the time the route handler hits, the req object is ready
so req.pipe has no meaning.
please check that url returns a valid response
request(url).pipe(res); should work
http / https is not an issue

NodeJS - Reverse Proxy with Route Changing

I'm currently using NodeJS/Express as a simple domain router running on my VPS on port 80. My routes.coffee looks something like this:
request = require("request")
module.exports = (app) ->
#404, 503, error
app.get "/404", (req, res, next) ->
res.send "404. Sowway. :("
app.get "/error", (req, res, next) ->
res.send "STOP SENDING ERRORS! It ain't cool, yo."
#Domain Redirects
app.all '/*', (req, res, next) ->
hostname = req.headers.host.split(":")[0]
#Website1.com
if hostname == 'website1.com'
res.status 301
res.redirect 'http://facebook.com/website1'
#Example2.com
else if hostname == 'example2.com'
pathToGo = (req.url).replace('www.','').replace('http://example2.com','')
request('http://localhost:8020'+pathToGo).pipe(res)
#Other
else
res.redirect '/404'
As you can see in Example2.com, I'm attempting to reverse proxy to another node instance on a different port. Overall it works perfectly, except for one issue. If the route on the other node instance changes (Redirects from example2.com/page1.html to example2.com/post5), the URL in the address bar doesn't change. Would anyone happen to have a nice workaround for this? Or maybe a better way to reverse proxy? Thanks!
In order to redirect the client, you should set the http-status-code to 3xx and send a location header.
I'm not familiar with request module but I believe it follows redirects by default.
On the other hand, you're piping the proxy-request's response to client's response object, discarding the headers and the status code. That's why the clients don't get redirected.
Here is a simple reverse HTTP proxy using the built-in HTTP client. It's written in javascript but you can easily translate it to coffeescript and use request module if you want.
var http = require('http');
var url = require('url');
var server = http.createServer(function (req, res) {
parsedUrl = url.parse(req.url);
var headersCopy = {};
// create a copy of request headers
for (attr in req.headers) {
if (!req.headers.hasOwnProperty(attr)) continue;
headersCopy[attr] = req.headers[attr];
}
// set request host header
if (headersCopy.host) headersCopy.host = 'localhost:8020';
var options = {
host: 'localhost:8020',
method: req.method,
path: parsedUrl.path,
headers: headersCopy
};
var clientRequest = http.request(options);
clientRequest.on('response', function (clientResponse) {
res.statusCode = clientResponse.statusCode;
for (header in clientResponse.headers) {
if (!clientResponse.headers.hasOwnProperty(header)) continue;
res.setHeader(header, clientResponse.headers[header]);
}
clientResponse.pipe(res);
});
req.pipe(clientRequest);
});
server.listen(80);
// drop root privileges
server.on('listening', function () {
process.setgid && process.setgid('nobody');
process.setuid && process.setuid('nobody');
});

Rewrite response headers with node-http-proxy

I'm using node-http-proxy and want to watch for a particular response
header and rewrite it if necessary. Anyone here have suggestions on to
do this?
My proxy server sits in front of a couple different node servers as
well as a java webapp. The java app is setting a cookie, but the
cookie has a path that is relative the the webapp's context. I need
the cookie to be secure and have a path to root without modifying the Java
application.
In other words, the following header is returned:
set-cookie: MYSPECIALCOOKIE=679b6291-d1cc-47be; Path=/app; HttpOnly
And I'd like to rewrite the Path value to:
set-cookie: MYSPECIALCOOKIE=679b6291-d1cc-47be; Path=/; HttpOnly; Secure
I'm not clear how I would do this using node-http-proxy. Suggestions?
Is there middleware to help with this?
You can achieve this by overloading the writeHead function of the response object. For example, this code will set the 'foo' response header to the value 'bar'. I've indicated where you can add your own logic to change the header values.
JavaScript is not my primary language, so there may be a more idiomatic way to overload the writeHead method.
httpProxy = require('http-proxy');
httpProxy.createServer(function (req, res, proxy) {
res.oldWriteHead = res.writeHead;
res.writeHead = function(statusCode, headers) {
/* add logic to change headers here */
var contentType = res.getHeader('content-type');
res.setHeader('content-type', 'text/plain');
// old way: might not work now
// as headers param is not always provided
// https://github.com/nodejitsu/node-http-proxy/pull/260/files
// headers['foo'] = 'bar';
res.oldWriteHead(statusCode, headers);
}
proxy.proxyRequest(req, res, {
host: 'localhost',
port: 3000
});
}).listen(8000);
Just listen to proxyRes event and put your logic.
proxy.on('proxyRes', (proxyRes, req, res) => {
// modifying headers goes here
});
See https://www.npmjs.com/package/http-proxy#listening-for-proxy-events
I didn't test this code, but it should allow you to edit your header before sending the request. Let me know if it works.
var httpProxy = require('http-proxy');
var server = httpProxy.createServer(function (req, res, proxy) {
var buffer = httpProxy.buffer(req);
req.headers['x-host'] = process.env.PROXY_URI;
proxy.proxyRequest(req, res, {
host: '127.0.0.1',
port: 9000,
});
});

Resources