UNPKG

redbird

Version:

A reverse proxy with support for dynamic tables

127 lines 4.98 kB
/** * Letsecript module for Redbird (c) Optimalbits 2016-2024 * * * */ import { createServer } from 'http'; import path from 'path'; import url from 'url'; import fs from 'fs'; import LeChallengeFs from './third-party/le-challenge-fs.js'; /** * LetsEncrypt certificates are stored like the following: * * /example.com * / * * * */ let leStoreConfig = {}; const webrootPath = ':configDir/:hostname/.well-known/acme-challenge'; function init(certPath, port, logger) { logger === null || logger === void 0 ? void 0 : logger.info('Initializing letsencrypt, path %s, port: %s', certPath, port); leStoreConfig = { configDir: certPath, privkeyPath: ':configDir/:hostname/privkey.pem', fullchainPath: ':configDir/:hostname/fullchain.pem', certPath: ':configDir/:hostname/cert.pem', chainPath: ':configDir/:hostname/chain.pem', workDir: ':configDir/letsencrypt/var/lib', logsDir: ':configDir/letsencrypt/var/log', webrootPath, debug: false, }; // we need to proxy for example: 'example.com/.well-known/acme-challenge' -> 'localhost:port/example.com/' return createServer(function (req, res) { if (req.method !== 'GET') { res.statusCode = 405; // Method Not Allowed res.end(); return; } const reqPath = url.parse(req.url).pathname; const basePath = path.resolve(certPath); const safePath = path.normalize(reqPath).replace(/^(\.\.[\/\\])+/, ''); // Prevent directory traversal const fullPath = path.join(basePath, safePath); if (!fullPath.startsWith(basePath)) { logger === null || logger === void 0 ? void 0 : logger.info(`Attempted directory traversal attack: ${req.url}`); res.statusCode = 403; // Forbidden res.end('Access denied'); return; } logger === null || logger === void 0 ? void 0 : logger.info('LetsEncrypt CA trying to validate challenge %s', fullPath); fs.stat(fullPath, function (err, stats) { if (err || !stats.isFile()) { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.write('404 Not Found\n'); res.end(); return; } res.writeHead(200); fs.createReadStream(fullPath, 'binary').pipe(res); }); }).listen(port); } /** * Gets the certificates for the given domain. * Handles all the LetsEncrypt protocol. Uses * existing certificates if any, or negotiates a new one. * Returns a promise that resolves to an object with the certificates. * TODO: We should use something like https://github.com/PaquitoSoft/memored/blob/master/index.js * to avoid */ async function getCertificates(domain, email, loopbackPort, production, renew, logger) { const LE = (await import('greenlock')).default; // Storage Backend const leStore = (await import('le-store-certbot')).create(leStoreConfig); // ACME Challenge Handlers const leChallenge = LeChallengeFs.create({ loopbackPort: loopbackPort, webrootPath, debug: false, }); const le = LE.create({ server: production ? 'https://acme-v02.api.letsencrypt.org/directory' : 'https://acme-staging-v02.api.letsencrypt.org/directory', store: leStore, // handles saving of config, accounts, and certificates challenges: { 'http-01': leChallenge }, // handles /.well-known/acme-challege keys and tokens challengeType: 'http-01', // default to this challenge type debug: false, log: function () { logger === null || logger === void 0 ? void 0 : logger.info(arguments, 'Lets encrypt debugger'); }, }); // Check in-memory cache of certificates for the named domain const cert = await le.check({ domains: [domain] }); const opts = { domains: [domain], email: email, agreeTos: true, rsaKeySize: 2048, // 2048 or higher challengeType: 'http-01', }; if (cert) { if (renew) { logger && logger.info('renewing cert for ' + domain); opts.duplicate = true; return le.renew(opts, cert).catch(function (err) { logger && logger.error(err, 'Error renewing certificates for ', domain); }); } else { logger && logger.info('Using cached cert for ' + domain); return cert; } } else { // Register Certificate manually logger === null || logger === void 0 ? void 0 : logger.info('Manually registering certificate for %s', domain); return le.register(opts).catch(function (err) { logger === null || logger === void 0 ? void 0 : logger.error(err, 'Error registering LetsEncrypt certificates'); }); } } export { init, getCertificates }; //# sourceMappingURL=letsencrypt.js.map