validate https requests using a Certificate Authority (CA) - node.js

I have deployed multiple microservices containing frontend application and backend service.
The frontend application is accessible via xyz.com domain. It calls the backend service API endpoint.
So, what I really want is to check is that if any request that is coming from the frontend application is valid and from authentic source on the basis of its domain and subdomain using Certificate Authority in Node.js.
After doing a little bit of research about how it can be done in node.js,
I found out that it can be done using nodejs https module's request method. But the problem with this approach is that nodejs maintains a list of CA certs, which easily gets out of date and there is a chance that the CA that has verified my domain certificate is not part of that list. Although they provide a way to pass additional CA's but still it is a dependency on the user side that they have to maintain the list. I am currently a little bit lost on how to do it in a proper way.
I need help on how to do this process easily and efficiently.

There are two ways to validate a domain in node.js
https.request
Nodejs https module's request method validates the domain provided against the chain of Certificate Authorities root certificate. A code example is given below:
var https = require('https');
var options = {
hostname: 'github.com/',
port: 443,
path: '/',
method: 'GET',
rejectUnauthorized: true
};
var ss;
var req = https.request(options, function(res) {
console.log("statusCode: ", res.statusCode);
console.log("headers: ", res.headers);
});
req.end();
req.on('error', function(e) {
console.error(e);
});
rejectUnauthorized: This means that it will validate the server/domain certificate against the chain of CA's root certificate.
The only problem with this approach is that this chain should be updated regularly otherwise a new domain that is signed by a certificate authority root certificate which is not part of the chain, marked as an invalid certificate(a common example is a self-signed certificate).
ssl-validate module
It can also be used but it requires another module to get the domain information.

Related

What key should I use to sign an intermediate SSL certificate for a reversed proxied HTTPS connection?

I have an Apache server hosting a couple webpages, and an express server running on another machine that connects via a reverse proxy to the apache server, providing an endpoint to the webpage. The webpage is already running on HTTPS with valid certificates, but now I have to configure the express server to do the same with the intermediate certificates, I believe Which private key should I use to setup this service? Or are the intermediate certs signed by the apache head? I don't think that is the case since the configuration dones't work without a key, but I still cannot get my head around how this works if the certificates are issued at the same time the private key of the server is generated. How is it possible to cofigure intermediate connections with intermediate certs if the intermediate servers didn't exist when the certificates where issued?
This is the express server configuration so far:
const options = {
// The same certificate apache uses. Is this right?
cert: fs.readFileSync(config.ssl.dir + config.ssl.cert)
// The three sections of the bundle the issuer provided
ca: [],
// The private key I don't have (its in the other server)
key: fs.readFileSync(config.ssl.dir + config.ssl.key),
};
config.ssl.ca.forEach((value) => {
if (value) options.ca.push(fs.readFileSync(config.ssl.dir + value));
});
server = https.createServer(options, app);
Some more information, I have three files:
certificate.csr, the one the apache server uses and works, so I think it is the root certificate
certificate.pem, with the exact same contents as certificate.csr
a_bundle.csr, with three sections. I believe those are the intermediate certs.
These files were provided by GoDaddy.com when buying the ssl certificates, but with meaningless names.

MTLS - generate certificate to nodejs client side

We need to communicate between our ec2 server and our customer server via Mutual TLS.
The requests are sent from our server to our customer server - so we are the client here.
I read this post, talking about how to generate the files.
The first step is to create a certificate authority (CA) that both the
client and server trust. The CA is just a public and private key with
the public key wrapped up in a self-signed X.509 certificate.
Our cert and their cert - should be signed from the same root CA? who should provide it?
The code in my side should be like:
const req = https.request(
{
hostname: 'myserver.internal.net',
port: 443,
path: '/',
method: 'GET',
cert: fs.readFileSync('client.crt'),
key: fs.readFileSync('client.key'),
ca: fs.readFileSync('ca.crt')
},
res => {
res.on('data', function(data) {
// do something with response
});
}
);
So what should we provide each other? We don't exactly understand and they are not providing more details, just asked us to give them a certificate...
Our cert and their cert - should be signed from the same root CA? who should provide it?
Since the control of the client certificate is done at the TLS server side (i.e. at the customer) it depends fully on what they expect. They might require a publicly signed certificate, they might require a certificate signed by their own CA. Or they might simply check that a specific certificate gets used and will also accept self-signed certificates for this.

NodeJS forwarding client SSL certificates

