@small-tech/auto-encrypt

4.0.0 • Public • Published

Auto Encrypt

Adds automatic provisioning and renewal of Let’s Encrypt TLS certificates with OCSP Stapling to Node.js https servers (including Express.js, etc.)

Implements the subset of RFC 8555 – Automatic Certificate Management Environment (ACME) – necessary for a client to support TLS certificate provisioning from Let’s Encrypt using HTTP-01 challenges.

How it works

The first time your web site is hit, it will take a couple of seconds to load as your Let’s Encrypt TLS certificates are automatically provisioned for you. From there on, your certificates will be seamlessly renewed 30 days before their expiry date.

When not provisioning certificates, Auto Encrypt will also forward HTTP calls to HTTPS on your server.

Compatibility

All tests pass on Node.js LTS (18.2+).

Tests fail on Node.js 19 (socket hang up error).

Installation

npm i @small-tech/auto-encrypt

Usage

Instructions

  1. Import the module:

    import AutoEncrypt from '@small-tech/auto-encrypt'
  2. Prefix your server creation code with a reference to the Auto Encrypt class:

    // const server = https.createServer(…) becomes
    const server = AutoEncrypt.https.createServer()
  3. When done, close your server as you would normally:

    server.close(() => {
      console.log('The server is now closed.')
    })

Example

The following code creates an HTTPS server running on port 443 with OCSP Stapling that automatically provisions and renews TLS certificates from Let’s Encrypt for the domains <hostname> and www.&lt;hostname>.

import AutoEncrypt from '@small-tech/auto-encrypt'

const server = AutoEncrypt.https.createServer((request, response) => {
  response.end('Hello, world')
})

server.listen(() => {
  console.log(`Auto-encrypted HTTPS server is running at ${os.hostname()} and www.${os.hostname()}.`)
})

// Later…

server.close(() => {
  console.log('The server is now closed.')
})

Note that on Linux, ports 80 and 443 require special privileges. Please see A note on Linux and the security farce that is “privileged ports”. If you just need a Node web server that handles all that and more for you (or to see how to implement privilege escalation seamlessly in your own servers, see Site.js).

Configuration

You can customise the default configuration by adding Auto Encrypt-specific options to the options object you pass to the Node https server.

You can specify the domains you want the certificate to support, whether the Let’s Encrypt staging server or a local Pebble server should be used instead of the default production server (useful during development and testing), and to specify a custom settings path for your Let’s Encrypt account and certificate information to be stored in.

Example

import AutoEncrypt from '@small-tech/auto-encrypt'

const options = {
  // Regular HTTPS server and TLS server options, if any, go here.

  // Optional Auto Encrypt options:
  domains: ['first.domain', 'second.domain', /* … */],
  server: AutoEncrypt.server.STAGING,
  settingsPath: '/custom/settings/path'
}

// Pass the options object to https.createServer()
const server = AutoEncrypt.https.createServer(options, listener)

// …

Default options

Here is the full list of Auto Encrypt options (all optional) and their defaults:

  • domains: the hostname of the current computer and the www subdomain at that hostname.
  • server: it will use the production server (which has rate limits). Valid values are AutoEncrypt.server.(PEBBLE|STAGING|PRODUCTION).
  • settingsPath: ~/.small-tech.org/auto-encrypt/

Making a graceful exit

When you’re ready to exit your app, just call the server.close() method as you would normally. This will allow Auto Encrypt to perform housekeeping like shutting own the HTTP server that is used for answering Let’s Encrypt challenges as well as performing HTTP to HTTPS redirections and to destroy its certificate check update interval which, if not destroyed, will prevent your app from exiting.

Developer documentation

If you want to help improve Auto Encrypt or better understand how it is structured and operates, please see the developer documentation.

Examples

Regular https

import AutoEncrypt from '@small-tech/auto-encrypt'

const server = AutoEncrypt.https.createServer({ domains: ['dev.ar.al'] }, (request, response) => {
    response.end('Hello, world!')
})

server.listen(() => {
  console.log('Auto-encrypted HTTPS server is running at https://dev.ar.al')
})

// Later…

server.close(() => {
  console.log('The server is now closed.')
})

Express.js

const express = require('express')
import AutoEncrypt from '@small-tech/auto-encrypt'

const app = express()
app.get('/', (request, response) => {
  response.end('Hello, world!')
})

const server = AutoEncrypt.https.createServer(
  autoEncrypt({ domains: ['dev.ar.al'] }),
  app
)

server.listen(() => {
  console.log('Auto-encrypted Express server is running at https://dev.ar.al')
})


// Later…

server.close(() => {
  console.log('The server is now closed.')
})

Like this? Fund us!

Small Technology Foundation is a tiny, independent not-for-profit.

We exist in part thanks to patronage by people like you. If you share our vision and want to support our work, please become a patron or donate to us today and help us continue to exist.

Audience

This is small technology.

If you’re evaluating this for a “startup” or an enterprise, let us save you some time: this is not the right tool for you. This tool is for individual developers to build personal web sites and apps for themselves and for others in a non-colonial manner that respects the human rights of the people who use them.

