npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

greenlock-cluster

v3.0.0

Published

Use automatic letsencrypt (free ssl certs) on multiple cores or even multiple machines

Downloads

753

Readme

Replaced: Use Greenlock Express v3

See https://git.rootprojects.org/root/greenlock-express.js

"use strict";

var pkg = require("./package.json");
require("greenlock-express")
    .init(function getConfig() {
        // Greenlock Config

        return {
            package: { name: pkg.name, version: pkg.version },
            maintainerEmail: pkg.author,

            // put cluster on full throttle!
            cloudnative: true
            webscale: true
            cluster: true
        };
    })
    .serve(httpsWorker);

function httpsWorker(glx) {
    // Serves on 80 and 443
    // Get's SSL certificates magically!

    glx.serveApp(function(req, res) {
        res.end("Hello, Encrypted World!");
    });
}

OLD STUFF BELOW

(Preserved for historical reference)

| A Root Project | greenlock (lib) | greenlock-cli | greenlock-express | greenlock-cluster | greenlock-koa | greenlock-hapi |

greenlock-cluster

(previously letsencrypt-cluster)

Use automatic letsencrypt with node on multiple cores or even multiple machines.

  • Take advantage of multi-core computing
  • Process certificates in master
  • Serve https from multiple workers
  • Can work with any clustering strategy #1

Install

npm install --save [email protected]

Usage

In a cluster environment you have some main file that boots your app and then conditionally loads certain code based on whether that fork is the master or just a worker.

In such a file you might want to define some of the options that need to be shared between both the master and the worker, like this:

boot.js:

"use strict";

var cluster = require("cluster");
var path = require("path");
var os = require("os");

var main;
var sharedOptions = {
    webrootPath: path.join(os.tmpdir(), "acme-challenge"), // /tmp/acme-challenge
    // used by le-challenge-fs, the default plugin

    renewWithin: 14 * 24 * 60 * 60 * 1000, // 10 days before expiration

    debug: true
};

if (cluster.isMaster) {
    main = require("./master");
} else {
    main = require("./worker");
}

main.init(sharedOptions);

Master

We think it makes the most sense to load greenlock in master. This can prevent race conditions (see node-letsencrypt#45) as only one process is writing the to file system or database at a time.

The main implementation detail here is approveDomains(options, certs, cb) for new domain certificates and potentially agreeToTerms(opts, cb) for new accounts.

The master takes the same arguments as node-greenlock (challenge, store, etc), plus a few extra (approveDomains... okay, just one extra):

master.js:

'use strict';

var cluster = require('cluster');

module.exports.init = function (sharedOpts) {
  var cores = require('os').cpus();
  var leMaster = require('greenlock-cluster/master').create({
    debug: sharedOpts.debug

    // You MUST change this to 'https://acme-v02.api.letsencrypt.org/directory' in production
    server: 'https://acme-staging-v02.api.letsencrypt.org/directory'
  , version: 'draft-11' // Let's Encrypt v2

  , renewWithin: sharedOpts.renewWithin

  , webrootPath: sharedOpts.webrootPath

  , approveDomains: function (masterOptions, certs, cb) {
      // Do any work that must be done by master to approve this domain
      // (in this example, it's assumed to be done by the worker)

      var results = { domain: masterOptions.domain                          // required
                    , options: masterOptions                                // domains, email, agreeTos
                    , certs: certs };                                       // altnames, privkey, cert
      cb(null, results);
    }
  });

  cores.forEach(function () {
    var worker = cluster.fork();
    leMaster.addWorker(worker);
  });
};

API

All options are passed directly to node-greenlock (in other works, leMaster is a greenlock instance), but a few are only actually used by greenlock-cluster.

  • leOptions.approveDomains(options, certs, cb) is special for greenlock-cluster, but will probably be included in node-greenlock in the future (no API change).

  • leMaster.addWorker(worker) is added by greenlock-cluster and must be called for each new worker.

Worker

The worker takes similar arguments to node-greenlock, but only ones that are useful for determining certificate renewal and for le.challenge.get.

If you want to a non-default le.challenge

worker.js:

"use strict";

module.exports.init = function(sharedOpts) {
    var leWorker = require("greenlock-cluster/worker").create({
        debug: sharedOpts.debug,

        renewWithin: sharedOpts.renewWithin,

        webrootPath: sharedOpts.webrootPath,

        // , challenge: require('le-challenge-fs').create({ webrootPath: '...', ... })

        approveDomains: function(workerOptions, certs, cb) {
            // opts = { domains, email, agreeTos, tosUrl }
            // certs = { subject, altnames, expiresAt, issuedAt }

            var results = {
                domain: workerOptions.domains[0],
                options: {
                    domains: workerOptions.domains
                },
                certs: certs
            };

            if (certs) {
                // modify opts.domains to match the original request
                // email is not necessary, because the account already exists
                // this will only fail if the account has become corrupt
                results.options.domains = certs.altnames;
                cb(null, results);
                return;
            }

            // This is where one would check one's application-specific database:
            //   1. Lookup the domain to see which email it belongs to
            //   2. Assign a default email if it isn't in the system
            //   3. If the email has no le account, `agreeToTerms` will fire unless `agreeTos` is preset

            results.options.email = "[email protected]";
            results.options.agreeTos = true; // causes agreeToTerms to be skipped
            cb(null, results);
        }
    });

    function app(req, res) {
        res.end("Hello, World!");
    }

    var redirectHttps = require("redirect-https")();
    var plainServer = require("http").createServer(leWorker.middleware(redirectHttps));
    plainServer.listen(80);

    var server = require("https").createServer(leWorker.httpsOptions, leWorker.middleware(app));
    server.listen(443);
};

API

node-greenlock is not used directly by the worker, but certain options are shared because certain logic is duplicated.

  • leOptions.renewWithin is shared so that the worker knows how earlier to request a new cert
  • leOptions.renewBy is passed to le-sni-auto so that it staggers renewals between renewWithin (latest) and renewBy (earlier)
  • leWorker.middleware(nextApp) uses greenlock/middleware for GET-ing http-01, hence sharedOptions.webrootPath
  • leWorker.httpsOptions has a default localhost certificate and the SNICallback.

There are a few options that aren't shown in these examples, so if you need to change something that isn't shown here, look at the code (it's not that much) or open an issue.

Message Passing

The master and workers will communicate through process.on('message', fn), process.send({}), worker.on('message', fn)and worker.send({}).

All messages have a type property which is a string and begins with LE_. All other messages are ignored.