Node.js: HTTP2 and Let's Encrypt v2

Previous: VPS, Koa and Let's Encrypt

Although ACME v2 is not final yet, Let’s Encrypt has announced a production endpoint back to March, and Greenlock 2.2.0 has added support for ACME draft 11, so maybe it’s time to give it a try.

I don’t use express, but I use greenlock-express as a meta package. The dependencies of greenlock-express 2.1.6 are

1
2
3
4
5
6
7
8
{
"greenlock": "^2.2.16",
"le-challenge-fs": "^2.0.8",
"le-sni-auto": "^2.1.4",
"le-store-certbot": "^2.1.0",
"redirect-https": "^1.1.5",
"spdy": "^3.4.7"
}

The initialization of greenlock have also changed a lot.

1
2
3
4
5
6
7
8
9
const greenlock = require("greenlock").create({
app: new Koa().callback(), // Your `(req, res) => void` style callback
agreeTos: true,
approveDomains: ["example.com"], // Let's Encrypt have also added support for wildcard certifications.
debug: true,
email: "[email protected]", // Your email address to accept the term of service
server: "https://acme-v02.api.letsencrypt.org/directory", // Let's Encrypt's ACME v2 production server
version: "v02", // Currently "v02" is an alias to "draft-11", but someday it will become final
});

Part 2: HTTP2

The spdy package caught my attension. I want to support HTTP2, not using spdy, but the built-in http2 module. There are several reasons:

  • spdy is third-party: while http2 is a built-in module of Node.js
  • spdy is slow: spdy is implemented in JavaScript, while http2 uses nghttp2 which is a C library
  • spdy is obsolete: the last commit of spdy was in March of 2017, while Node.js and nghttp2 are actively maintained

Time to copy some code from greenlock-express:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function explainError(e) {
console.error("Error:" + e.message);
if ("EACCES" === e.errno) {
console.error("You don't have prmission to access '" + e.address + ":" + e.port + "'.");
console.error("You probably need to use \"sudo\" or \"sudo setcap 'cap_net_bind_service=+ep' $(which node)\"");
return;
}
if ("EADDRINUSE" === e.errno) {
console.error("'" + e.address + ":" + e.port + "' is already being used by some other program.");
console.error("You probably need to stop that program or restart your computer.");
return;
}
console.error(e.code + ": '" + e.address + ":" + e.port + "'");
}

let p = 80;
require("http").createServer(greenlock.middleware(require("redirect-https")())).listen(p, function() {
console.log("Success! Bound to port '" + p + "' to handle ACME challenges and redirect to https");
}).on("error", function(e) {
console.log("Did not successfully create http server and bind to port '" + p + "':");
explainError(e);
process.exit(0);
});

p = 443;
greenlock.tlsOptions.allowHTTP1 = true;
server = require("http2").createSecureServer(greenlock.tlsOptions, greenlock.middleware(greenlock.app)).listen(p, function() {
console.log("Success! Serving https on port '" + p + "'");
}).on("error", function(e) {
console.log("Did not successfully create https server and bind to port '" + p + "':");
explainError(e);
process.exit(0);
});

I think it’s fairly self-explained.

Part 3: TLS 1.3

There is still one barrier to the edge of technology: TLS 1.3

OpenSSL 1.1.1 added support for TLS 1.3, but Node.js still have issues adopting it.

Part 4: QUIC

QUIC from Google is a strange thing: It transfers HTTP/2 requests over UDP, not TCP.

The author of nghttp2 has started a project to implement it, and it’s called ngtcp2.

Maybe it will be the future, maybe someday Node.js will have a package to consume it. I think this will be interesting.