Client details

Auto Encrypt does one thing and one thing well: it automatically provisions a Let’s Encrypt TLS certificate for your Node.js https servers using the HTTP-01 challenge method when your server is first hit from its hostname, serves it using OCSP Stapling, and automatically renews your certificate thereafter. And, when not provisioning certificates, it forwards any HTTP requests that your machine gets to HTTPS.

Auto Encrypt _does not and will not:

  • Implement wildcard certificates. For most small tech needs (personal web sites and web apps), you will likely need no more than two domains (the root domain and, due to historic and conventional reasons, the www subdomain). You will definitely not need more than the 100 domains that are supported per certificate. If you do, chances are you are looking to use Auto Encrypt in a startup or corporate setting, which is not what its for.

  • Implement DNS-01 or any other methods that cannot be fully automated.

Staging and production server behaviour and rate limits

By default, Auto Encrypt will use the Let’s Encrypt production environment. This is most likely what you want as it means that your HTTPS server will Just Work™, provisioning its TLS certificate automatically the first time the server is hit via its hostname and from thereon automatically renewing the certificate a month ahead of its expiry date.

However, be aware that the production server has rate limits.

For testing, Auto Encrypt provides seamless support for using the included local Pebble server or the Let’s Encrypt staging server.

If you do use the staging environment, be aware that browsers will reject the staging certificates unless you trust the Fake LE Root X1 certificate. If testing with an external https client written in Node, you can add the fake root certificate to your trust store temporarily, using the NODE_EXTRA_CA_CERTS environment variable or setting the ca options property when creating your https server. (Note that if you’re testing with the staging or Pebble server from the same process that Auto Encrypt is running in, Auto Encrypt automatically does this for you by monkeypatching Node’s TLS module with the necessary CA certificates.)

Needless to say, do not add the fake certificate root to the same trust store you use for your everyday browsing.

Related projects

From lower-level to higher-level:

Auto Encrypt Localhost

Automatically provisions and installs locally-trusted TLS certificates for Node.js https servers (including Express.js, etc.) using mkcert.

HTTPS

A drop-in standard Node.js HTTPS module replacement with both automatic development-time (localhost) certificates via Auto Encrypt Localhost and automatic production certificates via Auto Encrypt.

Site.js

A complete small technology tool for developing, testing, and deploying a secure static or dynamic personal web site or app with zero configuration.

Tests and coverage

This project aims for > 80% coverage. At a recent check, coverage was at 97.42% (statements), 92.64% (branch), 91.49% (functions), 97.42% (lines).

To see the current state of code coverage, run npm run coverage.

For more details, please see the developer documentation.

A note on Linux and the security farce that is “privileged ports”

Linux has an outdated feature dating from the mainframe days that requires a process that wants to bind to ports < 1024 to have elevated privileges. While this was a security feature in the days of dumb terminals, today it is a security anti-feature. (macOS has dropped this requirement as of macOS Mojave.)

On modern Linux systems, you can disable privileged ports like this:

sudo sysctl -w net.ipv4.ip_unprivileged_port_start=0

Or, if you want to cling to ancient historic relics like a conservative to a racist statue, ensure your Node process has the right to bind to so-called “privileged” ports by issuing the following command before use:

sudo setcap cap_net_bind_service=+ep $(which node)

If you are wrapping your Node app into an executable binary using a module like Nexe, you will have to ensure that every build of your app has that capability set. For an example of how we do this in Site.js, see this listing.

Technical definition

Implements the subset of RFC 8555 – Automatic Certificate Management Environment (ACME) – necessary for a Node.js https server to provision TLS certificates from Let’s Encrypt using the HTTP-01 challenge on first hit of an HTTPS route via use of the Server Name Indication (SNI) callback.

A note on coding conventions

The code uses TC39 class fields with the following naming conventions for private fields:

#property     // safe property access (either via accessor or where property is both safe to get and set)
#_property    // unsafe  property access (should only be used in accessors)

(Documenting these here as private class fields – ‘hashnames’ – are relatively new as of this writing and may not be familiar to some. The use of the underscore to differentiate direct property access from mediated access via an accessor is a project convention.)

Like this? Fund us!

Small Technology Foundation is a tiny, independent not-for-profit.

We exist in part thanks to patronage by people like you. If you share our vision and want to support our work, please become a patron or donate to us today and help us continue to exist.

Copyright

© 2020-present Aral Balkan, Small Technology Foundation.

Let’s Encrypt is a trademark of the Internet Security Research Group (ISRG). All rights reserved. Node.js is a trademark of Joyent, Inc. and is used with its permission. We are not endorsed by or affiliated with Joyent or ISRG.

License

AGPL version 3.0 or later.

Package Sidebar

Install

npm i @small-tech/auto-encrypt

Weekly Downloads

78

Version

4.0.0

License

AGPL-3.0-or-later

Unpacked Size

193 kB

Total Files

35

Last publish

Collaborators

  • aral