Include subdomain when proxying in node - node.js

I've setup a simple HTTPS server to handle the following to situations:
Requests to https://localhost:5000/ that have a matching file in my directory are served via connect.static(__dirname). This works great for everything like my index.html and my CSS files and is working exactly as I need.
Requests to https://localhost:5000/api should redirect to https://subdomain.mydomain.com:443/api.
The proxy is properly transferring everything over HTTPS and the SSL handshake part seems to be working exactly as I would expect. The problem is that my API uses the subdomain to determine what database to connect to and what data to return. So, my API sees the request
https://localhost:5000/api/something
instead of
https://subdomain.mydomain.com/api/something
and is throwing an error telling me I have to supply the subdomain.
How can I tell the node proxy to forward (or use) the domain/subdomain when doing the proxy?
Here is my code:
var fs = require('fs');
var connect = require('connect'),
https = require('https'),
httpProxy = require('http-proxy'),
options = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem')
},
endpoint = {
host: 'subdomain.mydomain.com',
port: 443,
prefix: '/api',
target: { https: true }
};
var proxy = new httpProxy.RoutingProxy();
var app = connect()
.use(connect.logger('dev'))
.use(function(req, res, next) {
if (req.url.indexOf(endpoint.prefix) === 0) {
proxy.proxyRequest(req, res, endpoint);
} else {
next();
}
})
.use(connect.static(__dirname));
https.createServer(options, app).listen(5000);
console.log('Listening on port 5000');

Just in case someone bumps into this old question, you should use http-proxy's changeOrigin option.

Related

Create server available on HTTP/HTTPs that can be used with PM2

