@small-tech/auto-encrypt 3.1.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 +50 -43
- package/index.js +6 -77
- package/lib/Certificate.js +1 -1
- package/lib/HttpServer.js +8 -14
- package/lib/Order.js +8 -1
- package/lib/acmeCsr.js +0 -6
- package/lib/test-helpers/index.js +12 -5
- package/package.json +14 -15
package/README.md
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
# Auto Encrypt
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
|
|
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.
|
|
4
6
|
|
|
5
7
|
## How it works
|
|
6
8
|
|
|
7
|
-
The first time your web site is hit, it
|
|
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
|
+
|
|
11
|
+
When not provisioning certificates, Auto Encrypt also forwards HTTP calls to HTTPS on your server.
|
|
12
|
+
|
|
13
|
+
## Compatibility
|
|
8
14
|
|
|
9
|
-
|
|
15
|
+
All tests pass on Node.js LTS (version 22).
|
|
16
|
+
|
|
17
|
+
[Tests fail on Node.js 19 (socket hang up error).](https://codeberg.org/small-tech/auto-encrypt/issues/3)
|
|
10
18
|
|
|
11
19
|
## Installation
|
|
12
20
|
|
|
@@ -19,30 +27,29 @@ npm i @small-tech/auto-encrypt
|
|
|
19
27
|
### Instructions
|
|
20
28
|
|
|
21
29
|
1. Import the module:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import AutoEncrypt from '@small-tech/auto-encrypt'
|
|
33
|
+
```
|
|
26
34
|
|
|
27
35
|
2. Prefix your server creation code with a reference to the Auto Encrypt class:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
// const server = https.createServer(…) becomes
|
|
39
|
+
const server = AutoEncrypt.https.createServer(…)
|
|
40
|
+
```
|
|
33
41
|
|
|
34
42
|
3. When done, close your server as you would normally:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
server.close(() => {
|
|
46
|
+
console.log('The server is now closed.')
|
|
47
|
+
})
|
|
48
|
+
```
|
|
41
49
|
|
|
42
50
|
### Example
|
|
43
51
|
|
|
44
|
-
The following code creates an HTTPS server running on port 443
|
|
45
|
-
|
|
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>_.
|
|
46
53
|
|
|
47
54
|
```js
|
|
48
55
|
import AutoEncrypt from '@small-tech/auto-encrypt'
|
|
@@ -62,7 +69,7 @@ server.close(() => {
|
|
|
62
69
|
})
|
|
63
70
|
```
|
|
64
71
|
|
|
65
|
-
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
|
|
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)).
|
|
66
73
|
|
|
67
74
|
## Configuration
|
|
68
75
|
|
|
@@ -70,6 +77,8 @@ You can customise the default configuration by adding Auto Encrypt-specific opti
|
|
|
70
77
|
|
|
71
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.
|
|
72
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
|
+
|
|
73
82
|
### Example
|
|
74
83
|
|
|
75
84
|
```js
|
|
@@ -94,9 +103,9 @@ const server = AutoEncrypt.https.createServer(options, listener)
|
|
|
94
103
|
|
|
95
104
|
Here is the full list of Auto Encrypt options (all optional) and their defaults:
|
|
96
105
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
106
|
+
- `domains`: the hostname of the current computer and the www subdomain at that hostname.
|
|
107
|
+
- `server`: it will use the production server (which has [rate limits](https://letsencrypt.org/docs/rate-limits/)). Valid values are `AutoEncrypt.server.(PEBBLE|STAGING|PRODUCTION)`.
|
|
108
|
+
- `settingsPath`: _~/.small-tech.org/auto-encrypt/_
|
|
100
109
|
|
|
101
110
|
## Making a graceful exit
|
|
102
111
|
|
|
@@ -104,7 +113,7 @@ When you’re ready to exit your app, just call the `server.close()` method as y
|
|
|
104
113
|
|
|
105
114
|
## Developer documentation
|
|
106
115
|
|
|
107
|
-
If you want to help improve Auto Encrypt or better understand how it is structured and operates, please see the [developer documentation](https://
|
|
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).
|
|
108
117
|
|
|
109
118
|
## Examples
|
|
110
119
|
|
|
@@ -170,18 +179,17 @@ If you’re evaluating this for a “startup” or an enterprise, let us save yo
|
|
|
170
179
|
|
|
171
180
|
## Client details
|
|
172
181
|
|
|
173
|
-
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,
|
|
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.
|
|
174
183
|
|
|
175
|
-
|
|
184
|
+
__Auto Encrypt _does not_ and _will not:___
|
|
176
185
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
- Implement DNS-01 or any other methods that cannot be fully automated.
|
|
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.
|
|
180
187
|
|
|
188
|
+
- __Implement DNS-01__ or any other methods that cannot be fully automated.
|
|
181
189
|
|
|
182
190
|
## Staging and production server behaviour and rate limits
|
|
183
191
|
|
|
184
|
-
By default, Auto Encrypt
|
|
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.
|
|
185
193
|
|
|
186
194
|
However, be aware that the production server has [rate limits](https://letsencrypt.org/docs/rate-limits/).
|
|
187
195
|
|
|
@@ -191,31 +199,30 @@ If you do use the staging environment, be aware that browsers will reject the st
|
|
|
191
199
|
|
|
192
200
|
Needless to say, do not add the fake certificate root to the same trust store you use for your everyday browsing.
|
|
193
201
|
|
|
194
|
-
|
|
195
202
|
## Related projects
|
|
196
203
|
|
|
197
204
|
From lower-level to higher-level:
|
|
198
205
|
|
|
199
206
|
### Auto Encrypt Localhost
|
|
200
207
|
|
|
201
|
-
|
|
202
|
-
|
|
208
|
+
- Source: https://codeberg.org/small-tech/auto-encrypt-localhost
|
|
209
|
+
- Package: [@small-tech/auto-encrypt-localhost](https://www.npmjs.com/package/@small-tech/auto-encrypt-localhost)
|
|
203
210
|
|
|
204
|
-
Automatically provisions and installs locally-trusted TLS certificates for Node.js https servers (
|
|
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).
|
|
205
212
|
|
|
206
213
|
### HTTPS
|
|
207
214
|
|
|
208
|
-
|
|
209
|
-
|
|
215
|
+
- Source: https://source.small-tech.org/site.js/lib/https
|
|
216
|
+
- Package: [@small-tech/https](https://www.npmjs.com/package/@small-tech/https)
|
|
210
217
|
|
|
211
|
-
|
|
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).
|
|
212
219
|
|
|
213
|
-
###
|
|
220
|
+
### Kitten
|
|
214
221
|
|
|
215
|
-
|
|
216
|
-
|
|
222
|
+
- Site: https://kitten.small-web.org
|
|
223
|
+
- Source: https://codeberg.org/kitten/app
|
|
217
224
|
|
|
218
|
-
A
|
|
225
|
+
A [💕 Small Web](https://ar.al/2020/08/07/what-is-the-small-web/) development kit.
|
|
219
226
|
|
|
220
227
|
## Tests and coverage
|
|
221
228
|
|
|
@@ -223,7 +230,7 @@ This project aims for > 80% coverage. At a recent check, coverage was at 97.42%
|
|
|
223
230
|
|
|
224
231
|
To see the current state of code coverage, run `npm run coverage`.
|
|
225
232
|
|
|
226
|
-
For more details, please see the [developer documentation](https://
|
|
233
|
+
For more details, please see the [developer documentation](https://codeberg.org/small-tech/auto-encrypt/src/branch/main/developer-documentation.md#tests).
|
|
227
234
|
|
|
228
235
|
## A note on Linux and the security farce that is “privileged ports”
|
|
229
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'
|
|
@@ -24,6 +23,11 @@ import Throws from './lib/util/Throws.js'
|
|
|
24
23
|
import HttpServer from './lib/HttpServer.js'
|
|
25
24
|
import log from './lib/util/log.js'
|
|
26
25
|
|
|
26
|
+
// This reverts IP address sort order to pre-Node 17 behaviour.
|
|
27
|
+
// See https://github.com/nodejs/node/issues/40537
|
|
28
|
+
import dns from 'node:dns'
|
|
29
|
+
dns.setDefaultResultOrder('ipv4first')
|
|
30
|
+
|
|
27
31
|
// Custom errors thrown by the autoEncrypt function.
|
|
28
32
|
const throws = new Throws({
|
|
29
33
|
[Symbol.for('BusyProvisioningCertificateError')]:
|
|
@@ -72,9 +76,6 @@ export default class AutoEncrypt {
|
|
|
72
76
|
*/
|
|
73
77
|
static get https () { return AutoEncrypt }
|
|
74
78
|
|
|
75
|
-
|
|
76
|
-
static ocspCache = null
|
|
77
|
-
|
|
78
79
|
/**
|
|
79
80
|
* Automatically manages Let’s Encrypt certificate provisioning and renewal for Node.js
|
|
80
81
|
* https servers using the HTTP-01 challenge on first hit of an HTTPS route via use of
|
|
@@ -169,7 +170,7 @@ export default class AutoEncrypt {
|
|
|
169
170
|
}
|
|
170
171
|
}
|
|
171
172
|
|
|
172
|
-
const server =
|
|
173
|
+
const server = https.createServer(options, listener)
|
|
173
174
|
|
|
174
175
|
//
|
|
175
176
|
// Monkey-patch the server.
|
|
@@ -207,25 +208,11 @@ export default class AutoEncrypt {
|
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
|
|
210
|
-
/**
|
|
211
|
-
* The OCSP module does not have a means of clearing its cache check timers
|
|
212
|
-
* so we do it here. (Otherwise, the test suite would hang.)
|
|
213
|
-
*/
|
|
214
|
-
static clearOcspCacheTimers () {
|
|
215
|
-
if (this.ocspCache !== null) {
|
|
216
|
-
const cacheIds = Object.keys(this.ocspCache.cache)
|
|
217
|
-
cacheIds.forEach(cacheId => {
|
|
218
|
-
clearInterval(this.ocspCache.cache[cacheId].timer)
|
|
219
|
-
})
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
211
|
/**
|
|
224
212
|
* Shut Auto Encrypt down. Do this before app exit. Performs necessary clean-up and removes
|
|
225
213
|
* any references that might cause the app to not exit.
|
|
226
214
|
*/
|
|
227
215
|
static shutdown () {
|
|
228
|
-
this.clearOcspCacheTimers()
|
|
229
216
|
this.certificate.stopCheckingForRenewal()
|
|
230
217
|
}
|
|
231
218
|
|
|
@@ -233,64 +220,6 @@ export default class AutoEncrypt {
|
|
|
233
220
|
// Private.
|
|
234
221
|
//
|
|
235
222
|
|
|
236
|
-
/**
|
|
237
|
-
* Adds Online Certificate Status Protocol (OCSP) stapling (also known as TLS Certificate Status Request extension)
|
|
238
|
-
* support to the passed server instance.
|
|
239
|
-
*
|
|
240
|
-
* @private
|
|
241
|
-
* @param {https.Server} server HTTPS server instance without OCSP Stapling support.
|
|
242
|
-
* @returns {https.Server} HTTPS server instance with OCSP Stapling support.
|
|
243
|
-
*/
|
|
244
|
-
static addOcspStapling(server) {
|
|
245
|
-
// OCSP stapling
|
|
246
|
-
//
|
|
247
|
-
// Many browsers will fetch OCSP from Let’s Encrypt when they load your site. This is a performance and privacy
|
|
248
|
-
// problem. Ideally, connections to your site should not wait for a secondary connection to Let’s Encrypt. Also,
|
|
249
|
-
// OCSP requests tell Let’s Encrypt which sites people are visiting. We have a good privacy policy and do not record
|
|
250
|
-
// individually identifying details from OCSP requests, we’d rather not even receive the data in the first place.
|
|
251
|
-
// Additionally, we anticipate our bandwidth costs for serving OCSP every time a browser visits a Let’s Encrypt site
|
|
252
|
-
// for the first time will be a big part of our infrastructure expense.
|
|
253
|
-
//
|
|
254
|
-
// By turning on OCSP Stapling, you can improve the performance of your website, provide better privacy protections
|
|
255
|
-
// … and help Let’s Encrypt efficiently serve as many people as possible.
|
|
256
|
-
//
|
|
257
|
-
// (Source: https://letsencrypt.org/docs/integration-guide/implement-ocsp-stapling)
|
|
258
|
-
|
|
259
|
-
this.ocspCache = new ocsp.Cache()
|
|
260
|
-
const cache = this.ocspCache
|
|
261
|
-
|
|
262
|
-
server.on('OCSPRequest', (certificate, issuer, callback) => {
|
|
263
|
-
|
|
264
|
-
if (certificate == null) {
|
|
265
|
-
return callback(new Error('Cannot OCSP staple: certificate not yet provisioned.'))
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
ocsp.getOCSPURI(certificate, function(error, uri) {
|
|
269
|
-
if (error) return callback(error)
|
|
270
|
-
if (uri === null) return callback()
|
|
271
|
-
|
|
272
|
-
const request = ocsp.request.generate(certificate, issuer)
|
|
273
|
-
|
|
274
|
-
cache.probe(request.id, (error, cached) => {
|
|
275
|
-
if (error) return callback(error)
|
|
276
|
-
|
|
277
|
-
if (cached !== false) {
|
|
278
|
-
return callback(null, cached.response)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
const options = {
|
|
282
|
-
url: uri,
|
|
283
|
-
ocsp: request.data
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
cache.request(request.id, options, callback);
|
|
287
|
-
})
|
|
288
|
-
})
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
return server
|
|
292
|
-
}
|
|
293
|
-
|
|
294
223
|
// Custom object description for console output (for debugging).
|
|
295
224
|
static [util.inspect.custom] () {
|
|
296
225
|
return `
|
package/lib/Certificate.js
CHANGED
|
@@ -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/HttpServer.js
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// B. At all other times:
|
|
19
19
|
// ======================
|
|
20
20
|
//
|
|
21
|
-
// Forwards http requests to https requests using a
|
|
21
|
+
// Forwards http requests to https requests using a 307 redirect.
|
|
22
22
|
//
|
|
23
23
|
// Copyright © 2020 Aral Balkan, Small Technology Foundation.
|
|
24
24
|
// License: AGPLv3 or later.
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
|
|
28
28
|
import http from 'http'
|
|
29
29
|
import encodeUrl from 'encodeurl'
|
|
30
|
-
import enableDestroy from 'server-destroy'
|
|
31
30
|
import log from './util/log.js'
|
|
32
31
|
|
|
33
32
|
export default class HttpServer {
|
|
@@ -120,18 +119,13 @@ export default class HttpServer {
|
|
|
120
119
|
response.end()
|
|
121
120
|
}
|
|
122
121
|
})
|
|
123
|
-
|
|
124
|
-
// Enable server to be destroyed without waiting for any existing connections to close.
|
|
125
|
-
// (While there shouldn’t be any existing connections and while the likelihood of someone
|
|
126
|
-
// trying to denial-of-service this very low, it’s still the right thing to do.)
|
|
127
|
-
enableDestroy(this.server)
|
|
128
122
|
}
|
|
129
123
|
|
|
130
124
|
set challengeServer (state) {
|
|
131
125
|
if (state) {
|
|
132
126
|
log(` 🔒 ❨auto-encrypt❩ HTTP server is now only responding to Let’s Encrypt challenges.`)
|
|
133
127
|
} else {
|
|
134
|
-
log(` 🔒 ❨auto-encrypt❩ HTTP server is now forwarding HTTP requests to HTTPS (
|
|
128
|
+
log(` 🔒 ❨auto-encrypt❩ HTTP server is now forwarding HTTP requests to HTTPS (307).`)
|
|
135
129
|
}
|
|
136
130
|
this.#isChallengeServer = state
|
|
137
131
|
}
|
|
@@ -156,16 +150,16 @@ export default class HttpServer {
|
|
|
156
150
|
|
|
157
151
|
async destroy () {
|
|
158
152
|
// Starts killing all connections and closes the server.
|
|
159
|
-
this.server.
|
|
153
|
+
this.server.closeAllConnections()
|
|
160
154
|
|
|
161
|
-
// Wait until the server is closed before returning.
|
|
162
155
|
await new Promise((resolve, reject) => {
|
|
163
|
-
this.server.
|
|
156
|
+
this.server.close(error => {
|
|
157
|
+
if (error) {
|
|
158
|
+
console.error(error)
|
|
159
|
+
reject(error)
|
|
160
|
+
}
|
|
164
161
|
resolve()
|
|
165
162
|
})
|
|
166
|
-
this.server.on('error', (error) => {
|
|
167
|
-
reject(error)
|
|
168
|
-
})
|
|
169
163
|
})
|
|
170
164
|
}
|
|
171
165
|
}
|
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
|
-
|
|
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
|
|
|
@@ -8,7 +8,6 @@ import fs from 'fs'
|
|
|
8
8
|
import os from 'os'
|
|
9
9
|
import path from 'path'
|
|
10
10
|
import http from 'http'
|
|
11
|
-
import enableServerDestroy from 'server-destroy'
|
|
12
11
|
import Configuration from '../Configuration.js'
|
|
13
12
|
import LetsEncryptServer from '../LetsEncryptServer.js'
|
|
14
13
|
import Throws from '../util/Throws.js'
|
|
@@ -19,7 +18,7 @@ import log from '../util/log.js'
|
|
|
19
18
|
//
|
|
20
19
|
|
|
21
20
|
const throws = new Throws({
|
|
22
|
-
[Symbol.for('MockServerCouldNotBeStartedError')]:
|
|
21
|
+
[Symbol.for('MockServerCouldNotBeStartedError')]: error => `Mock server could not be started (${error})`
|
|
23
22
|
})
|
|
24
23
|
|
|
25
24
|
export class MockServer {
|
|
@@ -45,7 +44,6 @@ export class MockServer {
|
|
|
45
44
|
|
|
46
45
|
async create () {
|
|
47
46
|
const server = http.createServer(this.#responseHandler)
|
|
48
|
-
enableServerDestroy(server)
|
|
49
47
|
this.#server = server
|
|
50
48
|
await new Promise((resolve, reject) => {
|
|
51
49
|
try {
|
|
@@ -60,12 +58,21 @@ export class MockServer {
|
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
async destroy () {
|
|
63
|
-
this.#server.
|
|
61
|
+
this.#server.closeAllConnections()
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
this.#server.close(error => {
|
|
64
|
+
if (error) {
|
|
65
|
+
console.error(error)
|
|
66
|
+
reject(error)
|
|
67
|
+
}
|
|
68
|
+
resolve()
|
|
69
|
+
})
|
|
70
|
+
})
|
|
64
71
|
}
|
|
65
72
|
}
|
|
66
73
|
|
|
67
74
|
export async function httpServerWithResponse (mockResponse) {
|
|
68
|
-
return new Promise((resolve,
|
|
75
|
+
return new Promise((resolve, _reject) => {
|
|
69
76
|
const server = http.createServer((request, response) => {
|
|
70
77
|
response.statusCode = mockResponse.statusCode
|
|
71
78
|
response.end(mockResponse.body)
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@small-tech/auto-encrypt",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
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
|
+
"engines": {
|
|
6
|
+
"node": ">=18.2.0"
|
|
7
|
+
},
|
|
5
8
|
"keywords": [
|
|
6
9
|
"let's encrypt",
|
|
7
10
|
"acme",
|
|
@@ -10,6 +13,7 @@
|
|
|
10
13
|
"tls",
|
|
11
14
|
"auto encrypt",
|
|
12
15
|
"small tech",
|
|
16
|
+
"small web",
|
|
13
17
|
"automatic"
|
|
14
18
|
],
|
|
15
19
|
"author": {
|
|
@@ -60,24 +64,19 @@
|
|
|
60
64
|
]
|
|
61
65
|
},
|
|
62
66
|
"dependencies": {
|
|
63
|
-
"bent": "
|
|
67
|
+
"bent": "^7.3.12",
|
|
64
68
|
"encodeurl": "^1.0.2",
|
|
65
|
-
"jose": "^1.
|
|
66
|
-
"moment": "^2.
|
|
67
|
-
"node-forge": "^1.3.1"
|
|
68
|
-
"ocsp": "^1.2.0",
|
|
69
|
-
"server-destroy": "^1.0.1"
|
|
69
|
+
"jose": "^1.28.2",
|
|
70
|
+
"moment": "^2.29.4",
|
|
71
|
+
"node-forge": "^1.3.1"
|
|
70
72
|
},
|
|
71
73
|
"devDependencies": {
|
|
72
74
|
"@small-tech/esm-tape-runner": "^1.0.3",
|
|
73
|
-
"@small-tech/node-pebble": "^
|
|
75
|
+
"@small-tech/node-pebble": "^5.1.3",
|
|
74
76
|
"@small-tech/tap-monkey": "^1.3.0",
|
|
75
|
-
"c8": "^7.
|
|
76
|
-
"dependency-cruiser": "^
|
|
77
|
-
"esbuild": "^0.8.53",
|
|
78
|
-
"jsdoc": "^3.6.6",
|
|
77
|
+
"c8": "^7.12.0",
|
|
78
|
+
"dependency-cruiser": "^12.3.0",
|
|
79
79
|
"jsdoc-to-markdown": "^6.0.1",
|
|
80
|
-
"tape": "^5.2.1"
|
|
81
|
-
"wtfnode": "^0.8.1"
|
|
80
|
+
"tape": "^5.2.1"
|
|
82
81
|
}
|
|
83
82
|
}
|