I'm a newbie to NodeJS(Any JS). I'm trying to develop an app which has multiple routers. Here are my router paths.
var app = express();
var apiRouter = express.Router();
var adminRouter = express.Router();
var pageRouter = express.Router();
app.use('/api', apiRouter);
app.use('/admin', adminRouter);
app.use('/', pageRouter);
apiRouter serve the rest api which will consume in mobile apps.
adminRouter serves the admin dash board.
pageRouter serves the end users as a landing page and some highlights of the app.
I have implemented SSL Peer implementation as below.
options = {
key: // Path to key file,
ca: // Path to CA crt file,
cert: // Path to crt file,
requestCert: true,
rejectUnauthorized: true
};
How ever I need to remove SSL trusted peer implementation from pageRouter as end user do not have my certification and key. So how can I disable the SSL trusted peer implementation on pageRouter only?
You won't know what path a client is making a request for until its TLS session has already been negotiated. By that point, it's too late to reject a connection for lacking a client certificate.
Consider putting the API endpoints which require a client certificate on a separate subdomain, or a different port.
Related
How do I manage API (HTTPS) certs in DEV vs PROD in express on a node/ angular environment deployed to Windows IIS? I've seen proxy rewrites mentioned on this but I'm not sure how it is managed on express side.
I know in PROD, IIS and rewrites in web.config help manage cert for front end, but most of the tutorials I see for https on express require hard coding a self signed cert and including it in build. In PROD I have an official signed cert on server that I will use on port 8443 and not sure how that changes code, see below:
const express = require('express')
const app = express()
const https = require('https')
const fs = require('fs')
const port = process.env.PORT || 8443;
app.get('/', (req, res) => {
res.send("IT'S WORKING!")
})
const httpsOptions = {
key: fs.readFileSync('./security/cert.key'),
cert: fs.readFileSync('./security/cert.pem')
}
const server = https.createServer(httpsOptions, app)
.listen(port, () => {
console.log('server running at ' + port)
})
On a separate, but related note, how does my API service in Angular change to call the backend API for Dev vs PROD. I'm guessing something like this which IIS would re-route to PROD domain?
private API_URL: string = 'https://localhost:8443/api/';
I've seen proxy rewrites mentioned on this but I'm not sure how it is
managed on express side.
It does not need managed on express side. Express does not need to know about the certs that IIS injects.
In PROD I have an official signed cert on server that I will use on
port 8443 and not sure how that changes code
Express does not need cert in prod, IIS bindings handle that. So in server.js code only include httpsOptions if on dev and inserting self signed cert.
On a separate, but related note, how does my API service in Angular
change to call the backend API for Dev vs PROD. I'm guessing something
like this which IIS would re-route to PROD domain?
Angular CLI should provide you an environments folder with environment.prod.ts and environment.ts files which will swap the variable values they hold based on build type. Simply add a variable in both for different urls for express to call when sending requests. Your prod url will not be localhost and instead should be the actual url the user will access in production.
I've read a tonne of articles on the web and looked at a tonne of questions on stack overflow related to the following, and they all provide basically the same solution which I am unable to implement due to security issues with my company.
I am trying to deploy a NodeJS app to a secure windows server without IIS. I'm not even sure if this is possible - there is very little support about deploying node apps to windows, and what support there is says to use IIS and iisnode together. To add to the complication, my company will not give me the key to the main SSL certificate of the server.
I have access to the server/cert store/certificate, but I can't export its key. Just wondering if there is a way to have server.js point to just the certificate instead of both the certificate AND the key?
I've tried to access the certificate and extract the key via https://www.npmjs.com/package/win-ca but haven't had any luck with this.
I was able to use a self-signed certificate and get everything working, but you need to accept the self-signed certificate in your browser which isn't a viable solution for production.
I've also looked into using nginx, let's encrypt, etc., but windows support for those isn't that great either.
Here is my code which works, but like I said, I need to accept the self-signed cert client side which isn't ideal:
const express = require('express');
const app = express();
const https = require('https');
const http = require('http');
const fs = require('fs');
const options = {
//self-signed cert, I'd rather point this to the main cert for the server
//but I don't have access to the key
key: fs.readFileSync('cert.key'),
cert: fs.readFileSync('cert.pem'),
};
// Create an HTTP service.
http.createServer(app).listen(80);
// Create an HTTPS service identical to the HTTP service.
https.createServer(options, app).listen(443);
app.get('/', function (req, res) {
res.send('Hello World!');
});
In the end I removed all the certificate and security related stuff from my server.js file and just put the website behind a load balancing proxy server.
I have a Node/Express app running on a server with https enabled but when trying to login with Facebook using Passport Facebook strategy, I get this error:
Insecure Login Blocked: You can't get an access token or log in to
this app from an insecure page. Try re-loading the page as https://
I understand Facebook requires authentication but my server already has it.
The certificate is from LetsEncrypt and since I'm using a managed server, I don't have access to the certificate files. My Express app is initialized normally.
const app = express();
const cors = require("cors");
app.use(cors({ origin: true, credentials: true }));
The original request comes from my frontend running on a different server, also with SSL from LetsEncrypt.
So my front end calls the Express endpoint, which handles it to Passport:
app.get(
"/api/auth/facebook",
storeRedirectToInSession,
passport.authenticate("facebook", { scope: ["public_profile", "email"] })
);
And then the error occurs.
My research indicated that enabling "trust proxy" on Express should fix it but it didn't. If I inspect req.secure it's always false.
Do I really need to have access to the certificate files?
Any help is appreciated.
Thanks
People of the future, I could not fix this issue without actually getting access to the cert files on my Node/Express app. I had support create symbolic links to the letsencrypt key and cert files, and then created a https express server as follows:
https
.createServer(
{
key: fs.readFileSync(keys.SSL_KEY_FILE),
cert: fs.readFileSync(keys.SSL_CERT_FILE),
},
app
)
.listen(port, function() {
console.log("ackend server listening on port ", port);
});
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.
On the OpenShift website here: https://help.openshift.com/hc/en-us/articles/202535440-How-do-I-get-SSL-for-my-domains-, it states
You can always take advantage of our *.rhcloud.com wildcard certificate in order
to securely connect to any application via it's original, OpenShift-provided
hostname URL.
However, Node's HTTPS server requires a file path to a certificate and private key in order to use HTTPS:
var privateKey = fs.readFileSync('sslcert/server.key', 'utf8');
var certificate = fs.readFileSync('sslcert/server.crt', 'utf8');
var credentials = {key: privateKey, cert: certificate};
var express = require('express');
var app = express();
var httpsServer = https.createServer(credentials, app);
httpsServer.listen(443);
None of the OpenShift environment variables (https://www.openshift.com/developers/openshift-environment-variables) appear to be related to SSL certificates, and the documentation does not mention it other than at the above link, which provides no technical information in actually using it.
How do I access the privateKey and certificate file on an OpenShift Node.js gear/cartridge?
It turns out that all SSL certificates are handled by OpenShift routers before they reach the gear/cartridge. There is no need to setup an HttpsServer at all, the normal HttpServer listening on port 8080 will receive both HTTP and HTTPS traffic transparently.
This is true whether you are using a custom certificate or the wildcard certificate, which is pretty nifty.
Nodejs Express application scenario is detailed at OpenShift https answer. To sum up, use the X-Forwarded-Proto header's value from the request headers given to your nodejs web server by openshift's proxy to determine if reply should redirect client to https or is client already requesting on https.