I'm trying to create a Socket.IO server that has the following goals:
Accessible on the local network of virtual machines using HTTP (http://<server-local-ip>)
That can be accessed via browser by users through the HTTPs protocol, and that can also make the socket.io.js bundle available via HTTPs (https://socket-server.example.com)
That uses all available CPUs in the virtual machine (the server will run in just one virtual machine) - (Possible with PM2)
Have the ability to be automatically restarted in case of failure (Possible with PM2)
For that I created a script based on the Socket.IO help article teaching how to use PM2 and this question that teaches to use HTTP and HTTPs.
/**
* pm2 start basic.js -i 0
*/
const http = require("http");
const https = require("https");
const { Server } = require("socket.io");
const { createAdapter } = require("#socket.io/cluster-adapter");
const { setupWorker } = require("#socket.io/sticky");
const { readFileSync } = require("fs");
const httpServer = http.createServer();
const httpsServer = https.createServer({
key: readFileSync("./localhost-key.pem"),
cert: readFileSync("./localhost.pem")
});
const io = new Server(httpServer, {
cors: {
origin: "*",
methods: ["GET", "POST"]
}
});
io.adapter(createAdapter());
setupWorker(io);
io.on("connection", (socket) => {
console.log(`connect ${socket.id}`);
});
httpsServer.on("request", (req, res) => {
io.engine.handleRequest(req, res);
});
httpsServer.on("upgrade", (req, socket, head) => {
io.engine.handleUpgrade(req, socket, head);
});
httpServer.listen(8080);
httpsServer.listen(4430);
Using HTTP and HTTPs always throws an error.
Via HTTPs I can't load the socket.io.js bundle. But as this service will be available via browser, it will be necessary to make it available via HTTPs to users.
Direct access via HTTPs displays:
{
code: 0,
message: "Transport unknown"
}
This is just using the first part of the script, without trying to run with PM2 yet.
When placing the PM2 part next to the script, other errors appear:
I have to remove the code httpServer.listen(3000); for HTTP to work
When I connect to HTTPs the code never finds the session, so it keeps trying to reconnect endlessly.
socket.io.js via HTTPs remains unreachable
Even using HTTP socket.io.js and connecting with <script src="http://localhost:8080/socket.io/socket.io.js"></script> <script> const socket = io('https://localhost:3001');</script> nothing works
However, if I run all this over HTTP only, without requiring HTTPs, it works perfectly.
What am I doing wrong for HTTP/HTTPs not to work together?
Will I have to make the server available only in HTTP and create a proxy via NGINX to support HTTPs and call the HTTP server?

localhost:300/localhost:443 redirect to localhost with nodejs

Hello everybody and thanks in advance for your answer.
I have a website which is serve by nodejs and I'm listening on port 300 for http and 443 for https:
const fs = require('fs');
const https = require('https');
const http = require('http');
const app = require('../app');
const env = require(`../environment/${process.env.NODE_ENV}`);
const httpServer = http.createServer((req, res) => {
res.writeHead(301, { Location: `https://${req.headers.host.split(':')[0] + ':' + env.portHttps}${req.url}` });
res.end();
}).listen(env.portHttp);
const options = {
key: fs.readFileSync(env.key),
cert: fs.readFileSync(env.cert),
};
const httpsServer = https.createServer(options, app).listen(env.portHttps);
This code is from a tutorial and I guess I don't understand it well because I was expecting to get my site calling localhost:300 or localhost:443 and each time, the request on google chrome redirect to https://localhost/ and I don't get why.
So it works fine but I'd like to know why the redirection because ... Why calling a .listen(port) then ?
PS: I have an angular app launch with a proxy :
{
"/": {
"target": "https://localhost",
"changeOrigin": true,
"secure": false
}
}
I know the purpose of this proxy, I only wonder why the redirection happen and the tutorial I've followed does't explain that.
You are creating an http server on port 80 and sending back a 301 HTTP Redirect to all requests.
These redirects work by returning status code 301 and an HTTP header Location with where you'd like the browser to go to. So when you visit http://localhost/abc you receive 301, Location: https://localhost/abc and your browser takes you there.
In the dev tools, if you go to the network tab and enable "preserve log" you should see both the redirect and the actual application page load.
const httpServer = http.createServer((req, res) => {
// 301 is the http status code
// Location: ... is the header which contains where to go to
res.writeHead(301, { Location: `https://${req.headers.host.split(':')[0] + ':' + env.portHttps}${req.url}` });
res.end();
}).listen(env.portHttp);
At the bottom of the file you set up the https server which listens on port 443 and serves your application.
All such secure (TLS) transfers are done using port 443, the standard port for HTTPS traffic. As such, when your browser recognizes the 443 port, it shows you the https secure lock (green) and 301 is the http redirect to all request which is the response status code.

Prevent http traffic, and allow https using Automated Certificate management

I am using express nodejs module to create the app server. The app is accessibile over both https and http
I want to prevent http endpoint.
I have enabled Automated certificate management. My question is, if I want to use the cert which is generated through acm, how can I use the key and the cert in my nodejs code.
You can create an HTTPS server and pass along a private key and certificate like so:
const fs = require('fs');
const https = require('https');
const express = require('express');
const app = express();
const options = {
key: fs.readFileSync('path/to/key', 'utf8'),
cert: fs.readFileSync('path/to/cert', 'utf8'),
};
const httpsServer = https.createServer(options, app);
httpsServer.listen(8443);
If you don't want to respond to HTTP requests at all, simply don't create an HTTP server. If you want to to forward users to HTTPS instead, you could add something like this:
const http = require('http');
app.use('*', (req, res, next) => {
if (!req.secure) {
const [ host ] = req.headers.host.split(':');
return res.redirect(`https://${host}:8443${req.url}`);
}
return next();
});
const httpServer = http.createServer(app);
httpServer.listen(8000);
Or you can handle this at reverse-proxy level. E.g. for Nginx:
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
}

Redirect non-www and HTTP to www and HTTPS

I am running an Express web server. I used Let's Encypt to create SSL certificates for both the www and non-www version of my domain. My DNS has these records:
CNAME | Host: # | Target: dynamicdns.example.com. | TTL: 60min
CNAME | Host: www | Target: dynamicdns.example.com. | TTL: 60min
I also have this node.js code to run the webserver:
// Dependencies
const express = require('express');
const fs = require('fs');
const http = require('http');
const https = require('https');
// Create express instance
const app = express();
// Get credentials from system
const privateKey = fs.readFileSync('/etc/letsencrypt/live/www.example.com/privkey.pem', 'utf8');
const certificate = fs.readFileSync('/etc/letsencrypt/live/www.example.com/cert.pem', 'utf8');
const ca = fs.readFileSync('/etc/letsencrypt/live/www.example.com/chain.pem', 'utf8');
const credentials = {
key: privateKey,
cert: certificate,
ca: ca
};
// Initalize server
app.use(express.static('public'));
const httpServer = http.createServer(app);
const httpsServer = https.createServer(credentials, app);
// Listen for HTTP
httpServer.listen(80, () => {
console.log('HTTP Server running on port 80');
});
// Listen for HTTPS
httpsServer.listen(443, () => {
console.log('HTTPS Server running on port 443');
});
This all works correctly, but it creates an issue with having multiple valid URLs for the same content. The following URLs are all valid right now:
http://example.com
http://www.example.com
https://example.com
https://www.example.com
I want to redirect all the wrong URLs to https://www.example.com, which is the correct URL. I'm not sure how I should go about doing this. Any help would be appreciated.
you could use a middleware like this one, to check all domains and redirect accordingly:
app.use((req, res, next) => {
if (!req.secure) {
return res.redirect('https://' + req.get('host') + req.url);
}
next()
})
Just make sure to use it just for production

How to enable HSTS with node-http-proxy?

Under node.js 0.8, I'm using node-http-proxy in "router table" mode configured like so:
var httpProxy = require("http-proxy");
var config = require("./config");
proxyServer = httpProxy.createServer({
hostnameOnly: true,
router: {
"one.example.com": "localhost:9000",
"two.example.com": "localhost:9001"
},
https: {
key: config.key,
cert: config.cert,
// mitigate BEAST: https://community.qualys.com/blogs/securitylabs/2011/10/17/mitigating-the-beast-attack-on-tls
honorCipherOrder: true,
ciphers: "ECDHE-RSA-AES128-SHA256:AES128-GCM-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH"
}
})
proxyServer.listen(8000)
I'd like to add HSTS (HTTP Strict Transport Security) so that compliant browsers will be told to always use SSL. To do this, I need to get http-proxy to add the header:
Strict-Transport-Security: max-age=60000
(or other max-age). How can I ask node-http-proxy to efficiently append this header?
For your example, I'm not sure as it seems this older question is using http-proxy#0.8. However, here's what I've done with http-proxy#1.0.0:
var httpProxy = require('http-proxy');
// https server to decrypt TLS traffic and direct to a normal HTTP backend
var proxy = httpProxy.createProxyServer({
target: {
host: 'localhost',
port: 9009 // or whatever port your local http proxy listens on
},
ssl: {
key: fs.readFileSync('valid-ssl-key.pem', 'utf8'),
cert: fs.readFileSync('valid-ssl-cert.pem', 'utf8')
}
}).listen(443); // HTTPS listener for the real server
// http server that redirects all requests to their corresponding
// https urls, and allows standards-compliant HTTP clients to
// prevent future insecure requests.
var server = http.createServer(function(req, res) {
res.statusCode = 301;
res.setHeader('Location', 'https://' + req.headers.host.split(':')[0] + req.url);
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
return res.end();
});
server.listen(80); // HTTP listener for the old HTTP clients

Resources