Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node 14: Upgrade greenlock and other dependencies #270

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ build/Release
# Deployed apps should consider commenting this line out:
# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
node_modules

# Greenlock config
greenlock.d
1 change: 1 addition & 0 deletions .greenlockrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"configDir":"./greenlock.d","manager":"@greenlock/manager"}
143 changes: 78 additions & 65 deletions lib/letsencrypt.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,37 @@
*
*
*/
var leStoreConfig = {};
var webrootPath = ':configDir/:hostname/.well-known/acme-challenge';
let leStoreConfig = {};
let webrootPath = '';

function init(certPath, port, logger) {
var http = require('http');
var path = require('path');
var url = require('url');
var fs = require('fs');
const http = require('http');
const path = require('path');
const url = require('url');
const fs = require('fs');

logger && 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 = `${certPath}/{domain}/.well-known/acme-challenge`;

webrootPath: webrootPath,
debug: false,
// Storage Backend
leStoreConfig = {
basePath: certPath,
module: require.resolve('greenlock-store-fs'),
privkeyPath: ':basePath/:subject/privkey.pem',
fullchainPath: ':basePath/:subject/fullchain.pem',
certPath: ':basePath/:subject/cert.pem',
chainPath: ':basePath/:subject/chain.pem',

webrootPath,
};

// we need to proxy for example: 'example.com/.well-known/acme-challenge' -> 'localhost:port/example.com/'
http
.createServer(function (req, res) {
var uri = url.parse(req.url).pathname;
var filename = path.join(certPath, uri);
var isForbiddenPath = uri.length < 3 || filename.indexOf(certPath) !== 0;
.createServer((req, res) => {
const uri = url.parse(req.url).pathname;
const filename = path.join(certPath, uri);
const isForbiddenPath = uri.length < 3 || filename.indexOf(certPath) !== 0;

if (isForbiddenPath) {
logger && logger.info('Forbidden request on LetsEncrypt port %s: %s', port, filename);
Expand Down Expand Up @@ -79,59 +79,72 @@ function init(certPath, port, logger) {
* to avoid
*/
function getCertificates(domain, email, production, renew, logger) {
var LE = require('greenlock');
var le;
let path = require('path');
JumpLink marked this conversation as resolved.
Show resolved Hide resolved
let packageRoot = path.dirname(require.resolve('redbird'));
let LE = require('greenlock');
let pkg = require(packageRoot + '/package.json');
let le;

// Storage Backend
var leStore = require('le-store-certbot').create(leStoreConfig);
if (renew) {
logger.warn('renew parameter is depricated and is automatically detected.')
}

// ACME Challenge Handlers
var leChallenge = require('le-challenge-fs').create({
webrootPath: webrootPath,
debug: false,
});
let leChallenge = {
module: require.resolve('acme-http-01-webroot'),
webroot: webrootPath,
debug: !production
}

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 (debug) {
logger && logger.info(arguments, 'Lets encrypt debugger');
packageAgent: pkg.name + '/' + pkg.version,
staging: !production,
maintainerEmail: email,
packageRoot,
configDir: './greenlock.d',
manager: '@greenlock/manager',
notify: (event, details) => {
if ('error' === event) {
// `details` is an error object in this case
logger.error(details);
} else {
// FOr possible events see https://git.rootprojects.org/root/acme.js#events
logger.debug({event, details}, 'notify');
}
},
});

// Check in-memory cache of certificates for the named domain
return le.check({ domains: [domain] }).then(function (cert) {
var 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 && logger.info('Manually registering certificate for %s', domain);
return le.register(opts).catch(function (err) {
logger && logger.error(err, 'Error registering LetsEncrypt certificates');
});
le.manager.defaults({
agreeToTerms: true,
subscriberEmail: email,
challenges: {
// handles /.well-known/acme-challege keys and tokens
'http-01': leChallenge,
},
store: leStoreConfig, // handles saving of config, accounts, and certificates
})

try {
le.add({
subject: domain,
altnames: [domain],
}).then( (event) => {
logger.debug(event, 'After add');
return event;
})
} catch (error) {
logger.error(error, 'Error registering LetsEncrypt certificates for ' + domain);
}

return le.get({ servername: domain }).then((site) => {
if (!site) {
logger.error(domain + ' was not found in any site config');
return;
}
logger.debug(site, 'Get LetsEncrypt certificates for ' + domain)
logger.info('Get LetsEncrypt certificates for ' + domain);
// site.pems.fullchain = site.pems.cert + '\n' + site.pems.chain + '\n';
return site;
});
}

Expand Down
30 changes: 17 additions & 13 deletions lib/proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var http = require('http'),
LRUCache = require('lru-cache'),
routeCache = new LRUCache({ max: 5000 }),
safe = require('safetimeout'),
letsencrypt = require('./letsencrypt.js'),
letsencrypt = require('./letsencrypt.js');
Promise = require('bluebird');

var ONE_DAY = 60 * 60 * 24 * 1000;
Expand All @@ -35,7 +35,11 @@ function ReverseProxy(opts) {
log = this.log = pino(
opts.bunyan || {
name: 'redbird',
}
prettyPrint: {
levelFirst: true
},
prettifier: require('pino-pretty'),
},
);
}

Expand Down Expand Up @@ -290,11 +294,11 @@ ReverseProxy.prototype.setupHttpsProxy = function (proxy, websocketsUpgrade, log
https = sslOpts.serverModule || require('https');
}

var httpsServer = (this.httpsServer = https.createServer(ssl, function (req, res) {
var httpsServer = (this.httpsServer = https.createServer(ssl, (req, res) => {
JumpLink marked this conversation as resolved.
Show resolved Hide resolved
var src = _this._getSource(req);
var httpProxyOpts = Object.assign({}, _this.opts.httpProxy);

_this._getTarget(src, req, res).then(function (target) {
_this._getTarget(src, req, res).then((target) => {
JumpLink marked this conversation as resolved.
Show resolved Hide resolved
if (target) {
httpProxyOpts.target = target;
proxy.web(req, res, httpProxyOpts);
Expand All @@ -306,12 +310,12 @@ ReverseProxy.prototype.setupHttpsProxy = function (proxy, websocketsUpgrade, log

httpsServer.on('upgrade', websocketsUpgrade);

httpsServer.on('error', function (err) {
httpsServer.on('error', (err) => {
log && log.error(err, 'HTTPS Server Error');
});

httpsServer.on('clientError', function (err) {
log && log.error(err, 'HTTPS Client Error');
httpsServer.on('clientError', (err) => {
log && log.error(err, 'HTTPS Client Error');
});

log && log.info('Listening to HTTPS requests on port %s', sslOpts.port);
Expand Down Expand Up @@ -451,18 +455,18 @@ ReverseProxy.prototype.updateCertificates = function (
) {
var _this = this;
return letsencrypt.getCertificates(domain, email, production, renew, this.log).then(
function (certs) {
if (certs) {
function (site) {
if (site && site.pems) {
var opts = {
key: certs.privkey,
cert: certs.cert + certs.chain,
key: site.pems.privkey,
cert: site.pems.cert + site.pems.chain,
};
_this.certs[domain] = tls.createSecureContext(opts).context;

//
// TODO: cluster friendly
//
var renewTime = certs.expiresAt - Date.now() - renewWithin;
var renewTime = site.expiresAt - Date.now() - renewWithin;
renewTime =
renewTime > 0 ? renewTime : _this.opts.letsencrypt.minRenewTime || 60 * 60 * 1000;

Expand Down Expand Up @@ -681,7 +685,7 @@ ReverseProxy.prototype._getTarget = function (src, req, res) {
req.host = target.host;
}

if (route.opts.onRequest) {
if (route.opts && route.opts.onRequest) {
const resultFromRequestHandler = route.opts.onRequest(req, res, target);
if (resultFromRequestHandler !== undefined) {
this.log &&
Expand Down
Loading