I am working on Node.js server application which is SSL enabled and accepts client certificates. I am using following code to create https server.
const options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
ca: fs.readFileSync('ca.pem'),
requestCert: true,
rejectUnauthorized: true
};
https.createServer(options,app).listen(8090, function(){
console.log("Listening on 8090");
});
Other Node.js based client apps are able to connect using their SSL certficate and get the service response.
However from my sever, I want to make another server call and wish to pass on the same client certificate I received. I simply want to forward the same ceritifcate, I understand I can get the certificate details in request object, but how to retrieve the crt and key from that object?
I am looking to do something like below:
app.get('/myservice', (req,res) => {
//req.socket.getPeerCertificate(true);
var agent = new https.Agent({
cert: somelibrary(req.socket.getPeerCertificate(true).??????),
key: somelibrary(req.socket.getPeerCertificate(true).??????),
});
fetch('http://AnotherServiceURL', { method: 'GET' agent}).then(function(response){
res.json(response);
});
});
Is there any library which can convert request certificate details in a way so as to forward those as key and cert? Or is there any other/better way of doing this?
I understand I can get the certificate details in request object, but how to retrieve the crt and key from that object?
While it would be possible to pass the client certificate itself it is impossible to use it again as client certificate in another TLS connection to the final target. For this you would need to have access to the private key of the client, which as the name says is private to the client. As for getting the client certificate (i.e. the public part) see Node.js: access the client certificate.

Self signed certificate in certificate chain

I am running a node.js service in a Kubernetes container. My service uses the token inside the service account to make calls to the API server from inside the POD. My code is like this
var fs = require('fs');
var tokenFile ='/var/run/secrets/kubernetes.io/serviceaccount/token';
var restCall = function(serviceUrl,reqMethod,callback){
var token = "";
fs.readFile(tokenFile, 'utf8', function(err, data) {
if (err) throw err;
token ='Bearer '+data;
});
var serviceUrl = https://<clusterName>/api/v1/nodes
var options = {
url: serviceUrl,
headers: {
'Content-Type': 'application/json',
'Authorization': token
},
method: reqMethod
};
I am receiving this error in response
self signed certificate in certificate chain.
I am able to make calls using the token through rest client, when I deployed to container it was returning socket connection error
You call the api with an https protocol, where most likely the certificate is not signed by external CA. This is normal. You should make sure you trust that CA before you make a call to service secured by certificate it issued. Most kube provisioners provide you back with CA certificate so you can add it to your trusted certs or provide to the client in some param.
On the other side, when you makethe call to api within your kube cluster an url like http://kubernetes.default/api/v1/nodes should be reachable.
Try using CA certificate, you will get it in following path
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Radek Pieczonka mentions this in his answer,
missed to pass this environment variable node_tls_unauthorized=0 which results in authentication issue

Node.js generate LetsEncrypt.org SSL certificate with specific common name

I am currently trying to create a LetsEncrypt SSL certificate package using the node letsencrypt package (https://www.npmjs.com/package/letsencrypt). I have managed to generate a standard certificate suite using the following code.
'use strict';
var express = require('express');
var LE = require('letsencrypt');
var le;
// server = staging for test encryption cert generation
// server = production for generating verified certificate
var le = LE.create({ server: 'production' });
// Define encryption certificate options as JSON object
var opts = {
domains: ['www.mydomain.com'], email: 'me#mydomain.com', agreeTos: true
};
// Submit certificate signing request to LetsEncrypt.
// Print certificates, keys (i.e. pem files) when received from server.
le.register(opts).then(function (certs) {
console.log(certs);
// privkey, cert, chain, expiresAt, issuedAt, subject, altnames
}, function (err) {
console.error(err);
});
var app = express();
// Create server listening on port 80 to handle ACME challenges
// i.e. basic web server to serve files so CA can verify website ownership
app.listen(80, function () {
console.log('Basic server started and listening on port 80');
console.log('Server handling basic ACME protocol challenges');
});
// Allow access to all static files in server directory
// Enables CA to access file served up to verify domain ownership
app.use('/', le.middleware());
Which works fine and generates me a trusted certificate from LetsEncrypt.org when accessed via www.mydomain.com. However, when I try to access my website on my internal (local) network via 192.168.0.myserveraddress. I get the following error:
Does anyone know how I can modify the common name in the certificate request to LetsEncrypt to 192.168.0.myserveraddress so I don't get this error when accessing my website via our local area network?
I actually solved this issue by setting up our local area network to allow loopback connections using what is called NAT Loopback.
This means I do not need to use the local IP address (192.168.0.myserveraddress) to access my server anymore and can just use www.mydomain.com to access it internally.
Since this maintains the domain name the certificate is now trusted and I no longer have the above error.
Additionally I believe that certificate authorities (i.e. LetsEncrypt) will not issue certificates for IP addresses. So the only way you can resolve the above error is to access the website via its domain name. See link below.
https://community.letsencrypt.org/t/certificate-for-static-ip/84.

Resources