This documentation includes the implementation details of Auto Encrypt and is intended to aid you if you’re trying to improve, debug, or get a deeper understanding of Auto Encrypt.
If you just want to use Auto Encrypt, please see the public API, as documented in the README.
The developer documentation is generated using jsdoc-to-markdown from the developer-documentation.hbs template.
The dependency diagram generation process as part of this requires Graphviz (dot
command) to be installed. e.g., on Ubuntu:
sudo apt install graphviz
To update the documentation:
npm run generate-developer-documentation
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.
Auto Encrypt is supported on:
- Node: LTS (currently 14.16.0).
- ECMAScript: ES2019
Not shown (for clarity): third-party Node modules, the util
namespace with helper modules – for logging, error handling, and an async forEach
implementation – and the typedefs
namespace with JSDoc type definitions.
Generated using dependency cruiser.
Main test tasks use an automatically-managed local Pebble server instance with settings optimised for performance.
-
Add
pebble
as an alias for127.0.0.1
and::1
in your /etc/hosts file. If you’re running in a container (e.g., using DistroBox/podman on Fedora Silverblue, make sure you set this in the etc/hosts file of the host system, not the container)) e.g.# Loopback entries; do not change. # For historical reasons, localhost precedes localhost.localdomain: 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 pebble ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 pebble
-
To run staging and production tests (you do not need to do this to run the Pebble tests): make sure that your system is reachable from your hostname (e.g., by using a service like ngrok.)
npm -s run test
Tests should also pass with Pebble’s default settings and with the Let’s Encrypt staging server. The full set of test tasks are:
Name | Server | Sleep? | Nonce reject? | Debug output? |
---|---|---|---|---|
test | Pebble | ✘ | ✘ | ✘ |
test-debug | Pebble | ✘ | ✘ | ✔ |
test-pebble-sleep | Pebble | ✔ | ✘ | ✘ |
test-pebble-sleep-debug | Pebble | ✔ | ✘ | ✔ |
test-pebble-sleep-noncereject | Pebble | ✔ | ✔ | ✘ |
test-pebble-sleep-noncereject-debug | Pebble | ✔ | ✔ | ✔ |
test-staging | Staging | n/a | n/a | ✘ |
test-staging-debug | Staging | n/a | n/a | ✔ |
There are several different code coverage tasks that correspond to the test tasks. Coverage task names begin with coverage instead of test and there are no debug versions for them.
npm -s run coverage
The full set of coverage tasks are:
Name | Server | Sleep? | Nonce reject? |
---|---|---|---|
coverage | Pebble | ✘ | ✘ |
coverage-pebble-sleep | Pebble | ✔ | ✘ |
coverage-pebble-sleep-noncereject | Pebble | ✔ | ✔ |
coverage-staging | Staging | n/a | n/a |
- @small-tech/auto-encrypt
Automatically provisions and renews Let’s Encrypt™ TLS certificates for Node.js® https servers (including Express.js, etc.)
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.
- lib/AcmeRequest
Abstract base request class for carrying out signed ACME requests over HTTPS.
- lib/Certificate
Represents a Let’s Encrypt TLS certificate.
- lib/Configuration
Global configuration class. Use initialise() method to populate.
- csrAsPem(domains, key) ⇒
String
Create a CSR given a list of domains and a Jose JWK.rsaKey.
- PreparedRequest :
Object
- ProtectedHeader
- HttpsHeaders :
Object
- ResponseObject
Automatically provisions and renews Let’s Encrypt™ TLS certificates for Node.js® https servers (including Express.js, etc.)
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.
License: AGPLv3 or later.
Copyright: © 2020 Aral Balkan, Small Technology Foundation.
- @small-tech/auto-encrypt
- module.exports ⏏
- instance
- .serverType :
LetsEncryptServer.type
- .serverType :
- static
- .https
- .createServer([options]) ⇒
https.Server
- .clearOcspCacheTimers()
- .shutdown()
- .addOcspStapling(server) ⇒
https.Server
℗
- instance
- module.exports ⏏
Auto Encrypt is a static class. Please do not instantiate.
Use: AutoEncrypt.https.createServer(…)
Enumeration.
Kind: instance property of module.exports
Read only: true
By aliasing the https property to the AutoEncrypt static class itself, we enable people to add AutoEncrypt to their existing apps by requiring the module and prefixing their https.createServer(…) line with AutoEncrypt:
Kind: static property of module.exports
Example
import AutoEncrypt from '@small-tech/auto-encrypt'
const server = AutoEncrypt.https.createServer()
Automatically manages Let’s Encrypt certificate provisioning and renewal for Node.js https servers using the HTTP-01 challenge on first hit of an HTTPS route via use of the Server Name Indication (SNI) callback.
Kind: static method of module.exports
Returns: https.Server
- The server instance returned by Node’s https.createServer() method.
Param | Type | Default | Description |
---|---|---|---|
[options] | Object |
Optional HTTPS options object with optional additional Auto Encrypt-specific configuration settings. | |
[options.domains] | Array.<String> |
Domain names to provision TLS certificates for. If missing, defaults to the hostname of the current computer and its www prefixed subdomain. | |
[options.serverType] | Enum |
AutoEncrypt.serverType.PRODUCTION |
Let’s Encrypt server type to use. AutoEncrypt.serverType.PRODUCTION, ….STAGING, or ….PEBBLE (see LetsEncryptServer.type). |
[options.settingsPath] | String |
~/.small-tech.org/auto-encrypt/ |
Path to save certificates/keys to. |
The OCSP module does not have a means of clearing its cache check timers so we do it here. (Otherwise, the test suite would hang.)
Kind: static method of module.exports
Shut Auto Encrypt down. Do this before app exit. Performs necessary clean-up and removes any references that might cause the app to not exit.
Kind: static method of module.exports
Adds Online Certificate Status Protocol (OCSP) stapling (also known as TLS Certificate Status Request extension) support to the passed server instance.
Kind: static method of module.exports
Returns: https.Server
- HTTPS server instance with OCSP Stapling support.
Access: private
Param | Type | Description |
---|---|---|
server | https.Server |
HTTPS server instance without OCSP Stapling support. |
Abstract base request class for carrying out signed ACME requests over HTTPS.
License: AGPLv3 or later.
Copyright: Copyright © 2020 Aral Balkan, Small Technology Foundation.
- lib/AcmeRequest
- module.exports ⏏
- .execute(command, payload, useKid, [successCodes], [url], [parseResponseBodyAsJSON]) ⇒
types.ResponseObject
- ._execute(preparedRequest, parseResponseBodyAsJSON) ⇒
types.ResponseObject
- .getBuffer(stream) ⇒
Buffer
- .prepare(command, payload, useKid, [successCodes], [url]) ⇒
types.PreparedRequest
- .execute(command, payload, useKid, [successCodes], [url], [parseResponseBodyAsJSON]) ⇒
- module.exports ⏏
Abstract base request class for carrying out signed ACME requests over HTTPS.
module.exports.execute(command, payload, useKid, [successCodes], [url], [parseResponseBodyAsJSON]) ⇒ types.ResponseObject
Executes a remote Let’s Encrypt command and either returns the result or throws.
Kind: instance method of module.exports
Param | Type | Default | Description |
---|---|---|---|
command | String |
Name of Directory command to invoke e.g. 'newAccount' | |
payload | Object | String |
Object to use as payload. For no payload, pass empty string. | |
useKid | Boolean |
true |
Use Key ID (true) or public JWK (false) (see RFC 8555 § 6.2). |
[successCodes] | Array.<Number> |
[200] |
Return codes accepted as success. Any other code throws. |
[url] | String |
|
If specified, use this URL, ignoring the command parameter. |
[parseResponseBodyAsJSON] | Boolean |
true |
Parse response body as JSON (true) or as string (false). |
Executes a prepared request.
Kind: instance method of module.exports
Param | Type | Description |
---|---|---|
preparedRequest | types.PreparedRequest |
The prepared request, ready to be executed. |
parseResponseBodyAsJSON | Boolean |
Should the request body be parsed as JSON (true) or should the native response object be returned (false). |
Concatenates the output of a stream and returns a buffer. Taken from the bent module.
Kind: instance method of module.exports
Returns: Buffer
- The concatenated output of the Node stream.
Param | Type | Description |
---|---|---|
stream | stream |
A Node stream. |
Separate the preparation of a request from the execution of it so we can easily test that different request configurations conform to our expectations.
Kind: instance method of module.exports
Param | Type | Default | Description |
---|---|---|---|
command | String |
(Required) Name of Let’s Encrypt command to invoke (see Directory). (sans 'Url' suffix). e.g. 'newAccount', 'newOrder', etc. | |
payload | Object | String |
(Required) Either an object to use as the payload or, if there is no payload, an empty string. | |
useKid | Boolean |
(Required) Should request use a Key ID (true) or, public JWK (false). (See RFC 8555 § 6.2 Request Authentication) | |
[successCodes] | Array.<Number> |
[200] |
Optional array of codes that signals success. Any other code throws. |
[url] | String |
|
If specified, will use this URL directly, ignoring the value in the command parameter. |
Represents a Let’s Encrypt TLS certificate.
License: AGPLv3 or later.
Copyright: Copyright © 2020 Aral Balkan, Small Technology Foundation.
- lib/Certificate
- module.exports ⏏
- new module.exports(domains)
- .attemptToRecoverFromFailedRenewalAttemptIfNecessary()
- async
- .getSecureContext() ⇒
Promise.<tls.SecureContext>
- .createSecureContext(renewCertificate) ⇒
Promise
℗ - .provisionCertificate() ⇒
Promise
℗ - .renewCertificate() ⇒
Promise
℗ - .checkForRenewal() ⇒
Promise
℗
- .getSecureContext() ⇒
- sync
- module.exports ⏏
Represents a Let’s Encrypt TLS certificate.
Creates an instance of Certificate.
Param | Type | Description |
---|---|---|
domains | Array.<String> |
List of domains this certificate covers. |
Check if certificate-identity.pem.old or certificate.pem.old files exist. If they do, it means that something went wrong while certificate was trying to be renewed. So restore them and use them and hopefully the next renewal attempt will succeed or at least buy the administrator of the server some time to fix the issue.
Kind: instance method of module.exports
Get a SecureContext that can be used in an SNICallback.
Kind: instance method of module.exports
Returns: Promise.<tls.SecureContext>
- A promise for a SecureContext that can be used in creating https servers.
Category: async
Creates and caches a secure context, provisioning a TLS certificate in the process, if necessary.
Kind: instance method of module.exports
Returns: Promise
- Fulfils immediately if certificate exists and does not need to be
renewed. Otherwise, fulfils when certificate has been provisioned.
Category: async
Access: private
Param | Type | Default | Description |
---|---|---|---|
renewCertificate | Boolean |
false |
If true, will start the process of renewing the certificate (but will continue to return the existing certificate until it is ready). |
Provisions a new Let’s Encrypt TLS certificate, persists it, and starts checking for renewals on it every day, starting with the next day.
Kind: instance method of module.exports
Returns: Promise
- Fulfils once a certificate has been provisioned.
Category: async
Access: private
Starts the certificate renewal process by requesting the creation of a fresh secure context.
Kind: instance method of module.exports
Returns: Promise
- Resolves once certificate is renewed and new secure context is
created and cached.
Category: async
Access: private
Checks if the certificate needs to be renewed (if it is within 30 days of its expiry date) and, if so, renews it. While the method is async, the result is not awaited on usage. Instead, it is a fire-and-forget method that’s called via a daily interval.
Kind: instance method of module.exports
Returns: Promise
- Fulfils immediately if certificate doesn’t need renewal. Otherwise, fulfils once certificate
has been renewed.
Category: async
Access: private
Starts checking for certificate renewals every 24 hours.
Kind: instance method of module.exports
Category: sync
Access: private
Param | Type | Default | Description |
---|---|---|---|
[alsoCheckNow] | boolean |
false |
If true, will also immediately check for renewal when the function is called (use this when loading a previously-provisioned and persisted certificate from disk). |
Stops the timer that checks for renewal daily. Use this during housekeeping before destroying this object.
Kind: instance method of module.exports
Category: sync
Access: private
Global configuration class. Use initialise() method to populate.
License: AGPLv3 or later.
Copyright: © 2020 Aral Balkan, Small Technology Foundation.
- lib/Configuration
- module.exports ⏏
- new module.exports(settings)
- .server :
LetsEncryptServer
- .domains :
Array.<String>
- .settingsPath :
String
- .accountPath :
String
- .accountIdentityPath :
String
- .certificatePath :
String
- .certificateDirectoryPath :
String
- .certificateIdentityPath :
String
- module.exports ⏏
Initialise the configuration. Must be called before accessing settings. May be called more than once.
Param | Type | Description |
---|---|---|
settings | Object |
Settings to initialise configuration with. |
settings.domains | Array.<String> |
List of domains Auto Encrypt will manage TLS certs for. |
settings.server | LetsEncryptServer |
Let’s Encrypt Server to use. |
settings.settingsPath | String |
Root settings path to use. Will use default path if null. |
The Let’s Encrypt Server instance.
Kind: instance property of module.exports
Read only: true
List of domains that Auto Encrypt will manage TLS certificates for.
Kind: instance property of module.exports
Read only: true
The root settings path. There is a different root settings path for pebble, staging and production modes.
Kind: instance property of module.exports
Read only: true
Path to the account.json file that contains the Key Id that uniquely identifies and authorises your account in the absence of a JWT (see RFC 8555 § 6.2. Request Authentication).
Kind: instance property of module.exports
Read only: true
The path to the account-identity.pem file that contains the private key for the account.
Kind: instance property of module.exports
Read only: true
The path to the certificate.pem file that contains the certificate chain provisioned from Let’s Encrypt.
Kind: instance property of module.exports
Read only: true
The directory the certificate and certificate identity (private key) PEM files are stored in.
Kind: instance property of module.exports
Read only: true
The path to the certificate-identity.pem file that holds the private key for the TLS certificate.
Kind: instance property of module.exports
Read only: true
Create a CSR given a list of domains and a Jose JWK.rsaKey.
Kind: global function
Returns: String
- A CSR in PEM format.
Param | Type |
---|---|
domains | Array.<String> |
key | JWK.rsaKey |
Kind: global typedef
Properties
Name | Type | Description |
---|---|---|
protectedHeader | ProtectedHeader |
JSON Web Signature (JWS) Protected Header (See RFC 7515 § A.6.1) |
signedRequest | JWS.FlattenedJWS |
Flattened JWS |
httpsRequest | bent.RequestFunction.<bent.ValidResponse> |
Asynchronous HTTPs request, ready to be executed. |
httpsHeaders | HttpsHeaders |
Hardcoded HTTPS headers. |
Kind: global typedef
Properties
Name | Type | Description |
---|---|---|
alg | String |
Hardcoded to 'RS256', currently the only algorithm supported by Let’s Encrypt (LE). |
nonce | String |
Nonce (a value that’s used only once to thwart replay attacks). |
url | String |
URL of the command on Let’s Encrypt’s servers. |
kid | String |
Key ID returned by LE (per RFC 8555 § 6.2, set either this or jwk, not both). |
jwk | JWKRSAKey |
Public JWK (per RFC 8555 § 6.2, set either this or jwk, not both). |
Kind: global typedef
Properties
Name | Type | Description |
---|---|---|
'Content-Type' | String |
Hardcoded to 'application/jose+json' |
'User-Agent' | String |
Hardcoded to 'small-tech.org-acme/1.0.0 node/12.16.0' |
'Accept-Language | String |
Hardcoded to 'en-US' |
Kind: global typedef
Properties
Name | Type | Description |
---|---|---|
headers | Object |
Native HTTPS response headers object. |
body | Object | String |
The response body as a native object or as a string. |
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.
© 2020-2021 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.