Generate Let's Encrypt certificates in Node!
I wanted to see how difficult it is to generate Let's Encrypt certificates in Node without using an external package like greenlock
or exec
-ing certbot
in child processes.
Essentially this boils down to creating keypairs, signing and sending HTTPS requests to Let's Encrypt's API endpoints. Not a huge lift; however, certnode
is pretty limited right now. Extending functionality might be a larger effort.
This was primarily a learning exercise but also an effort to create a package that hopefully makes it easy for others to generate certificates for their domains :)
npm i certnode
The example code can be found here.
Note: you must control the domain + email address you pass to client.generateCertificate()
. Also, be sure to allow inbound HTTP traffic (TCP, port 80) in your firewall rules.
const certnode = require('certnode')
const fs = require('fs')
const https = require('https')
const client = new certnode.Client()
// Generate fresh account keys for Let's Encrypt
await client.generateAccountKeyPair()
const { certificate, privateKeyData } = await client.generateCertificate('<domain>', '<email>')
const server = https.createServer({ cert: certificate, key: privateKeyData })
/* register event listeners */
server.listen(443, '0.0.0.0', () => {})
// Account private key is encrypted with passphrase, if provided.
await client.exportAccountKeyPair('<directory>', '[passphrase]')
// Certificate private key is encrypted with passphrase, if provided.
await Promise.all([
fs.promises.writeFile('/path/to/certificate', certificate),
certnode.writeKeyToFile('/path/to/privateKey', privateKeyData, '[passphrase]')
])
const anotherClient = new certnode.Client()
// If you previously exported with passphrase, provide the same passphrase.
await anotherClient.importAccountKeyPair('<directory>', '[passphrase]')
/* generate certificate with `anotherClient` */
const [certificate, privateKeyData] = await Promise.all([
fs.promises.readFile('/path/to/certificate', 'utf8'),
fs.promises.readFile('/path/to/privateKey', 'utf8')
])
// If you previously exported with passphrase, provide the same passphrase.
const server = https.createServer({
cert: certificate,
key: privateKeyData,
passphrase: '[passphrase]'
})
/* register event listeners */
server.listen(443, '0.0.0.0', () => {})
To generate the API docs:
npm run docs
Then open ./out/index.html
in your browser.
sudo domain=<domain> npm test
The test suite sends HTTPS requests to Let's Encrypt (staging environment) and generates certificates.
Therefore, tests must run from a domain
you control, presumably on a VPS. Since certnode
attains certificates through HTTP validation, it must run as root so it can listen on port 80. Make sure firewall rules allow inbound HTTP traffic.
Note: if you run tests several times in quick succession, you may be rate-limited by Let's Encrypt.
npm run lint