UNPKG

@small-tech/node-pebble

Version:

A Node.js wrapper for Let’s Encrypt’s Pebble (“a small RFC 8555 ACME test server not suited for a production certificate authority”).

151 lines (125 loc) 4.67 kB
#!/usr/bin/env node /** npm post-install script Downloads, installs, and configures Pebble. */ import os from 'os' import fs from 'fs' import path from 'path' import https from 'https' import { extract } from 'tar' const __dirname = new URL('.', import.meta.url).pathname /** Gets resource at passed URL string using HTTPS. @param { string } url */ async function secureGet (url) { return new Promise((resolve, reject) => { https.get(url, response => { const statusCode = response.statusCode const location = response.headers.location // Reject if it’s not one of the status codes we are testing. if (statusCode !== 200 && statusCode !== 302) { reject({statusCode}) } let body = '' response.on('data', _ => body += _) response.on('end', () => { resolve({statusCode, location, body}) }) }) }) } /** Streams resource at passed URL string to file at passed file path. @param { string } url @param { string } filePath */ async function secureStreamToFile (url, filePath) { return new Promise((resolve, reject) => { const fileStream = fs.createWriteStream(filePath) https.get(url, response => { response.pipe(fileStream) fileStream.on('finish', () => { fileStream.close() resolve() }) fileStream.on('error', error => { fs.unlinkSync(filePath) reject(error) }) }) }) } // // Sanity check: ensure we’re on a supported platform (Linux, macOS, or Windows) and bail if not. // const _platform = os.platform() if (_platform !== 'linux' && _platform !== 'darwin' && _platform !== 'win32' ) { throw new Error(`Node Pebble Error: unsupported platform (only Linux, macOS, and Windows are supported, not ${_platform}).`) } // // Pebble configuration file (adds shortlived profile). // const configuration = `{ "pebble": { "listenAddress": "0.0.0.0:14000", "managementListenAddress": "0.0.0.0:15000", "certificate": "test/certs/localhost/cert.pem", "privateKey": "test/certs/localhost/key.pem", "httpPort": 80, "tlsPort": 443, "externalAccountBindingRequired": false, "profiles": { "default": { "description": "The profile you know and love", "validityPeriod": 7776000 }, "shortlived": { "description": "A short-lived cert profile, without actual enforcement", "validityPeriod": 518400 } } } } ` // // Install the Pebble binary. // const PEBBLE_VERSION = 'v2.9.0' const architecture = os.arch() const platform = _platform === 'win32' ? 'windows' : _platform const binaryExtension = _platform === 'win32' ? '.exe' : '' const binaryName = `pebble${binaryExtension}` const binaryPath = path.join(__dirname, binaryName) const configurationFilePath = path.join(__dirname, 'test', 'config', 'pebble-config.json') const downloadFileName = `pebble-${platform}-${architecture === 'x64' ? 'amd64' : architecture}.tar.gz` const downloadUrl = `https://github.com/letsencrypt/pebble/releases/download/${PEBBLE_VERSION}/${downloadFileName}` const archivePath = path.join(__dirname, downloadFileName) console.log(' Node Pebble (postinstall)') console.log(' ────────────────────────────────────────────────────────────────────────') process.stdout.write(` ╰─ Removing old Pebble archive and binary (if any)… `) fs.rmSync(archivePath, {force: true}) fs.rmSync(binaryPath, {force: true}) process.stdout.write('done.\n') process.stdout.write(` ╰─ Downloading Pebble ${PEBBLE_VERSION} archive… `) const binaryRedirectUrl = (await secureGet(downloadUrl)).location await secureStreamToFile(binaryRedirectUrl, archivePath) process.stdout.write('done.\n') process.stdout.write(` ╰─ Unarchiving Pebble archive… `) await extract({ cwd: __dirname, file: archivePath, strip: 3 }) process.stdout.write('done.\n') process.stdout.write(` ╰─ Making the binary executable… `) fs.chmodSync(binaryPath, 0o755) process.stdout.write('done.\n') process.stdout.write(` ╰─ Configuring Pebble… `) fs.writeFileSync(configurationFilePath, configuration) process.stdout.write('done.\n') process.stdout.write(` ╰─ Deleting Pebble archive… `) fs.rmSync(archivePath) process.stdout.write('done.\n') console.log(' ────────────────────────────────────────────────────────────────────────')