This script runs certbot in manual mode with a custom adhoc local DNS server (written in node.js using my fork of joyent/node-mname) to respond to its challenge.
The Let's Encrypt non-profit does an amazing job at helping hosting providers provide SSL certificates for free to their customers. This should be your first choice to get one, but maybe your provider prefers charging you for said certificates. That's where we come to play.
If you've just bought a domain and have a Unix machine connected to the internet (laptop, VPS or dedicated instance), you'll get your SSL certificate in 10 minutes chrono. Let's start!
Before running this, make sure the machine you're running it from can act as the personal DNS server for your domain:
- Configure its externally-visible IP in your DNS registrar as the first and second personal/custom DNS server for the domain.
- Ensure your server's firewall is letting UDP port 53 traffic in. You might need to do this both on your machine (e.g. with
sudo ufw allow dns
on Linux) and in the control panel of your VPS / dedicated cloud instance. - If running from a local network, ensure your home router lets UDP/53 traffic through. Most routers will let you do that with upnp (provided in the package
miniupnpc
in most distros, incl. Homebrew):upnpc -e "Temporary DNS to get an SSL certificate" -r 53 UDP
- Ensure you have a recent node.js (I recommend using the "n" package) and the
dig
utility (often packaged underdnsutils
):# brew install dnsutils node # sudo apt install dnsutils node npm i -G n n latest
- Ensure there's no existing DNS server running on your machine. If you're not sure, there probably isn't any (the command may vary, something like
sudo systemctl stop <dns>
orsudo service <dns> stop
wheredns
is one ofnamed
,systemd-resolved
,bind9
) - Install certbot. Probably as simple as
sudo get install certbot
orbrew install certbot
. - Download ./letsencrypt.sh, make it executable and scrutinize it (we don't live in a web of trust I'm afraid). You should also inspect this branch of mine that it's pulling as an npm dependency. Feel free to run the tests with
TEST=1 ./letsencrypt.sh
to check things are likely to work as they should.
./letsencrypt.sh foo.xyz someone+foo.xyz+certbot@gmail.com
That will output certificate and private key files:
/etc/letsencrypt/live/foo.xyz/fullchain.pem
/etc/letsencrypt/live/foo.xyz/privkey.pem
Which you can immediately use to run a full-flegded, green-lock HTTPS server (don't forget to clear the way for HTTPS port 443/TCP in your firewalls, see instructions above):
const express = require('express');
const https = require('https');
const fs = require('fs');
const app = express();
app.get("/", (req, res) => res.send('Hello, HTTPS!').end());
const server = https.createServer({
cert: fs.readFileSync('/etc/letsencrypt/live/foo.xyz/fullchain.pem'),
key: fs.readFileSync('/etc/letsencrypt/live/foo.xyz/privkey.pem'),
}, app);
const listener = server.listen(process.env.PORT,
() => console.log(`Listening on port ${listener.address().port}`));
Run this with:
PORT=443 node server.js