Reputation: 31
I am using the http-proxy-middleware (https://www.npmjs.com/package/http-proxy-middleware) to implement a proxy to another REST API that has client-side certificate based authentication enabled (requestCert: true, rejectUnauthorized: true).
Client calls to the Proxy API ( https://localhost:3000/auth ) where http-proxy-middleware is configured and is supposed to proxy it to another REST API ( https://localhost:3002/auth ) that has client-side certificate based authentication enabled (requestCert: true, rejectUnauthorized: true).
I don't want any specific authentication to happen at the proxy. When I invoke the proxy with a path that will route to this target end-point with client-side certs based authentication, it is failing with error message:
Error received in proxy server:
[HPM] Rewriting path from "/auth" to ""
[HPM] GET /auth ~> https://localhost:3002/auth
RAW REQUEST from the target {
"host": "localhost:3000",
"connection": "close"
}
redirecting to auth
[HPM] Error occurred while trying to proxy request from localhost:3000 to https://localhost:3002/auth (EPROTO) (https://nodejs.org/api/errors.html#errors_common_system_errors)
Error received in client side:
Proxy error: Error: write EPROTO 28628:error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:c:\ws\deps\openssl\openssl\ssl\record\rec_layer_s3.c:1536:SSL alert number 40
I don't need the proxy to validate/act on client-side certs coming with the incoming request in any way (I have set secure: false for this), but rather just forward it to the target end point. We are seeing the the certs received from the client are not being passed/proxied/forwarded to the target end-point and hence cert based auth fails on the target end-point.
The client request when sent to the target end-point directly is working, but NOT when sent via http-proxy-middleware proxy.
My test server, client code is given below for reference.
Is there some way to configure the http-proxy-middleware so that it forwards/proxies the client-side certs received from the client to the target end-point so that the client-side certs sent by the client are available for cert based validation on the target REST end-point?
Could you please guide me on how to do this with http-proxy-middleware package or any other suitable way? Thanks in advance.
Server code
// Certificate based HTTPS Server
var authOptions = {
key: fs.readFileSync('./certs/server-key.pem'),
cert: fs.readFileSync('./certs/server-crt.pem'),
ca: fs.readFileSync('./certs/ca-crt.pem'),
requestCert: true,
rejectUnauthorized: true
};
var authApp = express();
authApp.get('/auth', function (req, res) {
res.send("data from auth");
});
var authServer = https.createServer(authOptions, authApp);
authServer.listen(3002);
// HTTP Proxy Middleware
var authProxyConfig = proxy({
target: 'https://localhost:3002/auth',
pathRewrite: {
'^/auth': '' // rewrite path
},
changeOrigin: true,
logLevel: 'debug',
secure: false,
onProxyReq: (proxyReq, req, res) => {
// Incoming request ( req ) : Not able to see the certificate that was passed by client.
// Refer the following client code for the same
},
onError: (err, req, res) => {
res.end(`Proxy error: ${err}.`);
}
});
proxyApp.use('/auth', authProxyConfig);
var unAuthOptions = {
key: fs.readFileSync('./certs/server-key.pem'),
cert: fs.readFileSync('./certs/server-crt.pem'),
ca: fs.readFileSync('./certs/ca-crt.pem'),
requestCert: false,
rejectUnauthorized: false
};
var proxyServer = https.createServer(unAuthOptions, proxyApp);
proxyServer.listen(3000);
Client Code
var fs = require('fs');
var https = require('https');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
var options = {
hostname: 'localhost',
port: 3000,
path: '/auth',
method: 'GET',
key: fs.readFileSync('./certs/client1-key.pem'),
cert: fs.readFileSync('./certs/client1-crt.pem'),
ca: fs.readFileSync('./certs/ca-crt.pem')
};
var req = https.request(options, function (res) {
res.on('data', function (data) {
process.stdout.write(data);
});
});
req.end();
Upvotes: 3
Views: 8940
Reputation: 3011
You specifically say // Incoming request ( req ) : Not able to see the certificate that was passed by client.
, so perhaps you have already looked at getPeerCertificate
and found the connection closed.
That said, in the onProxyReq
handler, you might try adding the cert to the proxyReq
from the req
with the getPeerCertificate
method (docs).
This SO answer + comment shows how to get and convert to valid cert.
const parseReqCert = (req) => {
const { socket } = req;
const prefix = '-----BEGIN CERTIFICATE-----';
const postfix = '-----END CERTIFICATE-----';
const pemText = socket.getPeerCertificate(true).raw.toString('base64').match(/.{0,64}/g);
return [prefix, pemText, postfix].join("\n");
}
const addClientCert = (proxyReq, req) => {
proxyReq.cert = parseReqCert(req);
return proxyReq;
}
const authProxyConfig = proxy({
...
onProxyReq: (proxyReq, req, res) => {
addClientCert(proxyReq, req);
},
...
})
Upvotes: 3