@small-tech/auto-encrypt 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,18 @@
1
1
  # Auto Encrypt
2
2
 
3
- Adds automatic provisioning and renewal of [Let’s Encrypt](https://letsencrypt.org) TLS certificates with [OCSP Stapling](https://letsencrypt.org/docs/integration-guide/#implement-ocsp-stapling) to [Node.js](https://nodejs.org) [https](https://nodejs.org/dist/latest-v12.x/docs/api/https.html) servers (including [Express.js](https://expressjs.com/), etc.)
3
+ Automatically provisions and renews [Let’s Encrypt](https://letsencrypt.org) TLS certificates on [Node.js](https://nodejs.org) [https](https://nodejs.org/dist/latest-v12.x/docs/api/https.html) servers (including [Kitten](https://kitten.small-web.org), [Polka](https://github.com/lukeed/polka/tree/next), [Express.js](https://expressjs.com/), etc.)
4
4
 
5
5
  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.
6
6
 
7
7
  ## How it works
8
8
 
9
- 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.
9
+ The first time your web site is hit, it takes a couple of seconds to load as your Let’s Encrypt TLS certificates are automatically provisioned for you. From there on, your certificates are seamlessly renewed 30 days before their expiry date.
10
10
 
11
- When not provisioning certificates, Auto Encrypt will also forward HTTP calls to HTTPS on your server.
11
+ When not provisioning certificates, Auto Encrypt also forwards HTTP calls to HTTPS on your server.
12
12
 
13
13
  ## Compatibility
14
14
 
15
- All tests pass on Node.js LTS (18.2+).
15
+ All tests pass on Node.js LTS (version 22).
16
16
 
17
17
  [Tests fail on Node.js 19 (socket hang up error).](https://codeberg.org/small-tech/auto-encrypt/issues/3)
18
18
 
@@ -49,7 +49,7 @@ npm i @small-tech/auto-encrypt
49
49
 
50
50
  ### Example
51
51
 
52
- The following code creates an HTTPS server running on port 443 with [OCSP Stapling](https://letsencrypt.org/docs/integration-guide/#implement-ocsp-stapling) that automatically provisions and renews TLS certificates from [Let’s Encrypt](https://letsencrypt.org) for the domains _<hostname>_ and _www.<hostname>_.
52
+ The following code creates an HTTPS server running on port 443 that automatically provisions and renews TLS certificates from [Let’s Encrypt](https://letsencrypt.org) for the domains _<hostname>_ and _www.<hostname>_.
53
53
 
54
54
  ```js
55
55
  import AutoEncrypt from '@small-tech/auto-encrypt'
@@ -69,7 +69,7 @@ server.close(() => {
69
69
  })
70
70
  ```
71
71
 
72
- 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”](#a-note-on-linux-and-the-security-farce-that-is-priviliged-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](https://sitejs.org)).
72
+ 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”](#a-note-on-linux-and-the-security-farce-that-is-priviliged-ports). If you just need a [Small Web](https://ar.al/2024/06/24/small-web-computer-science-colloquium-at-university-of-groningen/) server that handles all that and more for you (or to see how to implement privilege escalation seamlessly in your own servers, see [Kitten](https://kitten.small-web.org)).
73
73
 
74
74
  ## Configuration
75
75
 
@@ -77,6 +77,8 @@ You can customise the default configuration by adding Auto Encrypt-specific opti
77
77
 
78
78
  You can specify the domains you want the certificate to support, whether the Let’s Encrypt staging server or a local [Pebble](https://github.com/letsencrypt/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.
79
79
 
80
+ (Auto Encrypt uses [Node Pebble](https://codeberg.org/small-tech/node-pebble) to enable testing with a local Pebble server from Node.js.)
81
+
80
82
  ### Example
81
83
 
82
84
  ```js
@@ -111,7 +113,7 @@ When you’re ready to exit your app, just call the `server.close()` method as y
111
113
 
112
114
  ## Developer documentation
113
115
 
114
- If you want to help improve Auto Encrypt or better understand how it is structured and operates, please see the [developer documentation](https://github.com/small-tech/auto-encrypt/blob/master/developer-documentation.md).
116
+ If you want to help improve Auto Encrypt or better understand how it is structured and operates, please see the [developer documentation](https://codeberg.org/small-tech/auto-encrypt/src/branch/main/developer-documentation.md).
115
117
 
116
118
  ## Examples
117
119
 
@@ -177,17 +179,17 @@ If you’re evaluating this for a “startup” or an enterprise, let us save yo
177
179
 
178
180
  ## Client details
179
181
 
180
- 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.
182
+ 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, and automatically renews your certificate thereafter. When not provisioning certificates, it forwards any HTTP requests that your machine gets to HTTPS.
181
183
 
182
- Auto Encrypt __does not_ and __will not__:
184
+ __Auto Encrypt _does not_ and _will not:___
183
185
 
184
- - Implement wildcard certificates. For most [small tech](https://small-tech.org/about/#small-technology) 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.
186
+ - __Implement wildcard certificates.__ For most [small tech](https://small-tech.org/about/#small-technology) 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.
185
187
 
186
- - Implement DNS-01 or any other methods that cannot be fully automated.
188
+ - __Implement DNS-01__ or any other methods that cannot be fully automated.
187
189
 
188
190
  ## Staging and production server behaviour and rate limits
189
191
 
190
- 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.
192
+ By default, Auto Encrypt uses Let’s Encrypt’s production environment. This is most likely what you want as it means your HTTPS server will Just Work™, i.e., provision its TLS certificate automatically the first time the server is hit via its hostname and from thereon automatically renew the certificate a month ahead of its expiry date.
191
193
 
192
194
  However, be aware that the production server has [rate limits](https://letsencrypt.org/docs/rate-limits/).
193
195
 
@@ -203,24 +205,24 @@ From lower-level to higher-level:
203
205
 
204
206
  ### Auto Encrypt Localhost
205
207
 
206
- - Source: https://source.small-tech.org/site.js/lib/auto-encrypt-localhost
208
+ - Source: https://codeberg.org/small-tech/auto-encrypt-localhost
207
209
  - Package: [@small-tech/auto-encrypt-localhost](https://www.npmjs.com/package/@small-tech/auto-encrypt-localhost)
208
210
 
209
- Automatically provisions and installs locally-trusted TLS certificates for Node.js https servers (including Express.js, etc.) using [mkcert](https://github.com/FiloSottile/mkcert/).
211
+ Automatically provisions and installs locally-trusted TLS certificates for Node.js https servers in 100% JavaScript (without any native dependencies like mkcert and certutil).
210
212
 
211
213
  ### HTTPS
212
214
 
213
215
  - Source: https://source.small-tech.org/site.js/lib/https
214
216
  - Package: [@small-tech/https](https://www.npmjs.com/package/@small-tech/https)
215
217
 
216
- A drop-in [standard Node.js HTTPS module](https://nodejs.org/dist/latest-v12.x/docs/api/https.html) replacement with both automatic development-time (localhost) certificates via Auto Encrypt Localhost and automatic production certificates via Auto Encrypt.
218
+ Drop-in replace for the [standard Node.js HTTPS module](https://nodejs.org/dist/latest-v12.x/docs/api/https.html) replacement that automatically handles TLS certificate provisioning and renewal both at localhost (via Auto Encrypt Localhost) and at hostname (via Auto Encrypt).
217
219
 
218
- ### Site.js
220
+ ### Kitten
219
221
 
220
- - Web site: https://sitejs.org
221
- - Source: https://source.small-tech.org/site.js/app
222
+ - Site: https://kitten.small-web.org
223
+ - Source: https://codeberg.org/kitten/app
222
224
 
223
- A complete [small technology](https://small-tech.org/about/#small-technology) tool for developing, testing, and deploying a secure static or dynamic personal web site or app with zero configuration.
225
+ A [💕 Small Web](https://ar.al/2020/08/07/what-is-the-small-web/) development kit.
224
226
 
225
227
  ## Tests and coverage
226
228
 
@@ -228,7 +230,7 @@ This project aims for > 80% coverage. At a recent check, coverage was at 97.42%
228
230
 
229
231
  To see the current state of code coverage, run `npm run coverage`.
230
232
 
231
- For more details, please see the [developer documentation](https://github.com/small-tech/auto-encrypt/blob/master/developer-documentation.md#tests).
233
+ For more details, please see the [developer documentation](https://codeberg.org/small-tech/auto-encrypt/src/branch/main/developer-documentation.md#tests).
232
234
 
233
235
  ## A note on Linux and the security farce that is “privileged ports”
234
236
 
package/index.js CHANGED
@@ -14,7 +14,6 @@
14
14
  import os from 'os'
15
15
  import util from 'util'
16
16
  import https from 'https'
17
- import ocsp from 'ocsp'
18
17
  import monkeyPatchTls from './lib/staging/monkeyPatchTls.js'
19
18
  import LetsEncryptServer from './lib/LetsEncryptServer.js'
20
19
  import Configuration from './lib/Configuration.js'
@@ -77,9 +76,6 @@ export default class AutoEncrypt {
77
76
  */
78
77
  static get https () { return AutoEncrypt }
79
78
 
80
-
81
- static ocspCache = null
82
-
83
79
  /**
84
80
  * Automatically manages Let’s Encrypt certificate provisioning and renewal for Node.js
85
81
  * https servers using the HTTP-01 challenge on first hit of an HTTPS route via use of
@@ -174,7 +170,7 @@ export default class AutoEncrypt {
174
170
  }
175
171
  }
176
172
 
177
- const server = this.addOcspStapling(https.createServer(options, listener))
173
+ const server = https.createServer(options, listener)
178
174
 
179
175
  //
180
176
  // Monkey-patch the server.
@@ -212,25 +208,11 @@ export default class AutoEncrypt {
212
208
  }
213
209
 
214
210
 
215
- /**
216
- * The OCSP module does not have a means of clearing its cache check timers
217
- * so we do it here. (Otherwise, the test suite would hang.)
218
- */
219
- static clearOcspCacheTimers () {
220
- if (this.ocspCache !== null) {
221
- const cacheIds = Object.keys(this.ocspCache.cache)
222
- cacheIds.forEach(cacheId => {
223
- clearInterval(this.ocspCache.cache[cacheId].timer)
224
- })
225
- }
226
- }
227
-
228
211
  /**
229
212
  * Shut Auto Encrypt down. Do this before app exit. Performs necessary clean-up and removes
230
213
  * any references that might cause the app to not exit.
231
214
  */
232
215
  static shutdown () {
233
- this.clearOcspCacheTimers()
234
216
  this.certificate.stopCheckingForRenewal()
235
217
  }
236
218
 
@@ -238,64 +220,6 @@ export default class AutoEncrypt {
238
220
  // Private.
239
221
  //
240
222
 
241
- /**
242
- * Adds Online Certificate Status Protocol (OCSP) stapling (also known as TLS Certificate Status Request extension)
243
- * support to the passed server instance.
244
- *
245
- * @private
246
- * @param {https.Server} server HTTPS server instance without OCSP Stapling support.
247
- * @returns {https.Server} HTTPS server instance with OCSP Stapling support.
248
- */
249
- static addOcspStapling(server) {
250
- // OCSP stapling
251
- //
252
- // Many browsers will fetch OCSP from Let’s Encrypt when they load your site. This is a performance and privacy
253
- // problem. Ideally, connections to your site should not wait for a secondary connection to Let’s Encrypt. Also,
254
- // OCSP requests tell Let’s Encrypt which sites people are visiting. We have a good privacy policy and do not record
255
- // individually identifying details from OCSP requests, we’d rather not even receive the data in the first place.
256
- // Additionally, we anticipate our bandwidth costs for serving OCSP every time a browser visits a Let’s Encrypt site
257
- // for the first time will be a big part of our infrastructure expense.
258
- //
259
- // By turning on OCSP Stapling, you can improve the performance of your website, provide better privacy protections
260
- // … and help Let’s Encrypt efficiently serve as many people as possible.
261
- //
262
- // (Source: https://letsencrypt.org/docs/integration-guide/implement-ocsp-stapling)
263
-
264
- this.ocspCache = new ocsp.Cache()
265
- const cache = this.ocspCache
266
-
267
- server.on('OCSPRequest', (certificate, issuer, callback) => {
268
-
269
- if (certificate == null) {
270
- return callback(new Error('Cannot OCSP staple: certificate not yet provisioned.'))
271
- }
272
-
273
- ocsp.getOCSPURI(certificate, function(error, uri) {
274
- if (error) return callback(error)
275
- if (uri === null) return callback()
276
-
277
- const request = ocsp.request.generate(certificate, issuer)
278
-
279
- cache.probe(request.id, (error, cached) => {
280
- if (error) return callback(error)
281
-
282
- if (cached !== false) {
283
- return callback(null, cached.response)
284
- }
285
-
286
- const options = {
287
- url: uri,
288
- ocsp: request.data
289
- }
290
-
291
- cache.request(request.id, options, callback);
292
- })
293
- })
294
- })
295
-
296
- return server
297
- }
298
-
299
223
  // Custom object description for console output (for debugging).
300
224
  static [util.inspect.custom] () {
301
225
  return `
@@ -369,7 +369,7 @@ export default class Certificate {
369
369
  const issuer = certificate.issuer.value[0][0].value.toString('utf-8').slice(2).trim()
370
370
  const issuedAt = new Date(certificate.validity.notBefore.value)
371
371
  const expiresAt = new Date(certificate.validity.notAfter.value)
372
- const subject = certificate.subject.value[0][0].value.toString('utf-8').slice(2).trim()
372
+ const subject = certificate.subject.value.length > 0 ? certificate.subject.value[0][0].value.toString('utf-8').slice(2).trim() : '(No subject)'
373
373
 
374
374
  const alternativeNames = ((certificate.extensions.filter(extension => {
375
375
  return extension.extnID === 'subjectAlternativeName'
package/lib/Order.js CHANGED
@@ -31,6 +31,7 @@ const throws = new Throws()
31
31
  export default class Order {
32
32
  #data = null
33
33
  #headers = null
34
+ #location = null
34
35
  #order = null
35
36
  #certificate = null
36
37
  #certificateIdentity = null
@@ -65,6 +66,10 @@ export default class Order {
65
66
  get data () { return this.#data }
66
67
  set data (value) {
67
68
  this.#data = value
69
+ // It seems that Let’s Encrypt are no longer sending the location
70
+ // back on status checks, only on order finalisation, so let’s\
71
+ // make sure we don’t accidentally.
72
+ if (this.#data.headers.location !== undefined) this.#location = this.#data.headers.location
68
73
  this.#headers = this.#data.headers
69
74
  this.#order = this.#data.body
70
75
  }
@@ -168,10 +173,12 @@ export default class Order {
168
173
  try {
169
174
  if (numAttempts === 1) {
170
175
  // Finalise using CSR.
176
+ log(' 📝 ❨auto-encrypt❩ Finalising using CSR.')
171
177
  this.data = await (new FinaliseOrderRequest()).execute(this.finaliseUrl, csr)
172
178
  } else {
173
179
  // Check for order status.
174
- this.data = await (new CheckOrderStatusRequest()).execute(this.#headers.location)
180
+ log(' 👀 ❨auto-encrypt❩ Checking for order status.')
181
+ this.data = await (new CheckOrderStatusRequest()).execute(this.#location)
175
182
  }
176
183
  } catch (error) {
177
184
  // TODO: Handle error.
package/lib/acmeCsr.js CHANGED
@@ -53,12 +53,6 @@ function csrAsPem (domains, key) {
53
53
  extensions: [{
54
54
  name: 'subjectAltName',
55
55
  altNames
56
- }, {
57
- // OCSP Must Staple
58
- // RFC 7633. Also see https://scotthelme.co.uk/ocsp-must-staple/
59
- // 1.3.6.1.5.5.7.1.24 = DER(3003020105) (Sequence > Int > 5) *smh* ASN.1 is devil spawn.
60
- id: '1.3.6.1.5.5.7.1.24',
61
- value: forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.SEQUENCE, true, [ forge.asn1.create(forge.asn1.Class.UNIVERSAL, forge.asn1.Type.INTEGER, false, forge.asn1.integerToDer(5).getBytes())])
62
56
  }]
63
57
  }])
64
58
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@small-tech/auto-encrypt",
3
- "version": "4.0.0",
4
- "description": "Adds automatic provisioning and renewal of Let’s Encrypt TLS certificates with OCSP Stapling to Node.js https servers (including Express.js, etc.)",
3
+ "version": "4.1.0",
4
+ "description": "Automatically provisions and renews Let’s Encrypt TLS certificates on Node.js https servers (including Kitten, Polka, Express.js, etc.)",
5
5
  "engines": {
6
6
  "node": ">=18.2.0"
7
7
  },
@@ -68,12 +68,11 @@
68
68
  "encodeurl": "^1.0.2",
69
69
  "jose": "^1.28.2",
70
70
  "moment": "^2.29.4",
71
- "node-forge": "^1.3.1",
72
- "ocsp": "^1.2.0"
71
+ "node-forge": "^1.3.1"
73
72
  },
74
73
  "devDependencies": {
75
74
  "@small-tech/esm-tape-runner": "^1.0.3",
76
- "@small-tech/node-pebble": "^4.2.4",
75
+ "@small-tech/node-pebble": "^5.1.3",
77
76
  "@small-tech/tap-monkey": "^1.3.0",
78
77
  "c8": "^7.12.0",
79
78
  "dependency-cruiser": "^12.3.0",