Home Icon
LinkedIn logo
GitHub logo
Profile Picture
ERICWHITEFIELD
front-end developer

Express.js Certbot and AWS Linux Security

The networking issues you'll run into can be quite revealing

You’re likely to hit a few problems with blocked web traffic and Linux permissions if you try to run an Express.js server on EC2. I'll explain why, and what that reveals about Linux security.

Lets start an Express.js server via Node

        var express = require('express');
        var app = express();
        var https = require('https');
        var port = 443;
        var options = {
            key: fs.readFileSync('/pathtothecerts/privkey.pem'),
            cert: fs.readFileSync('/pathtothecerts/fullchain.pem')
        };
        server = https.createServer(options, app);
        server.listen(port, () => console.log(`Example app listening on port ${port}!`) );

First problem: binding to SSL port 443 is not allowed

We get an error when we try to start the server because it can’t bind port 443.

This is because it’s common Linux security setting to disallow binding to “privileged ports” (ports less than 1024) unless the process has root privileges.

Don't let Node.js bind to privilaged posts

Technically it is possible to allow a process to bind to “privileged ports” without root

$ sudo setcap 'cap_net_bind_service=+ep' /path/to/program

This is bad for security. You have to apply this the interpruter (Node.js) not just your express script. So you're making a system wide change that is dangerous.

A better option is redirect traffic from 443 using IPTABLES

Amazon EC2 will allow you to do this.

$ sudo iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 4000

To the outside world, our server is available on port 443, even though our server is bound to port 4000. So our binding problem is solved.

Second problem: Default AWS firewall rules are blocking traffic

AWS Security Groups act as a virtual firewall that controls traffic for one or more instances. It’s important to remember every EC2 instance needs one assigned. If you didn’t set it explicitly when you created your EC2 instance then the default policy is likely blocking your traffic.

Third problem: Let's Encrpt / Certbot traffic is blocked

For Express.js we will need to run Certbot in 'Standalone Mode'. In this mode Certbot runs it's own server on port 80.

$ sudo certbot certonly --standalone -d example.com

(BTW, you'll need to manually enter the path to these certs in your Express.js server script.)

The problem is that traffic is being redirected to port 4000 by our IPTables rules

Here is script you can use to run Certbot and reset your IPTable rules. This assumes we're also using PM2 to keep our Express server running.

#!/bin/bash
pm2 stop all
iptables -t nat -D PREROUTING 2
iptables -t nat -D PREROUTING 1
certbot certonly --standalone -d example.com
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8000
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 4000
pm2 start all

There are better options

You might be wondering if professionals go through all this hassle all the time. They don't, because the tools for larger deployments already solve these problems.

Nginx (for example) doesn't have a port binding problem

It’s customary to start Nginx as root (which can bind 443) and Nginx then forks multiple worker processes as a separate user.

Nginx can act as a server or it can proxy the traffic to another server on a non-priveleged port. Similar to what we did with IPTables.

Certbot has pluggins for Nginx (Apache, Haproxy, Plesk)

With the Certbot pluggin for Nginx installed it's easy to request certs.

$ sudo certbot --nginx

This will automagically get certs for the domains in your Nginx config. It will even add lines to your Nginx config file so that it points to you cert files.