From 61fb942dda7a6077ad18302e8b11c2f701bf4234 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 1 Nov 2019 15:14:07 -0600 Subject: [PATCH] whitespace --- .prettierrc | 4 +- README.md | 182 +++++++++--------- config.js | 28 +-- demo.js | 48 ++--- examples/cluster/server.js | 52 ++--- examples/express/my-express-app.js | 6 +- examples/express/server.js | 32 ++-- examples/http-proxy/server.js | 64 +++---- examples/http/server.js | 44 ++--- examples/http2/server.js | 54 +++--- examples/https/server.js | 54 +++--- examples/old-demo.js | 75 ++++++++ examples/old-force-renew.js | 30 +++ examples/old-remote-access.js | 104 ++++++++++ examples/old-vhost.js | 134 +++++++++++++ examples/old-wildcard.js | 77 ++++++++ examples/quickstart/README.md | 16 +- examples/quickstart/server.js | 40 ++-- examples/socket.io/server.js | 54 +++--- examples/websockets/server.js | 64 +++---- greenlock-express.js | 40 ++-- greenlock.js | 104 +++++----- http-middleware.js | 148 +++++++-------- https-middleware.js | 208 ++++++++++---------- lib/compat.js | 34 ++-- main.js | 20 +- master.js | 244 ++++++++++++------------ package-lock.json | 276 +++++++++++++-------------- package.json | 98 +++++----- servers.js | 236 ++++++++++++----------- single.js | 28 +-- sni.js | 296 ++++++++++++++--------------- test/greenlock.js | 114 +++++------ worker.js | 92 ++++----- 34 files changed, 1762 insertions(+), 1338 deletions(-) create mode 100644 examples/old-demo.js create mode 100644 examples/old-force-renew.js create mode 100644 examples/old-remote-access.js create mode 100644 examples/old-vhost.js create mode 100644 examples/old-wildcard.js diff --git a/.prettierrc b/.prettierrc index 76c4a67..d306351 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,7 @@ { "bracketSpacing": true, "printWidth": 120, - "tabWidth": 2, + "tabWidth": 4, "trailingComma": "none", - "useTabs": true + "useTabs": false } diff --git a/README.md b/README.md index d8e9289..244afa7 100644 --- a/README.md +++ b/README.md @@ -22,26 +22,26 @@ Greenlock Express is a **Web Server** with **Fully Automated HTTPS** and renewal "use strict"; function httpsWorker(glx) { - // Serves on 80 and 443 - // Get's SSL certificates magically! + // Serves on 80 and 443 + // Get's SSL certificates magically! - glx.serveApp(function(req, res) { - res.end("Hello, Encrypted World!"); - }); + glx.serveApp(function(req, res) { + res.end("Hello, Encrypted World!"); + }); } var pkg = require("./package.json"); require("greenlock-express") - .init(function getConfig() { - // Greenlock Config + .init(function getConfig() { + // Greenlock Config - return { - package: { name: pkg.name, version: pkg.version }, - maintainerEmail: pkg.author, - cluster: false - }; - }) - .serve(httpsWorker); + return { + package: { name: pkg.name, version: pkg.version }, + maintainerEmail: pkg.author, + cluster: false + }; + }) + .serve(httpsWorker); ``` Manage via API or the config file: @@ -50,44 +50,44 @@ Manage via API or the config file: ```json { - "subscriberEmail": "letsencrypt-test@therootcompany.com", - "agreeToTerms": true, - "sites": { - "example.com": { - "subject": "example.com", - "altnames": ["example.com", "www.example.com"] - } - } + "subscriberEmail": "letsencrypt-test@therootcompany.com", + "agreeToTerms": true, + "sites": { + "example.com": { + "subject": "example.com", + "altnames": ["example.com", "www.example.com"] + } + } } ``` # Let's Encrypt for... -- IoT -- Enterprise On-Prem -- Local Development -- Home Servers -- Quitting Heroku +- IoT +- Enterprise On-Prem +- Local Development +- Home Servers +- Quitting Heroku # Features -- [x] Let's Encrypt v2 (November 2019) - - [x] ACME Protocol (RFC 8555) - - [x] HTTP Validation (HTTP-01) - - [x] DNS Validation (DNS-01) - - [ ] ALPN Validation (TLS-ALPN-01) - - Need ALPN validation? [contact us](mailto:greenlock-support@therootcompany.com) -- [x] Automated HTTPS - - [x] Fully Automatic Renewals every 45 days - - [x] Free SSL - - [x] **Wildcard** SSL - - [x] **Localhost** certificates - - [x] HTTPS-enabled Secure **WebSockets** (`wss://`) -- [x] Fully customizable - - [x] **Reasonable defaults** - - [x] Domain Management - - [x] Key and Certificate Management - - [x] ACME Challenge Plugins +- [x] Let's Encrypt v2 (November 2019) + - [x] ACME Protocol (RFC 8555) + - [x] HTTP Validation (HTTP-01) + - [x] DNS Validation (DNS-01) + - [ ] ALPN Validation (TLS-ALPN-01) + - Need ALPN validation? [contact us](mailto:greenlock-support@therootcompany.com) +- [x] Automated HTTPS + - [x] Fully Automatic Renewals every 45 days + - [x] Free SSL + - [x] **Wildcard** SSL + - [x] **Localhost** certificates + - [x] HTTPS-enabled Secure **WebSockets** (`wss://`) +- [x] Fully customizable + - [x] **Reasonable defaults** + - [x] Domain Management + - [x] Key and Certificate Management + - [x] ACME Challenge Plugins # QuickStart Guide @@ -127,7 +127,7 @@ works with everything. // A plain, node-style app function myPlainNodeHttpApp(req, res) { - res.end("Hello, Encrypted World!"); + res.end("Hello, Encrypted World!"); } // Wrap that plain app in express, @@ -152,9 +152,9 @@ module.exports = app; Greenlock Express is designed with these goals in mind: -- Simplicity and ease-of-use -- Performance and scalability -- Configurability and control +- Simplicity and ease-of-use +- Performance and scalability +- Configurability and control You can start with **near-zero configuration** and slowly add options for greater performance and customization @@ -164,21 +164,21 @@ later, if you need them. ```js require("greenlock-express") - .init(getConfig) - .serve(worker); + .init(getConfig) + .serve(worker); function getConfig() { - return { - // uses name and version as part of the ACME client user-agent - // uses author as the contact for support notices - package: require("./package.json") - }; + return { + // uses name and version as part of the ACME client user-agent + // uses author as the contact for support notices + package: require("./package.json") + }; } function worker(server) { - // Works with any Node app (Express, etc) - var app = require("my-express-app.js"); - server.serveApp(app); + // Works with any Node app (Express, etc) + var app = require("my-express-app.js"); + server.serveApp(app); } ``` @@ -222,14 +222,14 @@ This will update the config file (assuming the default fs-based management plugi ```json { - "subscriberEmail": "letsencrypt-test@therootcompany.com", - "agreeToTerms": true, - "sites": { - "example.com": { - "subject": "example.com", - "altnames": ["example.com", "www.example.com"] - } - } + "subscriberEmail": "letsencrypt-test@therootcompany.com", + "agreeToTerms": true, + "sites": { + "example.com": { + "subject": "example.com", + "altnames": ["example.com", "www.example.com"] + } + } } ``` @@ -269,10 +269,10 @@ npx greenlock add --subject example.com --altnames example.com,www.example.com Note: **Localhost**, **Wildcard**, and Certificates for Private Networks require [**DNS validation**](https://git.rootprojects.org/root/greenlock-exp). -- DNS Validation - - [**Wildcards**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/wildcards/) (coming soon) - - [**Localhost**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/localhost/) (coming soon) - - [**CI/CD**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/ci-cd/) (coming soon) +- DNS Validation + - [**Wildcards**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/wildcards/) (coming soon) + - [**Localhost**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/localhost/) (coming soon) + - [**CI/CD**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/ci-cd/) (coming soon) @@ -280,17 +280,17 @@ Note: **Localhost**, **Wildcard**, and Certificates for Private Networks require **These are in-progress** Check back tomorrow (Nov 2nd, 2019). -- [greenlock-express.js/examples/](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples) - - [Express](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/express/) - - [Node's **http2**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/http2/) - - [Node's https](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/https/) - - [**WebSockets**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/websockets/) - - [Socket.IO](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/socket-io/) - - [Cluster](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/cluster/) - - [**Wildcards**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/wildcards/) (coming soon) - - [**Localhost**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/localhost/) (coming soon) - - [**CI/CD**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/ci-cd/) (coming soon) - - [HTTP Proxy](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/http-proxy/) +- [greenlock-express.js/examples/](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples) + - [Express](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/express/) + - [Node's **http2**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/http2/) + - [Node's https](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/https/) + - [**WebSockets**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/websockets/) + - [Socket.IO](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/socket-io/) + - [Cluster](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/cluster/) + - [**Wildcards**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/wildcards/) (coming soon) + - [**Localhost**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/localhost/) (coming soon) + - [**CI/CD**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/ci-cd/) (coming soon) + - [HTTP Proxy](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/http-proxy/) # Easy to Customize @@ -300,10 +300,10 @@ Note: **Localhost**, **Wildcard**, and Certificates for Private Networks require - [greenlock.js/examples/](https://git.rootprojects.org/root/greenlock.js/src/branch/master/examples) --> -- [Custom Domain Management](https://git.rootprojects.org/root/greenlock-manager-test.js) -- [Custom Key & Cert Storage](https://git.rootprojects.org/root/greenlock-store-test.js) -- [Custom ACME HTTP-01 Challenges](https://git.rootprojects.org/root/acme-http-01-test.js) -- [Custom ACME DNS-01 Challenges](https://git.rootprojects.org/root/acme-dns-01-test.js) +- [Custom Domain Management](https://git.rootprojects.org/root/greenlock-manager-test.js) +- [Custom Key & Cert Storage](https://git.rootprojects.org/root/greenlock-store-test.js) +- [Custom ACME HTTP-01 Challenges](https://git.rootprojects.org/root/acme-http-01-test.js) +- [Custom ACME DNS-01 Challenges](https://git.rootprojects.org/root/acme-dns-01-test.js) # Ready-made Integrations @@ -345,12 +345,12 @@ We're working on more comprehensive documentation for this newly released versio Do you need... -- training? -- specific features? -- different integrations? -- bugfixes, on _your_ timeline? -- custom code, built by experts? -- commercial support and licensing? +- training? +- specific features? +- different integrations? +- bugfixes, on _your_ timeline? +- custom code, built by experts? +- commercial support and licensing? You're welcome to [contact us](mailto:aj@therootcompany.com) in regards to IoT, On-Prem, Enterprise, and Internal installations, integrations, and deployments. diff --git a/config.js b/config.js index 005abd0..3aa97de 100644 --- a/config.js +++ b/config.js @@ -2,19 +2,19 @@ var path = require("path"); module.exports = { - email: "jon.doe@example.com", - configDir: path.join(__dirname, "acme"), - srv: "/srv/www/", - api: "/srv/api/", - proxy: { - "example.com": "http://localhost:4080", - "*.example.com": "http://localhost:4080" - }, + email: "jon.doe@example.com", + configDir: path.join(__dirname, "acme"), + srv: "/srv/www/", + api: "/srv/api/", + proxy: { + "example.com": "http://localhost:4080", + "*.example.com": "http://localhost:4080" + }, - // DNS-01 challenges only - challenges: { - "*.example.com": require("acme-dns-01-YOUR_DNS_HOST").create({ - token: "xxxx" - }) - } + // DNS-01 challenges only + challenges: { + "*.example.com": require("acme-dns-01-YOUR_DNS_HOST").create({ + token: "xxxx" + }) + } }; diff --git a/demo.js b/demo.js index 9e33bed..6399c85 100644 --- a/demo.js +++ b/demo.js @@ -1,35 +1,35 @@ "use strict"; require("./") - .init(initialize) - .serve(worker) - .master(function() { - console.log("Hello from master"); - }); + .init(initialize) + .serve(worker) + .master(function() { + console.log("Hello from master"); + }); function initialize() { - var pkg = require("./package.json"); - var config = { - package: { - name: "Greenlock_Express_Demo", - version: pkg.version, - author: pkg.author - }, - staging: true, - cluster: true, + var pkg = require("./package.json"); + var config = { + package: { + name: "Greenlock_Express_Demo", + version: pkg.version, + author: pkg.author + }, + staging: true, + cluster: true, - notify: function(ev, params) { - console.info(ev, params); - } - }; - return config; + notify: function(ev, params) { + console.info(ev, params); + } + }; + return config; } function worker(glx) { - console.info(); - console.info("Hello from worker #" + glx.id()); + console.info(); + console.info("Hello from worker #" + glx.id()); - glx.serveApp(function(req, res) { - res.end("Hello, Encrypted World!"); - }); + glx.serveApp(function(req, res) { + res.end("Hello, Encrypted World!"); + }); } diff --git a/examples/cluster/server.js b/examples/cluster/server.js index 03e4eea..ba66dc2 100644 --- a/examples/cluster/server.js +++ b/examples/cluster/server.js @@ -3,37 +3,37 @@ var pkg = require("../../package.json"); //require("greenlock-express") require("../../") - .init(function getConfig() { - // Greenlock Config + .init(function getConfig() { + // Greenlock Config - return { - package: { name: "websocket-example", version: pkg.version }, - maintainerEmail: "jon@example.com", + return { + package: { name: "websocket-example", version: pkg.version }, + maintainerEmail: "jon@example.com", - // When you're ready to go full cloud scale, you just change this to true: - // Note: in cluster you CANNOT use in-memory state (see below) - cluster: true, + // When you're ready to go full cloud scale, you just change this to true: + // Note: in cluster you CANNOT use in-memory state (see below) + cluster: true, - // This will default to the number of workers being equal to - // n-1 cpus, with a minimum of 2 - workers: 4 - }; - }) - .serve(httpsWorker); + // This will default to the number of workers being equal to + // n-1 cpus, with a minimum of 2 + workers: 4 + }; + }) + .serve(httpsWorker); function httpsWorker(glx) { - // WRONG - // This won't work like you - // think because EACH worker - // has ITS OWN `count`. - var count = 0; + // WRONG + // This won't work like you + // think because EACH worker + // has ITS OWN `count`. + var count = 0; - var app = function(req, res) { - res.end("Hello... how many times now? Oh, " + count + " times"); - count += 1; - }; + var app = function(req, res) { + res.end("Hello... how many times now? Oh, " + count + " times"); + count += 1; + }; - // Serves on 80 and 443... for each worker - // Get's SSL certificates magically! - glx.serveApp(app); + // Serves on 80 and 443... for each worker + // Get's SSL certificates magically! + glx.serveApp(app); } diff --git a/examples/express/my-express-app.js b/examples/express/my-express-app.js index fada117..aabbb2b 100644 --- a/examples/express/my-express-app.js +++ b/examples/express/my-express-app.js @@ -4,13 +4,13 @@ var express = require("express"); var app = express(); app.use("/", function(req, res) { - res.setHeader("Content-Type", "text/html; charset=utf-8"); - res.end("Hello, World!\n\nšŸ’š šŸ”’.js"); + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end("Hello, World!\n\nšŸ’š šŸ”’.js"); }); // DO NOT DO app.listen() unless we're testing this directly if (require.main === module) { - app.listen(3000); + app.listen(3000); } // Instead do export the app: diff --git a/examples/express/server.js b/examples/express/server.js index 97f5752..3242553 100644 --- a/examples/express/server.js +++ b/examples/express/server.js @@ -1,27 +1,27 @@ "use strict"; function httpsWorker(glx) { - var app = require("./my-express-app.js"); + var app = require("./my-express-app.js"); - app.get("/hello", function(req, res) { - res.end("Hello, Encrypted World!"); - }); + app.get("/hello", function(req, res) { + res.end("Hello, Encrypted World!"); + }); - // Serves on 80 and 443 - // Get's SSL certificates magically! - glx.serveApp(app); + // Serves on 80 and 443 + // Get's SSL certificates magically! + glx.serveApp(app); } var pkg = require("../../package.json"); //require("greenlock-express") require("../../") - .init(function getConfig() { - // Greenlock Config + .init(function getConfig() { + // Greenlock Config - return { - package: { name: "http2-example", version: pkg.version }, - maintainerEmail: "jon@example.com", - cluster: false - }; - }) - .serve(httpsWorker); + return { + package: { name: "http2-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/http-proxy/server.js b/examples/http-proxy/server.js index 3d4cc77..c8bf342 100644 --- a/examples/http-proxy/server.js +++ b/examples/http-proxy/server.js @@ -1,44 +1,44 @@ "use strict"; function httpsWorker(glx) { - // we need the raw https server - var server = glx.httpsServer(); - var proxy = require("http-proxy").createProxyServer({ xfwd: true }); + // we need the raw https server + var server = glx.httpsServer(); + var proxy = require("http-proxy").createProxyServer({ xfwd: true }); - // catches error events during proxying - proxy.on("error", function(err, req, res) { - console.error(err); - res.statusCode = 500; - res.end(); - return; - }); + // catches error events during proxying + proxy.on("error", function(err, req, res) { + console.error(err); + res.statusCode = 500; + res.end(); + return; + }); - // We'll proxy websockets too - server.on("upgrade", function(req, socket, head) { - proxy.ws(req, socket, head, { - ws: true, - target: "ws://localhost:3000" - }); - }); + // We'll proxy websockets too + server.on("upgrade", function(req, socket, head) { + proxy.ws(req, socket, head, { + ws: true, + target: "ws://localhost:3000" + }); + }); - // servers a node app that proxies requests to a localhost - glx.serveApp(function(req, res) { - proxy.web(req, res, { - target: "http://localhost:3000" - }); - }); + // servers a node app that proxies requests to a localhost + glx.serveApp(function(req, res) { + proxy.web(req, res, { + target: "http://localhost:3000" + }); + }); } var pkg = require("../../package.json"); //require("greenlock-express") require("../../") - .init(function getConfig() { - // Greenlock Config + .init(function getConfig() { + // Greenlock Config - return { - package: { name: "http-proxy-example", version: pkg.version }, - maintainerEmail: "jon@example.com", - cluster: false - }; - }) - .serve(httpsWorker); + return { + package: { name: "http-proxy-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/http/server.js b/examples/http/server.js index 28b3c47..8aa11bb 100644 --- a/examples/http/server.js +++ b/examples/http/server.js @@ -11,32 +11,32 @@ var pkg = require("../../package.json"); // Use glx.httpServer(redirectToHttps) instead. function httpsWorker(glx) { - // - // HTTP can only be used for ACME HTTP-01 Challenges - // (and it is not required for DNS-01 challenges) - // + // + // HTTP can only be used for ACME HTTP-01 Challenges + // (and it is not required for DNS-01 challenges) + // - // Get the raw http server: - var httpServer = glx.httpServer(function(req, res) { - res.statusCode = 301; - res.setHeader("Location", "https://" + req.headers.host + req.path); - res.end("Insecure connections are not allowed. Redirecting..."); - }); + // Get the raw http server: + var httpServer = glx.httpServer(function(req, res) { + res.statusCode = 301; + res.setHeader("Location", "https://" + req.headers.host + req.path); + res.end("Insecure connections are not allowed. Redirecting..."); + }); - httpServer.listen(80, "0.0.0.0", function() { - console.info("Listening on ", httpServer.address()); - }); + httpServer.listen(80, "0.0.0.0", function() { + console.info("Listening on ", httpServer.address()); + }); } //require("greenlock-express") require("../../") - .init(function getConfig() { - // Greenlock Config + .init(function getConfig() { + // Greenlock Config - return { - package: { name: "plain-http-example", version: pkg.version }, - maintainerEmail: "jon@example.com", - cluster: false - }; - }) - .serve(httpsWorker); + return { + package: { name: "plain-http-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/http2/server.js b/examples/http2/server.js index 023b3c3..1d663f5 100644 --- a/examples/http2/server.js +++ b/examples/http2/server.js @@ -11,38 +11,38 @@ var pkg = require("../../package.json"); // Use glx.httpsServer(tlsOptions, app) instead. function httpsWorker(glx) { - // - // HTTP2 would have been the default httpsServer for node v12+ - // However... https://github.com/expressjs/express/issues/3388 - // + // + // HTTP2 would have been the default httpsServer for node v12+ + // However... https://github.com/expressjs/express/issues/3388 + // - // Get the raw http2 server: - var http2Server = glx.http2Server(function(req, res) { - res.end("Hello, Encrypted World!"); - }); + // Get the raw http2 server: + var http2Server = glx.http2Server(function(req, res) { + res.end("Hello, Encrypted World!"); + }); - http2Server.listen(443, "0.0.0.0", function() { - console.info("Listening on ", http2Server.address()); - }); + http2Server.listen(443, "0.0.0.0", function() { + console.info("Listening on ", http2Server.address()); + }); - // Note: - // You must ALSO listen on port 80 for ACME HTTP-01 Challenges - // (the ACME and http->https middleware are loaded by glx.httpServer) - var httpServer = glx.httpServer(); - httpServer.listen(80, "0.0.0.0", function() { - console.info("Listening on ", httpServer.address()); - }); + // Note: + // You must ALSO listen on port 80 for ACME HTTP-01 Challenges + // (the ACME and http->https middleware are loaded by glx.httpServer) + var httpServer = glx.httpServer(); + httpServer.listen(80, "0.0.0.0", function() { + console.info("Listening on ", httpServer.address()); + }); } //require("greenlock-express") require("../../") - .init(function getConfig() { - // Greenlock Config + .init(function getConfig() { + // Greenlock Config - return { - package: { name: "http2-example", version: pkg.version }, - maintainerEmail: "jon@example.com", - cluster: false - }; - }) - .serve(httpsWorker); + return { + package: { name: "http2-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/https/server.js b/examples/https/server.js index ace97bf..9ca89c5 100644 --- a/examples/https/server.js +++ b/examples/https/server.js @@ -11,38 +11,38 @@ var pkg = require("../../package.json"); // Use glx.httpsServer(tlsOptions, app) instead. function httpsWorker(glx) { - // - // HTTPS 1.1 is the default - // (HTTP2 would be the default but... https://github.com/expressjs/express/issues/3388) - // + // + // HTTPS 1.1 is the default + // (HTTP2 would be the default but... https://github.com/expressjs/express/issues/3388) + // - // Get the raw https server: - var httpsServer = glx.httpsServer(null, function(req, res) { - res.end("Hello, Encrypted World!"); - }); + // Get the raw https server: + var httpsServer = glx.httpsServer(null, function(req, res) { + res.end("Hello, Encrypted World!"); + }); - httpsServer.listen(443, "0.0.0.0", function() { - console.info("Listening on ", httpsServer.address()); - }); + httpsServer.listen(443, "0.0.0.0", function() { + console.info("Listening on ", httpsServer.address()); + }); - // Note: - // You must ALSO listen on port 80 for ACME HTTP-01 Challenges - // (the ACME and http->https middleware are loaded by glx.httpServer) - var httpServer = glx.httpServer(); - httpServer.listen(80, "0.0.0.0", function() { - console.info("Listening on ", httpServer.address()); - }); + // Note: + // You must ALSO listen on port 80 for ACME HTTP-01 Challenges + // (the ACME and http->https middleware are loaded by glx.httpServer) + var httpServer = glx.httpServer(); + httpServer.listen(80, "0.0.0.0", function() { + console.info("Listening on ", httpServer.address()); + }); } //require("greenlock-express") require("../../") - .init(function getConfig() { - // Greenlock Config + .init(function getConfig() { + // Greenlock Config - return { - package: { name: "https1-example", version: pkg.version }, - maintainerEmail: "jon@example.com", - cluster: false - }; - }) - .serve(httpsWorker); + return { + package: { name: "https1-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/old-demo.js b/examples/old-demo.js new file mode 100644 index 0000000..8000314 --- /dev/null +++ b/examples/old-demo.js @@ -0,0 +1,75 @@ +"use strict"; + +// npm install spdy@3.x + +//var Greenlock = require('greenlock-express') +var Greenlock = require("../"); + +var greenlock = Greenlock.create({ + // Let's Encrypt v2 is ACME draft 11 + version: "draft-11", + + server: "https://acme-v02.api.letsencrypt.org/directory", + // Note: If at first you don't succeed, stop and switch to staging + // https://acme-staging-v02.api.letsencrypt.org/directory + + // You MUST change this to a valid email address + email: "jon@example.com", + + // You MUST NOT build clients that accept the ToS without asking the user + agreeTos: true, + + // You MUST change these to valid domains + // NOTE: all domains will validated and listed on the certificate + approvedDomains: ["example.com", "www.example.com"], + + // You MUST have access to write to directory where certs are saved + // ex: /home/foouser/acme/etc + configDir: "~/.config/acme/", + + // Get notified of important updates and help me make greenlock better + communityMember: true + + //, debug: true +}); + +//////////////////////// +// http-01 Challenges // +//////////////////////// + +// http-01 challenge happens over http/1.1, not http2 +var redirectHttps = require("redirect-https")(); +var acmeChallengeHandler = greenlock.middleware(function(req, res) { + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end( + "

Hello, āš ļø Insecure World!

Visit Secure Site" + + '' + ); +}); +require("http") + .createServer(acmeChallengeHandler) + .listen(80, function() { + console.log("Listening for ACME http-01 challenges on", this.address()); + }); + +//////////////////////// +// http2 via SPDY h2 // +//////////////////////// + +// spdy is a drop-in replacement for the https API +var spdyOptions = Object.assign({}, greenlock.tlsOptions); +spdyOptions.spdy = { protocols: ["h2", "http/1.1"], plain: false }; +var server = require("spdy").createServer( + spdyOptions, + require("express")().use("/", function(req, res) { + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end("

Hello, šŸ” Secure World!

"); + }) +); +server.on("error", function(err) { + console.error(err); +}); +server.on("listening", function() { + console.log("Listening for SPDY/http2/https requests on", this.address()); +}); +server.listen(443); diff --git a/examples/old-force-renew.js b/examples/old-force-renew.js new file mode 100644 index 0000000..6869f6f --- /dev/null +++ b/examples/old-force-renew.js @@ -0,0 +1,30 @@ +"use strict"; + +//require('greenlock-express') +require("../") + .create({ + // Let's Encrypt v2 is ACME draft 11 + version: "draft-11", + + server: "https://acme-v02.api.letsencrypt.org/directory", + // Note: If at first you don't succeed, stop and switch to staging + // https://acme-staging-v02.api.letsencrypt.org/directory + + email: "john.doe@example.com", + + agreeTos: true, + + approvedDomains: ["example.com", "www.example.com"], + + app: require("express")().use("/", function(req, res) { + res.end("Hello, World!"); + }), + + renewWithin: 91 * 24 * 60 * 60 * 1000, + renewBy: 90 * 24 * 60 * 60 * 1000, + + // Get notified of important updates and help me make greenlock better + communityMember: true, + debug: true + }) + .listen(80, 443); diff --git a/examples/old-remote-access.js b/examples/old-remote-access.js new file mode 100644 index 0000000..2852aa1 --- /dev/null +++ b/examples/old-remote-access.js @@ -0,0 +1,104 @@ +"use strict"; + +// +// WARNING: Not for noobs +// Try the simple example first +// + +// +// This demo is used with tunnel-server.js and tunnel-client.js +// + +var email = "john.doe@gmail.com"; +var domains = ["example.com"]; +var agreeLeTos = true; +//var secret = "My Little Brony"; +var secret = require("crypto") + .randomBytes(16) + .toString("hex"); + +require("../") + .create({ + version: "draft-11", + + server: "https://acme-v02.api.letsencrypt.org/directory", + // Note: If at first you don't succeed, stop and switch to staging + // https://acme-staging-v02.api.letsencrypt.org/directory + + email: email, + agreeTos: agreeLeTos, + approveDomains: domains, + configDir: "~/.config/acme/", + app: remoteAccess(secret), + // Get notified of important updates and help me make greenlock better + communityMember: true + //, debug: true + }) + .listen(3000, 8443); + +function remoteAccess(secret) { + var express = require("express"); + var basicAuth = require("express-basic-auth"); + var serveIndex = require("serve-index"); + + var rootIndex = serveIndex("/", { hidden: true, icons: true, view: "details" }); + var rootFs = express.static("/", { dotfiles: "allow", redirect: true, index: false }); + + var userIndex = serveIndex(require("os").homedir(), { hidden: true, icons: true, view: "details" }); + var userFs = express.static(require("os").homedir(), { dotfiles: "allow", redirect: true, index: false }); + + var app = express(); + var realm = "Login Required"; + + var myAuth = basicAuth({ + users: { root: secret, user: secret }, + challenge: true, + realm: realm, + unauthorizedResponse: function(/*req*/) { + return 'Unauthorized Home'; + } + }); + + app.get("/", function(req, res) { + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end('View Files' + "  |  " + 'Logout'); + }); + app.use("/logout", function(req, res) { + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.setHeader("WWW-Authenticate", 'Basic realm="' + realm + '"'); + res.statusCode = 401; + //res.setHeader('Location', '/'); + res.end('Logged out   |   Home'); + }); + app.use("/browse", myAuth); + app.use("/browse", function(req, res, next) { + if ("root" === req.auth.user) { + rootFs(req, res, function() { + rootIndex(req, res, next); + }); + return; + } + if ("user" === req.auth.user) { + userFs(req, res, function() { + userIndex(req, res, next); + }); + return; + } + res.end("Sad Panda"); + }); + + console.log(""); + console.log(""); + console.log("Usernames are\n"); + console.log("\troot"); + console.log("\tuser"); + console.log(""); + console.log("Password (for both) is\n"); + console.log("\t" + secret); + console.log(""); + console.log("Shhhh... It's a secret to everybody!"); + console.log(""); + console.log(""); + + return app; +} diff --git a/examples/old-vhost.js b/examples/old-vhost.js new file mode 100644 index 0000000..ed93322 --- /dev/null +++ b/examples/old-vhost.js @@ -0,0 +1,134 @@ +#!/usr/bin/env node +"use strict"; + +/////////////////// +// vhost example // +/////////////////// + +// +// virtual hosting example +// + +// The prefix where sites go by name. +// For example: whatever.com may live in /srv/www/whatever.com, thus /srv/www is our path +var srv = process.argv[3] || "/srv/www/"; + +var path = require("path"); +var fs = require("fs").promises; +var finalhandler = require("finalhandler"); +var serveStatic = require("serve-static"); + +//var glx = require('greenlock-express') +var glx = require("./").create({ + version: "draft-11", // Let's Encrypt v2 is ACME draft 11 + + server: "https://acme-v02.api.letsencrypt.org/directory", // If at first you don't succeed, stop and switch to staging + // https://acme-staging-v02.api.letsencrypt.org/directory + + configDir: process.argv[4] || "~/.config/acme/", // You MUST have access to write to directory where certs + // are saved. ex: /home/foouser/.config/acme + + approveDomains: myApproveDomains, // Greenlock's wraps around tls.SNICallback. Check the + // domain name here and reject invalid ones + + app: myVhostApp, // Any node-style http app (i.e. express, koa, hapi, rill) + + /* CHANGE TO A VALID EMAIL */ + email: process.argv[2] || "jon.doe@example.com", // Email for Let's Encrypt account and Greenlock Security + agreeTos: true // Accept Let's Encrypt ToS + //, communityMember: true // Join Greenlock to get important updates, no spam + + //, debug: true +}); + +var server = glx.listen(80, 443); +server.on("listening", function() { + console.info(server.type + " listening on", server.address()); +}); + +function myApproveDomains(opts, certs, cb) { + console.log("sni:", opts.domain); + // In this example the filesystem is our "database". + // We check in /srv/www for whatever.com and if it exists, it's allowed + + // SECURITY Greenlock validates opts.domains ahead-of-time so you don't have to + return checkWwws(opts.domains[0]) + .then(function() { + //opts.email = email; + opts.agreeTos = true; + cb(null, { options: opts, certs: certs }); + }) + .catch(cb); +} + +function checkWwws(_hostname) { + if (!_hostname) { + // SECURITY, don't allow access to the 'srv' root + // (greenlock-express uses middleware to check '..', etc) + return ""; + } + var hostname = _hostname; + var _hostdir = path.join(srv, hostname); + var hostdir = _hostdir; + // TODO could test for www/no-www both in directory + return fs + .readdir(hostdir) + .then(function() { + // TODO check for some sort of htaccess.json and use email in that + // NOTE: you can also change other options such as `challengeType` and `challenge` + // opts.challengeType = 'http-01'; + // opts.challenge = require('le-challenge-fs').create({}); + return hostname; + }) + .catch(function() { + if ("www." === hostname.slice(0, 4)) { + // Assume we'll redirect to non-www if it's available. + hostname = hostname.slice(4); + hostdir = path.join(srv, hostname); + return fs.readdir(hostdir).then(function() { + // TODO list both domains? + return hostname; + }); + } else { + // Or check and see if perhaps we should redirect non-www to www + hostname = "www." + hostname; + hostdir = path.join(srv, hostname); + return fs.readdir(hostdir).then(function() { + // TODO list both domains? + return hostname; + }); + } + }) + .catch(function() { + throw new Error("rejecting '" + _hostname + "' because '" + _hostdir + "' could not be read"); + }); +} + +function myVhostApp(req, res) { + // SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to + // (also: only domains approved above will get here) + console.log("vhost:", req.headers.host); + if (!req.headers.host) { + // SECURITY, don't allow access to the 'srv' root + // (greenlock-express uses middleware to check '..', etc) + return res.end(); + } + + // We could cache wether or not a host exists for some amount of time + var fin = finalhandler(req, res); + return checkWwws(req.headers.host) + .then(function(hostname) { + if (hostname !== req.headers.host) { + res.statusCode = 302; + res.setHeader("Location", "https://" + hostname); + // SECURITY this is safe only because greenlock disallows invalid hostnames + res.end(""); + return; + } + var serve = serveStatic(path.join(srv, hostname), { redirect: true }); + serve(req, res, fin); + }) + .catch(function() { + fin(); + }); +} diff --git a/examples/old-wildcard.js b/examples/old-wildcard.js new file mode 100644 index 0000000..349846e --- /dev/null +++ b/examples/old-wildcard.js @@ -0,0 +1,77 @@ +#!/usr/bin/env node +"use strict"; +/*global Promise*/ + +/////////////////////// +// wildcard example // +////////////////////// + +// +// wildcard example +// + +//var glx = require('greenlock-express') +var glx = require("../").create({ + version: "draft-11", // Let's Encrypt v2 is ACME draft 11 + + server: "https://acme-staging-v02.api.letsencrypt.org/directory", + //, server: 'https://acme-v02.api.letsencrypt.org/directory' // If at first you don't succeed, stop and switch to staging + // https://acme-staging-v02.api.letsencrypt.org/directory + + configDir: "~/acme/", // You MUST have access to write to directory where certs + // are saved. ex: /home/foouser/.config/acme + + approveDomains: myApproveDomains, // Greenlock's wraps around tls.SNICallback. Check the + // domain name here and reject invalid ones + + app: require("./my-express-app.js"), // Any node-style http app (i.e. express, koa, hapi, rill) + + /* CHANGE TO A VALID EMAIL */ + email: "jon.doe@example.com", // Email for Let's Encrypt account and Greenlock Security + agreeTos: true, // Accept Let's Encrypt ToS + communityMember: true, // Join Greenlock to (very rarely) get important updates + + //, debug: true + store: require("le-store-fs") +}); + +var server = glx.listen(80, 443); +server.on("listening", function() { + console.info(server.type + " listening on", server.address()); +}); + +function myApproveDomains(opts) { + console.log("sni:", opts.domain); + + // must be 'example.com' or start with 'example.com' + if ( + "example.com" !== opts.domain && + "example.com" !== + opts.domain + .split(".") + .slice(1) + .join(".") + ) { + return Promise.reject(new Error("we don't serve your kind here: " + opts.domain)); + } + + // the primary domain for the cert + opts.subject = "example.com"; + // the altnames (including the primary) + opts.domains = [opts.subject, "*.example.com"]; + + if (!opts.challenges) { + opts.challenges = {}; + } + opts.challenges["http-01"] = require("le-challenge-fs").create({}); + // Note: When implementing a dns-01 plugin you should make it check in a loop + // until it can positively confirm that the DNS changes have propagated. + // That could take several seconds to a few minutes. + opts.challenges["dns-01"] = require("le-challenge-dns").create({}); + + // explicitly set account id and certificate.id + opts.account = { id: opts.email }; + opts.certificate = { id: opts.subject }; + + return Promise.resolve(opts); +} diff --git a/examples/quickstart/README.md b/examples/quickstart/README.md index 0ebffad..f79371b 100644 --- a/examples/quickstart/README.md +++ b/examples/quickstart/README.md @@ -10,13 +10,13 @@ Manage via API or the config file: ```json { - "subscriberEmail": "letsencrypt-test@therootcompany.com", - "agreeToTerms": true, - "sites": { - "example.com": { - "subject": "example.com", - "altnames": ["example.com", "www.example.com"] - } - } + "subscriberEmail": "letsencrypt-test@therootcompany.com", + "agreeToTerms": true, + "sites": { + "example.com": { + "subject": "example.com", + "altnames": ["example.com", "www.example.com"] + } + } } ``` diff --git a/examples/quickstart/server.js b/examples/quickstart/server.js index 82192f2..a45d6ae 100644 --- a/examples/quickstart/server.js +++ b/examples/quickstart/server.js @@ -1,32 +1,32 @@ "use strict"; function httpsWorker(glx) { - // This can be a node http app (shown), - // an Express app, or Hapi, Koa, Rill, etc - var app = function(req, res) { - res.end("Hello, Encrypted World!"); - }; + // This can be a node http app (shown), + // an Express app, or Hapi, Koa, Rill, etc + var app = function(req, res) { + res.end("Hello, Encrypted World!"); + }; - // Serves on 80 and 443 - // Get's SSL certificates magically! - glx.serveApp(app); + // Serves on 80 and 443 + // Get's SSL certificates magically! + glx.serveApp(app); } var pkg = require("../../package.json"); //require("greenlock-express") require("../../") - .init(function getConfig() { - // Greenlock Config + .init(function getConfig() { + // Greenlock Config - return { - // Package name+version is used for ACME client user agent - package: { name: "websocket-example", version: pkg.version }, + return { + // Package name+version is used for ACME client user agent + package: { name: "websocket-example", version: pkg.version }, - // Maintainer email is the contact for critical bug and security notices - maintainerEmail: "jon@example.com", + // Maintainer email is the contact for critical bug and security notices + maintainerEmail: "jon@example.com", - // Change to true when you're ready to make your app cloud-scale - cluster: false - }; - }) - .serve(httpsWorker); + // Change to true when you're ready to make your app cloud-scale + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/socket.io/server.js b/examples/socket.io/server.js index fb8be4d..a729dcb 100644 --- a/examples/socket.io/server.js +++ b/examples/socket.io/server.js @@ -9,41 +9,41 @@ // (see the websocket example) function httpsWorker(glx) { - var socketio = require("socket.io"); - var io; + var socketio = require("socket.io"); + var io; - // we need the raw https server - var server = glx.httpsServer(); + // we need the raw https server + var server = glx.httpsServer(); - io = socketio(server); + io = socketio(server); - // Then you do your socket.io stuff - io.on("connection", function(socket) { - console.log("a user connected"); - socket.emit("Welcome"); + // Then you do your socket.io stuff + io.on("connection", function(socket) { + console.log("a user connected"); + socket.emit("Welcome"); - socket.on("chat message", function(msg) { - socket.broadcast.emit("chat message", msg); - }); - }); + socket.on("chat message", function(msg) { + socket.broadcast.emit("chat message", msg); + }); + }); - // servers a node app that proxies requests to a localhost - glx.serveApp(function(req, res) { - res.setHeader("Content-Type", "text/html; charset=utf-8"); - res.end("Hello, World!\n\nšŸ’š šŸ”’.js"); - }); + // servers a node app that proxies requests to a localhost + glx.serveApp(function(req, res) { + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end("Hello, World!\n\nšŸ’š šŸ”’.js"); + }); } var pkg = require("../../package.json"); //require("greenlock-express") require("../../") - .init(function getConfig() { - // Greenlock Config + .init(function getConfig() { + // Greenlock Config - return { - package: { name: "socket-io-example", version: pkg.version }, - maintainerEmail: "jon@example.com", - cluster: false - }; - }) - .serve(httpsWorker); + return { + package: { name: "socket-io-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/websockets/server.js b/examples/websockets/server.js index c5694ad..952108f 100644 --- a/examples/websockets/server.js +++ b/examples/websockets/server.js @@ -1,42 +1,42 @@ "use strict"; function httpsWorker(glx) { - // we need the raw https server - var server = glx.httpsServer(); - var WebSocket = require("ws"); - var ws = new WebSocket.Server({ server: server }); - ws.on("connection", function(ws, req) { - // inspect req.headers.authorization (or cookies) for session info - ws.send( - "[Secure Echo Server] Hello!\nAuth: '" + - (req.headers.authorization || "none") + - "'\n" + - "Cookie: '" + - (req.headers.cookie || "none") + - "'\n" - ); - ws.on("message", function(data) { - ws.send(data); - }); - }); + // we need the raw https server + var server = glx.httpsServer(); + var WebSocket = require("ws"); + var ws = new WebSocket.Server({ server: server }); + ws.on("connection", function(ws, req) { + // inspect req.headers.authorization (or cookies) for session info + ws.send( + "[Secure Echo Server] Hello!\nAuth: '" + + (req.headers.authorization || "none") + + "'\n" + + "Cookie: '" + + (req.headers.cookie || "none") + + "'\n" + ); + ws.on("message", function(data) { + ws.send(data); + }); + }); - // servers a node app that proxies requests to a localhost - glx.serveApp(function(req, res) { - res.setHeader("Content-Type", "text/html; charset=utf-8"); - res.end("Hello, World!\n\nšŸ’š šŸ”’.js"); - }); + // servers a node app that proxies requests to a localhost + glx.serveApp(function(req, res) { + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end("Hello, World!\n\nšŸ’š šŸ”’.js"); + }); } var pkg = require("../../package.json"); //require("greenlock-express") require("../../") - .init(function getConfig() { - // Greenlock Config + .init(function getConfig() { + // Greenlock Config - return { - package: { name: "websocket-example", version: pkg.version }, - maintainerEmail: "jon@example.com", - cluster: false - }; - }) - .serve(httpsWorker); + return { + package: { name: "websocket-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/greenlock-express.js b/greenlock-express.js index 2000c36..c36d94b 100644 --- a/greenlock-express.js +++ b/greenlock-express.js @@ -17,28 +17,28 @@ var GLE = module.exports; // under the hood. That's the hope, anyway. GLE.init = function(fn) { - if (cluster.isWorker) { - // ignore the init function and launch the worker - return require("./worker.js").create(); - } + if (cluster.isWorker) { + // ignore the init function and launch the worker + return require("./worker.js").create(); + } - var opts = fn(); - if (!opts || "object" !== typeof opts) { - throw new Error( - "the `Greenlock.init(fn)` function should return an object `{ maintainerEmail, packageAgent, notify }`" - ); - } + var opts = fn(); + if (!opts || "object" !== typeof opts) { + throw new Error( + "the `Greenlock.init(fn)` function should return an object `{ maintainerEmail, packageAgent, notify }`" + ); + } - // just for ironic humor - ["cloudnative", "cloudscale", "webscale", "distributed", "blockchain"].forEach(function(k) { - if (opts[k]) { - opts.cluster = true; - } - }); + // just for ironic humor + ["cloudnative", "cloudscale", "webscale", "distributed", "blockchain"].forEach(function(k) { + if (opts[k]) { + opts.cluster = true; + } + }); - if (opts.cluster) { - return require("./master.js").create(opts); - } + if (opts.cluster) { + return require("./master.js").create(opts); + } - return require("./single.js").create(opts); + return require("./single.js").create(opts); }; diff --git a/greenlock.js b/greenlock.js index 6770cbb..c6fff08 100644 --- a/greenlock.js +++ b/greenlock.js @@ -1,29 +1,29 @@ "use strict"; module.exports.create = function(opts) { - opts = parsePackage(opts); - opts.packageAgent = addGreenlockAgent(opts); + opts = parsePackage(opts); + opts.packageAgent = addGreenlockAgent(opts); - var Greenlock = require("@root/greenlock"); - var greenlock = Greenlock.create(opts); + var Greenlock = require("@root/greenlock"); + var greenlock = Greenlock.create(opts); - // re-export as top-level function to simplify rpc with workers - greenlock.getAcmeHttp01ChallengeResponse = function(opts) { - return greenlock.challenges.get(opts); - }; + // re-export as top-level function to simplify rpc with workers + greenlock.getAcmeHttp01ChallengeResponse = function(opts) { + return greenlock.challenges.get(opts); + }; - return greenlock; + return greenlock; }; function addGreenlockAgent(opts) { - // Add greenlock as part of Agent, unless this is greenlock - var packageAgent = opts.packageAgent || ""; - if (!/greenlock(-express|-pro)?/i.test(packageAgent)) { - var pkg = require("./package.json"); - packageAgent += " Greenlock_Express/" + pkg.version; - } + // Add greenlock as part of Agent, unless this is greenlock + var packageAgent = opts.packageAgent || ""; + if (!/greenlock(-express|-pro)?/i.test(packageAgent)) { + var pkg = require("./package.json"); + packageAgent += " Greenlock_Express/" + pkg.version; + } - return packageAgent.trim(); + return packageAgent.trim(); } // ex: "John Doe (https://john.doe)" @@ -32,46 +32,46 @@ function addGreenlockAgent(opts) { // ex: "john@example.com" var looseEmailRe = /(^|[\s<])([^'" <>:;`]+@[^'" <>:;`]+\.[^'" <>:;`]+)/; function parsePackage(opts) { - // 'package' is sometimes a reserved word - var pkg = opts.package || opts.pkg; - if (!pkg) { - opts.maintainerEmail = parseMaintainer(opts.maintainerEmail); - return opts; - } + // 'package' is sometimes a reserved word + var pkg = opts.package || opts.pkg; + if (!pkg) { + opts.maintainerEmail = parseMaintainer(opts.maintainerEmail); + return opts; + } - if (!opts.packageAgent) { - var err = "missing `package.THING`, which is used for the ACME client user agent string"; - if (!pkg.name) { - throw new Error(err.replace("THING", "name")); - } - if (!pkg.version) { - throw new Error(err.replace("THING", "version")); - } - opts.packageAgent = pkg.name + "/" + pkg.version; - } + if (!opts.packageAgent) { + var err = "missing `package.THING`, which is used for the ACME client user agent string"; + if (!pkg.name) { + throw new Error(err.replace("THING", "name")); + } + if (!pkg.version) { + throw new Error(err.replace("THING", "version")); + } + opts.packageAgent = pkg.name + "/" + pkg.version; + } - if (!opts.maintainerEmail) { - try { - opts.maintainerEmail = pkg.author.email || pkg.author.match(looseEmailRe)[2]; - } catch (e) {} - } - if (!opts.maintainerEmail) { - throw new Error("missing or malformed `package.author`, which is used as the contact for support notices"); - } - opts.package = undefined; - opts.maintainerEmail = parseMaintainer(opts.maintainerEmail); + if (!opts.maintainerEmail) { + try { + opts.maintainerEmail = pkg.author.email || pkg.author.match(looseEmailRe)[2]; + } catch (e) {} + } + if (!opts.maintainerEmail) { + throw new Error("missing or malformed `package.author`, which is used as the contact for support notices"); + } + opts.package = undefined; + opts.maintainerEmail = parseMaintainer(opts.maintainerEmail); - return opts; + return opts; } function parseMaintainer(maintainerEmail) { - try { - maintainerEmail = maintainerEmail.match(looseEmailRe)[2]; - } catch (e) { - maintainerEmail = null; - } - if (!maintainerEmail) { - throw new Error("missing or malformed `maintainerEmail`, which is used as the contact for support notices"); - } - return maintainerEmail; + try { + maintainerEmail = maintainerEmail.match(looseEmailRe)[2]; + } catch (e) { + maintainerEmail = null; + } + if (!maintainerEmail) { + throw new Error("missing or malformed `maintainerEmail`, which is used as the contact for support notices"); + } + return maintainerEmail; } diff --git a/http-middleware.js b/http-middleware.js index 2608f67..149081b 100644 --- a/http-middleware.js +++ b/http-middleware.js @@ -5,102 +5,102 @@ var servernameRe = /^[a-z0-9\.\-]+$/i; var challengePrefix = "/.well-known/acme-challenge/"; HttpMiddleware.create = function(gl, defaultApp) { - if (defaultApp && "function" !== typeof defaultApp) { - throw new Error("use greenlock.httpMiddleware() or greenlock.httpMiddleware(function (req, res) {})"); - } + if (defaultApp && "function" !== typeof defaultApp) { + throw new Error("use greenlock.httpMiddleware() or greenlock.httpMiddleware(function (req, res) {})"); + } - return function(req, res, next) { - var hostname = HttpMiddleware.sanitizeHostname(req); + return function(req, res, next) { + var hostname = HttpMiddleware.sanitizeHostname(req); - req.on("error", function(err) { - explainError(gl, err, "http_01_middleware_socket", hostname); - }); + req.on("error", function(err) { + explainError(gl, err, "http_01_middleware_socket", hostname); + }); - if (skipIfNeedBe(req, res, next, defaultApp, hostname)) { - return; - } + if (skipIfNeedBe(req, res, next, defaultApp, hostname)) { + return; + } - var token = req.url.slice(challengePrefix.length); + var token = req.url.slice(challengePrefix.length); - gl.getAcmeHttp01ChallengeResponse({ type: "http-01", servername: hostname, token: token }) - .catch(function(err) { - respondToError(gl, res, err, "http_01_middleware_challenge_response", hostname); - return { __done: true }; - }) - .then(function(result) { - if (result && result.__done) { - return; - } - return respondWithGrace(res, result, hostname, token); - }); - }; + gl.getAcmeHttp01ChallengeResponse({ type: "http-01", servername: hostname, token: token }) + .catch(function(err) { + respondToError(gl, res, err, "http_01_middleware_challenge_response", hostname); + return { __done: true }; + }) + .then(function(result) { + if (result && result.__done) { + return; + } + return respondWithGrace(res, result, hostname, token); + }); + }; }; function skipIfNeedBe(req, res, next, defaultApp, hostname) { - if (!hostname || 0 !== req.url.indexOf(challengePrefix)) { - if ("function" === typeof defaultApp) { - defaultApp(req, res, next); - } else if ("function" === typeof next) { - next(); - } else { - res.statusCode = 500; - res.end("[500] Developer Error: app.use('/', greenlock.httpMiddleware()) or greenlock.httpMiddleware(app)"); - } - } + if (!hostname || 0 !== req.url.indexOf(challengePrefix)) { + if ("function" === typeof defaultApp) { + defaultApp(req, res, next); + } else if ("function" === typeof next) { + next(); + } else { + res.statusCode = 500; + res.end("[500] Developer Error: app.use('/', greenlock.httpMiddleware()) or greenlock.httpMiddleware(app)"); + } + } } function respondWithGrace(res, result, hostname, token) { - var keyAuth = result && result.keyAuthorization; - if (keyAuth && "string" === typeof keyAuth) { - res.setHeader("Content-Type", "text/plain; charset=utf-8"); - res.end(keyAuth); - return; - } + var keyAuth = result && result.keyAuthorization; + if (keyAuth && "string" === typeof keyAuth) { + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.end(keyAuth); + return; + } - res.statusCode = 404; - res.setHeader("Content-Type", "application/json; charset=utf-8"); - res.end(JSON.stringify({ error: { message: "domain '" + hostname + "' has no token '" + token + "'." } })); + res.statusCode = 404; + res.setHeader("Content-Type", "application/json; charset=utf-8"); + res.end(JSON.stringify({ error: { message: "domain '" + hostname + "' has no token '" + token + "'." } })); } function explainError(gl, err, ctx, hostname) { - if (!err.servername) { - err.servername = hostname; - } - if (!err.context) { - err.context = ctx; - } - (gl.notify || gl._notify)("error", err); - return err; + if (!err.servername) { + err.servername = hostname; + } + if (!err.context) { + err.context = ctx; + } + (gl.notify || gl._notify)("error", err); + return err; } function respondToError(gl, res, err, ctx, hostname) { - err = explainError(gl, err, ctx, hostname); - res.statusCode = 500; - res.end("Internal Server Error: See logs for details."); + err = explainError(gl, err, ctx, hostname); + res.statusCode = 500; + res.end("Internal Server Error: See logs for details."); } HttpMiddleware.getHostname = function(req) { - return req.hostname || req.headers["x-forwarded-host"] || (req.headers.host || ""); + return req.hostname || req.headers["x-forwarded-host"] || (req.headers.host || ""); }; HttpMiddleware.sanitizeHostname = function(req) { - // we can trust XFH because spoofing causes no ham in this limited use-case scenario - // (and only telebit would be legitimately setting XFH) - var servername = HttpMiddleware.getHostname(req) - .toLowerCase() - .replace(/:.*/, ""); - try { - req.hostname = servername; - } catch (e) { - // read-only express property - } - if (req.headers["x-forwarded-host"]) { - req.headers["x-forwarded-host"] = servername; - } - try { - req.headers.host = servername; - } catch (e) { - // TODO is this a possible error? - } + // we can trust XFH because spoofing causes no ham in this limited use-case scenario + // (and only telebit would be legitimately setting XFH) + var servername = HttpMiddleware.getHostname(req) + .toLowerCase() + .replace(/:.*/, ""); + try { + req.hostname = servername; + } catch (e) { + // read-only express property + } + if (req.headers["x-forwarded-host"]) { + req.headers["x-forwarded-host"] = servername; + } + try { + req.headers.host = servername; + } catch (e) { + // TODO is this a possible error? + } - return (servernameRe.test(servername) && -1 === servername.indexOf("..") && servername) || ""; + return (servernameRe.test(servername) && -1 === servername.indexOf("..") && servername) || ""; }; diff --git a/https-middleware.js b/https-middleware.js index 80be005..4cd404b 100644 --- a/https-middleware.js +++ b/https-middleware.js @@ -4,56 +4,56 @@ var SanitizeHost = module.exports; var HttpMiddleware = require("./http-middleware.js"); SanitizeHost.create = function(gl, app) { - return function(req, res, next) { - function realNext() { - if ("function" === typeof app) { - app(req, res); - } else if ("function" === typeof next) { - next(); - } else { - res.statusCode = 500; - res.end("Error: no middleware assigned"); - } - } + return function(req, res, next) { + function realNext() { + if ("function" === typeof app) { + app(req, res); + } else if ("function" === typeof next) { + next(); + } else { + res.statusCode = 500; + res.end("Error: no middleware assigned"); + } + } - var hostname = HttpMiddleware.getHostname(req); - // Replace the hostname, and get the safe version - var safehost = HttpMiddleware.sanitizeHostname(req); + var hostname = HttpMiddleware.getHostname(req); + // Replace the hostname, and get the safe version + var safehost = HttpMiddleware.sanitizeHostname(req); - // if no hostname, move along - if (!hostname) { - realNext(); - return; - } + // if no hostname, move along + if (!hostname) { + realNext(); + return; + } - // if there were unallowed characters, complain - if (safehost.length !== hostname.length) { - res.statusCode = 400; - res.end("Malformed HTTP Header: 'Host: " + hostname + "'"); - return; - } + // if there were unallowed characters, complain + if (safehost.length !== hostname.length) { + res.statusCode = 400; + res.end("Malformed HTTP Header: 'Host: " + hostname + "'"); + return; + } - // Note: This sanitize function is also called on plain sockets, which don't need Domain Fronting checks - if (req.socket.encrypted) { - if (req.socket && "string" === typeof req.socket.servername) { - // Workaround for https://github.com/nodejs/node/issues/22389 - if (!SanitizeHost._checkServername(safehost, req.socket)) { - res.statusCode = 400; - res.setHeader("Content-Type", "text/html; charset=utf-8"); - res.end( - "

Domain Fronting Error

" + - "

This connection was secured using TLS/SSL for '" + - (req.socket.servername || "").toLowerCase() + - "'

" + - "

The HTTP request specified 'Host: " + - safehost + - "', which is (obviously) different.

" + - "

Because this looks like a domain fronting attack, the connection has been terminated.

" - ); - return; - } - } - /* + // Note: This sanitize function is also called on plain sockets, which don't need Domain Fronting checks + if (req.socket.encrypted) { + if (req.socket && "string" === typeof req.socket.servername) { + // Workaround for https://github.com/nodejs/node/issues/22389 + if (!SanitizeHost._checkServername(safehost, req.socket)) { + res.statusCode = 400; + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end( + "

Domain Fronting Error

" + + "

This connection was secured using TLS/SSL for '" + + (req.socket.servername || "").toLowerCase() + + "'

" + + "

The HTTP request specified 'Host: " + + safehost + + "', which is (obviously) different.

" + + "

Because this looks like a domain fronting attack, the connection has been terminated.

" + ); + return; + } + } + /* else if (safehost && !gl._skip_fronting_check) { // We used to print a log message here, but it turns out that it's @@ -66,74 +66,74 @@ SanitizeHost.create = function(gl, app) { //gl._skip_fronting_check = true; } */ - } + } - // carry on - realNext(); - }; + // carry on + realNext(); + }; }; var warnDomainFronting = true; var warnUnexpectedError = true; SanitizeHost._checkServername = function(safeHost, tlsSocket) { - var servername = (tlsSocket.servername || "").toLowerCase(); + var servername = (tlsSocket.servername || "").toLowerCase(); - // acceptable: older IoT devices may lack SNI support - if (!servername) { - return true; - } - // acceptable: odd... but acceptable - if (!safeHost) { - return true; - } - if (safeHost === servername) { - return true; - } + // acceptable: older IoT devices may lack SNI support + if (!servername) { + return true; + } + // acceptable: odd... but acceptable + if (!safeHost) { + return true; + } + if (safeHost === servername) { + return true; + } - if ("function" !== typeof tlsSocket.getCertificate) { - // domain fronting attacks allowed - if (warnDomainFronting) { - // https://github.com/nodejs/node/issues/24095 - console.warn( - "Warning: node " + - process.version + - " is vulnerable to domain fronting attacks. Please use node v11.2.0 or greater." - ); - warnDomainFronting = false; - } - return true; - } + if ("function" !== typeof tlsSocket.getCertificate) { + // domain fronting attacks allowed + if (warnDomainFronting) { + // https://github.com/nodejs/node/issues/24095 + console.warn( + "Warning: node " + + process.version + + " is vulnerable to domain fronting attacks. Please use node v11.2.0 or greater." + ); + warnDomainFronting = false; + } + return true; + } - // connection established with servername and session is re-used for allowed name - // See https://github.com/nodejs/node/issues/24095 - var cert = tlsSocket.getCertificate(); - try { - // TODO optimize / cache? - // *should* always have a string, right? - // *should* always be lowercase already, right? - //console.log(safeHost, cert.subject.CN, cert.subjectaltname); - var isSubject = (cert.subject.CN || "").toLowerCase() === safeHost; - if (isSubject) { - return true; - } + // connection established with servername and session is re-used for allowed name + // See https://github.com/nodejs/node/issues/24095 + var cert = tlsSocket.getCertificate(); + try { + // TODO optimize / cache? + // *should* always have a string, right? + // *should* always be lowercase already, right? + //console.log(safeHost, cert.subject.CN, cert.subjectaltname); + var isSubject = (cert.subject.CN || "").toLowerCase() === safeHost; + if (isSubject) { + return true; + } - var dnsnames = (cert.subjectaltname || "").split(/,\s+/); - var inSanList = dnsnames.some(function(name) { - // always prefixed with "DNS:" - return safeHost === name.slice(4).toLowerCase(); - }); + var dnsnames = (cert.subjectaltname || "").split(/,\s+/); + var inSanList = dnsnames.some(function(name) { + // always prefixed with "DNS:" + return safeHost === name.slice(4).toLowerCase(); + }); - if (inSanList) { - return true; - } - } catch (e) { - // not sure what else to do in this situation... - if (warnUnexpectedError) { - console.warn("Warning: encoutered error while performing domain fronting check: " + e.message); - warnUnexpectedError = false; - } - return true; - } + if (inSanList) { + return true; + } + } catch (e) { + // not sure what else to do in this situation... + if (warnUnexpectedError) { + console.warn("Warning: encoutered error while performing domain fronting check: " + e.message); + warnUnexpectedError = false; + } + return true; + } - return false; + return false; }; diff --git a/lib/compat.js b/lib/compat.js index 3e5e7bc..59f95dc 100644 --- a/lib/compat.js +++ b/lib/compat.js @@ -1,37 +1,37 @@ "use strict"; function requireBluebird() { - try { - return require("bluebird"); - } catch (e) { - console.error(""); - console.error("DON'T PANIC. You're running an old version of node with incomplete Promise support."); - console.error("EASY FIX: `npm install --save bluebird`"); - console.error(""); - throw e; - } + try { + return require("bluebird"); + } catch (e) { + console.error(""); + console.error("DON'T PANIC. You're running an old version of node with incomplete Promise support."); + console.error("EASY FIX: `npm install --save bluebird`"); + console.error(""); + throw e; + } } if ("undefined" === typeof Promise) { - global.Promise = requireBluebird(); + global.Promise = requireBluebird(); } if ("function" !== typeof require("util").promisify) { - require("util").promisify = requireBluebird().promisify; + require("util").promisify = requireBluebird().promisify; } if (!console.debug) { - console.debug = console.log; + console.debug = console.log; } var fs = require("fs"); var fsAsync = {}; Object.keys(fs).forEach(function(key) { - var fn = fs[key]; - if ("function" !== typeof fn || !/[a-z]/.test(key[0])) { - return; - } - fsAsync[key] = require("util").promisify(fn); + var fn = fs[key]; + if ("function" !== typeof fn || !/[a-z]/.test(key[0])) { + return; + } + fsAsync[key] = require("util").promisify(fn); }); exports.fsAsync = fsAsync; diff --git a/main.js b/main.js index 1e3c309..1a15447 100644 --- a/main.js +++ b/main.js @@ -13,20 +13,20 @@ _hasSetSecureContext = !!require("https").createServer({}, function() {}).setSec // TODO document in issues if (!_hasSetSecureContext) { - // TODO this isn't necessary if greenlock options are set with options.cert - console.warn("Warning: node " + process.version + " is missing tlsSocket.setSecureContext()."); - console.warn(" The default certificate may not be set."); - shouldUpgrade = true; + // TODO this isn't necessary if greenlock options are set with options.cert + console.warn("Warning: node " + process.version + " is missing tlsSocket.setSecureContext()."); + console.warn(" The default certificate may not be set."); + shouldUpgrade = true; } if (major < 11 || (11 === major && minor < 2)) { - // https://github.com/nodejs/node/issues/24095 - console.warn("Warning: node " + process.version + " is missing tlsSocket.getCertificate()."); - console.warn(" This is necessary to guard against domain fronting attacks."); - shouldUpgrade = true; + // https://github.com/nodejs/node/issues/24095 + console.warn("Warning: node " + process.version + " is missing tlsSocket.getCertificate()."); + console.warn(" This is necessary to guard against domain fronting attacks."); + shouldUpgrade = true; } if (shouldUpgrade) { - console.warn("Warning: Please upgrade to node v11.2.0 or greater."); - console.warn(); + console.warn("Warning: Please upgrade to node v11.2.0 or greater."); + console.warn(); } diff --git a/master.js b/master.js index 261f124..871a9a0 100644 --- a/master.js +++ b/master.js @@ -9,152 +9,152 @@ var os = require("os"); var msgPrefix = "greenlock:"; Master.create = function(opts) { - var resolveCb; - var _readyCb; - var _kicked = false; + var resolveCb; + var _readyCb; + var _kicked = false; - var greenlock = require("./greenlock.js").create(opts); + var greenlock = require("./greenlock.js").create(opts); - var ready = new Promise(function(resolve) { - resolveCb = resolve; - }).then(function(fn) { - _readyCb = fn; - return fn; - }); + var ready = new Promise(function(resolve) { + resolveCb = resolve; + }).then(function(fn) { + _readyCb = fn; + return fn; + }); - function kickoff() { - if (_kicked) { - return; - } - _kicked = true; + function kickoff() { + if (_kicked) { + return; + } + _kicked = true; - Master._spawnWorkers(opts, greenlock); + Master._spawnWorkers(opts, greenlock); - ready.then(function(fn) { - // not sure what this API should be yet - fn(); - }); - } + ready.then(function(fn) { + // not sure what this API should be yet + fn(); + }); + } - var master = { - serve: function() { - kickoff(); - return master; - }, - master: function(fn) { - if (_readyCb) { - throw new Error("can't call master twice"); - } - kickoff(); - resolveCb(fn); - return master; - } - }; - return master; + var master = { + serve: function() { + kickoff(); + return master; + }, + master: function(fn) { + if (_readyCb) { + throw new Error("can't call master twice"); + } + kickoff(); + resolveCb(fn); + return master; + } + }; + return master; }; function range(n) { - n = parseInt(n, 10); - if (!n) { - return []; - } - return new Array(n).join(",").split(","); + n = parseInt(n, 10); + if (!n) { + return []; + } + return new Array(n).join(",").split(","); } Master._spawnWorkers = function(opts, greenlock) { - var numCpus = parseInt(process.env.NUMBER_OF_PROCESSORS, 10) || os.cpus().length; + var numCpus = parseInt(process.env.NUMBER_OF_PROCESSORS, 10) || os.cpus().length; - // process rpc messages - // start when dead - var numWorkers = parseInt(opts.workers || opts.numWorkers, 10); - if (!numWorkers) { - if (numCpus <= 2) { - numWorkers = 2; - } else { - numWorkers = numCpus - 1; - } - } + // process rpc messages + // start when dead + var numWorkers = parseInt(opts.workers || opts.numWorkers, 10); + if (!numWorkers) { + if (numCpus <= 2) { + numWorkers = 2; + } else { + numWorkers = numCpus - 1; + } + } - cluster.once("exit", function() { - setTimeout(function() { - process.exit(3); - }, 100); - }); + cluster.once("exit", function() { + setTimeout(function() { + process.exit(3); + }, 100); + }); - var workers = range(numWorkers); - function next() { - if (!workers.length) { - return; - } - workers.pop(); + var workers = range(numWorkers); + function next() { + if (!workers.length) { + return; + } + workers.pop(); - // for a nice aesthetic - setTimeout(function() { - Master._spawnWorker(opts, greenlock); - next(); - }, 250); - } + // for a nice aesthetic + setTimeout(function() { + Master._spawnWorker(opts, greenlock); + next(); + }, 250); + } - next(); + next(); }; Master._spawnWorker = function(opts, greenlock) { - var w = cluster.fork(); - // automatically added to master's `cluster.workers` - w.once("exit", function(code, signal) { - // TODO handle failures - // Should test if the first starts successfully - // Should exit if failures happen too quickly + var w = cluster.fork(); + // automatically added to master's `cluster.workers` + w.once("exit", function(code, signal) { + // TODO handle failures + // Should test if the first starts successfully + // Should exit if failures happen too quickly - // For now just kill all when any die - if (signal) { - console.error("worker was killed by signal:", signal); - } else if (code !== 0) { - console.error("worker exited with error code:", code); - } else { - console.error("worker unexpectedly quit without exit code or signal"); - } - process.exit(2); + // For now just kill all when any die + if (signal) { + console.error("worker was killed by signal:", signal); + } else if (code !== 0) { + console.error("worker exited with error code:", code); + } else { + console.error("worker unexpectedly quit without exit code or signal"); + } + process.exit(2); - //addWorker(); - }); + //addWorker(); + }); - function handleMessage(msg) { - if (0 !== (msg._id || "").indexOf(msgPrefix)) { - return; - } - if ("string" !== typeof msg._funcname) { - // TODO developer error - return; - } + function handleMessage(msg) { + if (0 !== (msg._id || "").indexOf(msgPrefix)) { + return; + } + if ("string" !== typeof msg._funcname) { + // TODO developer error + return; + } - function rpc() { - return greenlock[msg._funcname](msg._input) - .then(function(result) { - w.send({ - _id: msg._id, - _result: result - }); - }) - .catch(function(e) { - var error = new Error(e.message); - Object.getOwnPropertyNames(e).forEach(function(k) { - error[k] = e[k]; - }); - w.send({ - _id: msg._id, - _error: error - }); - }); - } + function rpc() { + return greenlock[msg._funcname](msg._input) + .then(function(result) { + w.send({ + _id: msg._id, + _result: result + }); + }) + .catch(function(e) { + var error = new Error(e.message); + Object.getOwnPropertyNames(e).forEach(function(k) { + error[k] = e[k]; + }); + w.send({ + _id: msg._id, + _error: error + }); + }); + } - try { - rpc(); - } catch (e) { - console.error("Unexpected and uncaught greenlock." + msg._funcname + " error:"); - console.error(e); - } - } + try { + rpc(); + } catch (e) { + console.error("Unexpected and uncaught greenlock." + msg._funcname + " error:"); + console.error(e); + } + } - w.on("message", handleMessage); + w.on("message", handleMessage); }; diff --git a/package-lock.json b/package-lock.json index 5bc06a1..9c5b852 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,140 +1,140 @@ { - "name": "@root/greenlock-express", - "version": "3.0.11", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@root/acme": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@root/acme/-/acme-3.0.8.tgz", - "integrity": "sha512-VmBvLvWdCDkolkanI9Dzm1ouSWPaAa2eCCwcDZcVQbWoNiUIOqbbd57fcMA/gZxLyuJPStD2WXFuEuSMPDxcww==", - "requires": { - "@root/encoding": "^1.0.1", - "@root/keypairs": "^0.9.0", - "@root/pem": "^1.0.4", - "@root/request": "^1.3.11", - "@root/x509": "^0.7.2" - } - }, - "@root/asn1": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@root/asn1/-/asn1-1.0.0.tgz", - "integrity": "sha512-0lfZNuOULKJDJmdIkP8V9RnbV3XaK6PAHD3swnFy4tZwtlMDzLKoM/dfNad7ut8Hu3r91wy9uK0WA/9zym5mig==", - "requires": { - "@root/encoding": "^1.0.1" - } - }, - "@root/csr": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@root/csr/-/csr-0.8.1.tgz", - "integrity": "sha512-hKl0VuE549TK6SnS2Yn9nRvKbFZXn/oAg+dZJU/tlKl/f/0yRXeuUzf8akg3JjtJq+9E592zDqeXZ7yyrg8fSQ==", - "requires": { - "@root/asn1": "^1.0.0", - "@root/pem": "^1.0.4", - "@root/x509": "^0.7.2" - } - }, - "@root/encoding": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@root/encoding/-/encoding-1.0.1.tgz", - "integrity": "sha512-OaEub02ufoU038gy6bsNHQOjIn8nUjGiLcaRmJ40IUykneJkIW5fxDqKxQx48cszuNflYldsJLPPXCrGfHs8yQ==" - }, - "@root/greenlock": { - "version": "3.0.25", - "resolved": "https://registry.npmjs.org/@root/greenlock/-/greenlock-3.0.25.tgz", - "integrity": "sha512-VC8H9MTkbqxlB2LGntmcq5cstkE0TdZLvxm25SO5i7c6abJBVMQafhTD415OXwoGimnmWTn6SZ93Fj73d9QX/w==", - "requires": { - "@root/acme": "^3.0.8", - "@root/csr": "^0.8.1", - "@root/keypairs": "^0.9.0", - "@root/mkdirp": "^1.0.0", - "@root/request": "^1.3.10", - "acme-http-01-standalone": "^3.0.5", - "cert-info": "^1.5.1", - "greenlock-manager-fs": "^3.0.1", - "greenlock-store-fs": "^3.2.0", - "safe-replace": "^1.1.0" - } - }, - "@root/keypairs": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@root/keypairs/-/keypairs-0.9.0.tgz", - "integrity": "sha512-NXE2L9Gv7r3iC4kB/gTPZE1vO9Ox/p14zDzAJ5cGpTpytbWOlWF7QoHSJbtVX4H7mRG/Hp7HR3jWdWdb2xaaXg==", - "requires": { - "@root/encoding": "^1.0.1", - "@root/pem": "^1.0.4", - "@root/x509": "^0.7.2" - } - }, - "@root/mkdirp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@root/mkdirp/-/mkdirp-1.0.0.tgz", - "integrity": "sha512-hxGAYUx5029VggfG+U9naAhQkoMSXtOeXtbql97m3Hi6/sQSRL/4khKZPyOF6w11glyCOU38WCNLu9nUcSjOfA==" - }, - "@root/pem": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@root/pem/-/pem-1.0.4.tgz", - "integrity": "sha512-rEUDiUsHtild8GfIjFE9wXtcVxeS+ehCJQBwbQQ3IVfORKHK93CFnRtkr69R75lZFjcmKYVc+AXDB+AeRFOULA==" - }, - "@root/request": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@root/request/-/request-1.4.2.tgz", - "integrity": "sha512-J8FM4+SJuc7WRC+Jz17m+VT2lgI7HtatHhxN1F2ck5aIKUAxJEaR4u/gLBsgT60mVHevKCjKN0O8115UtJjwLw==" - }, - "@root/x509": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@root/x509/-/x509-0.7.2.tgz", - "integrity": "sha512-ENq3LGYORK5NiMFHEVeNMt+fTXaC7DTS6sQXoqV+dFdfT0vmiL5cDLjaXQhaklJQq0NiwicZegzJRl1ZOTp3WQ==", - "requires": { - "@root/asn1": "^1.0.0", - "@root/encoding": "^1.0.1" - } - }, - "acme-http-01-standalone": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/acme-http-01-standalone/-/acme-http-01-standalone-3.0.5.tgz", - "integrity": "sha512-W4GfK+39GZ+u0mvxRVUcVFCG6gposfzEnSBF20T/NUwWAKG59wQT1dUbS1NixRIAsRuhpGc4Jx659cErFQH0Pg==" - }, - "cert-info": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/cert-info/-/cert-info-1.5.1.tgz", - "integrity": "sha512-eoQC/yAgW3gKTKxjzyClvi+UzuY97YCjcl+lSqbsGIy7HeGaWxCPOQFivhUYm27hgsBMhsJJFya3kGvK6PMIcQ==" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "greenlock-manager-fs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/greenlock-manager-fs/-/greenlock-manager-fs-3.0.1.tgz", - "integrity": "sha512-vZfGFq1TTKxaAqdGDUwNservrNzXx0xCwT/ovG/N378GrhS+U5S8B8LUlNtQU7Fdw6RToMiBcm22OOxSrvZ2zw==", - "requires": { - "@root/mkdirp": "^1.0.0", - "safe-replace": "^1.1.0" - } - }, - "greenlock-store-fs": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/greenlock-store-fs/-/greenlock-store-fs-3.2.0.tgz", - "integrity": "sha512-zqcPnF+173oYq5qU7FoGtuqeG8dmmvAiSnz98kEHAHyvgRF9pE1T0MM0AuqDdj45I3kXlCj2gZBwutnRi37J3g==", - "requires": { - "@root/mkdirp": "^1.0.0", - "safe-replace": "^1.1.0" - } - }, - "redirect-https": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/redirect-https/-/redirect-https-1.3.0.tgz", - "integrity": "sha512-9GzwI/+Cqw3jlSg0CW6TgBQbhiVhkHSDvW8wjgRQ9IK34wtxS71YJiQeazSCSEqbvowHCJuQZgmQFl1xUHKEgg==", - "requires": { - "escape-html": "^1.0.3" - } - }, - "safe-replace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-replace/-/safe-replace-1.1.0.tgz", - "integrity": "sha512-9/V2E0CDsKs9DWOOwJH7jYpSl9S3N05uyevNjvsnDauBqRowBPOyot1fIvV5N2IuZAbYyvrTXrYFVG0RZInfFw==" - } - } + "name": "@root/greenlock-express", + "version": "3.0.13", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@root/acme": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@root/acme/-/acme-3.0.8.tgz", + "integrity": "sha512-VmBvLvWdCDkolkanI9Dzm1ouSWPaAa2eCCwcDZcVQbWoNiUIOqbbd57fcMA/gZxLyuJPStD2WXFuEuSMPDxcww==", + "requires": { + "@root/encoding": "^1.0.1", + "@root/keypairs": "^0.9.0", + "@root/pem": "^1.0.4", + "@root/request": "^1.3.11", + "@root/x509": "^0.7.2" + } + }, + "@root/asn1": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@root/asn1/-/asn1-1.0.0.tgz", + "integrity": "sha512-0lfZNuOULKJDJmdIkP8V9RnbV3XaK6PAHD3swnFy4tZwtlMDzLKoM/dfNad7ut8Hu3r91wy9uK0WA/9zym5mig==", + "requires": { + "@root/encoding": "^1.0.1" + } + }, + "@root/csr": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@root/csr/-/csr-0.8.1.tgz", + "integrity": "sha512-hKl0VuE549TK6SnS2Yn9nRvKbFZXn/oAg+dZJU/tlKl/f/0yRXeuUzf8akg3JjtJq+9E592zDqeXZ7yyrg8fSQ==", + "requires": { + "@root/asn1": "^1.0.0", + "@root/pem": "^1.0.4", + "@root/x509": "^0.7.2" + } + }, + "@root/encoding": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@root/encoding/-/encoding-1.0.1.tgz", + "integrity": "sha512-OaEub02ufoU038gy6bsNHQOjIn8nUjGiLcaRmJ40IUykneJkIW5fxDqKxQx48cszuNflYldsJLPPXCrGfHs8yQ==" + }, + "@root/greenlock": { + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/@root/greenlock/-/greenlock-3.0.25.tgz", + "integrity": "sha512-VC8H9MTkbqxlB2LGntmcq5cstkE0TdZLvxm25SO5i7c6abJBVMQafhTD415OXwoGimnmWTn6SZ93Fj73d9QX/w==", + "requires": { + "@root/acme": "^3.0.8", + "@root/csr": "^0.8.1", + "@root/keypairs": "^0.9.0", + "@root/mkdirp": "^1.0.0", + "@root/request": "^1.3.10", + "acme-http-01-standalone": "^3.0.5", + "cert-info": "^1.5.1", + "greenlock-manager-fs": "^3.0.1", + "greenlock-store-fs": "^3.2.0", + "safe-replace": "^1.1.0" + } + }, + "@root/keypairs": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@root/keypairs/-/keypairs-0.9.0.tgz", + "integrity": "sha512-NXE2L9Gv7r3iC4kB/gTPZE1vO9Ox/p14zDzAJ5cGpTpytbWOlWF7QoHSJbtVX4H7mRG/Hp7HR3jWdWdb2xaaXg==", + "requires": { + "@root/encoding": "^1.0.1", + "@root/pem": "^1.0.4", + "@root/x509": "^0.7.2" + } + }, + "@root/mkdirp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@root/mkdirp/-/mkdirp-1.0.0.tgz", + "integrity": "sha512-hxGAYUx5029VggfG+U9naAhQkoMSXtOeXtbql97m3Hi6/sQSRL/4khKZPyOF6w11glyCOU38WCNLu9nUcSjOfA==" + }, + "@root/pem": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@root/pem/-/pem-1.0.4.tgz", + "integrity": "sha512-rEUDiUsHtild8GfIjFE9wXtcVxeS+ehCJQBwbQQ3IVfORKHK93CFnRtkr69R75lZFjcmKYVc+AXDB+AeRFOULA==" + }, + "@root/request": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@root/request/-/request-1.4.2.tgz", + "integrity": "sha512-J8FM4+SJuc7WRC+Jz17m+VT2lgI7HtatHhxN1F2ck5aIKUAxJEaR4u/gLBsgT60mVHevKCjKN0O8115UtJjwLw==" + }, + "@root/x509": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@root/x509/-/x509-0.7.2.tgz", + "integrity": "sha512-ENq3LGYORK5NiMFHEVeNMt+fTXaC7DTS6sQXoqV+dFdfT0vmiL5cDLjaXQhaklJQq0NiwicZegzJRl1ZOTp3WQ==", + "requires": { + "@root/asn1": "^1.0.0", + "@root/encoding": "^1.0.1" + } + }, + "acme-http-01-standalone": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/acme-http-01-standalone/-/acme-http-01-standalone-3.0.5.tgz", + "integrity": "sha512-W4GfK+39GZ+u0mvxRVUcVFCG6gposfzEnSBF20T/NUwWAKG59wQT1dUbS1NixRIAsRuhpGc4Jx659cErFQH0Pg==" + }, + "cert-info": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/cert-info/-/cert-info-1.5.1.tgz", + "integrity": "sha512-eoQC/yAgW3gKTKxjzyClvi+UzuY97YCjcl+lSqbsGIy7HeGaWxCPOQFivhUYm27hgsBMhsJJFya3kGvK6PMIcQ==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "greenlock-manager-fs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/greenlock-manager-fs/-/greenlock-manager-fs-3.0.1.tgz", + "integrity": "sha512-vZfGFq1TTKxaAqdGDUwNservrNzXx0xCwT/ovG/N378GrhS+U5S8B8LUlNtQU7Fdw6RToMiBcm22OOxSrvZ2zw==", + "requires": { + "@root/mkdirp": "^1.0.0", + "safe-replace": "^1.1.0" + } + }, + "greenlock-store-fs": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/greenlock-store-fs/-/greenlock-store-fs-3.2.0.tgz", + "integrity": "sha512-zqcPnF+173oYq5qU7FoGtuqeG8dmmvAiSnz98kEHAHyvgRF9pE1T0MM0AuqDdj45I3kXlCj2gZBwutnRi37J3g==", + "requires": { + "@root/mkdirp": "^1.0.0", + "safe-replace": "^1.1.0" + } + }, + "redirect-https": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/redirect-https/-/redirect-https-1.3.0.tgz", + "integrity": "sha512-9GzwI/+Cqw3jlSg0CW6TgBQbhiVhkHSDvW8wjgRQ9IK34wtxS71YJiQeazSCSEqbvowHCJuQZgmQFl1xUHKEgg==", + "requires": { + "escape-html": "^1.0.3" + } + }, + "safe-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-replace/-/safe-replace-1.1.0.tgz", + "integrity": "sha512-9/V2E0CDsKs9DWOOwJH7jYpSl9S3N05uyevNjvsnDauBqRowBPOyot1fIvV5N2IuZAbYyvrTXrYFVG0RZInfFw==" + } + } } diff --git a/package.json b/package.json index ecf89ca..5b00a33 100644 --- a/package.json +++ b/package.json @@ -1,51 +1,51 @@ { - "name": "@root/greenlock-express", - "version": "3.0.12", - "description": "Free SSL and managed or automatic HTTPS for node.js with Express, Koa, Connect, Hapi, and all other middleware systems.", - "main": "greenlock-express.js", - "homepage": "https://greenlock.domains", - "files": [ - "*.js", - "lib", - "scripts" - ], - "scripts": { - "start": "node_todo server.js ./config.js", - "test": "node_todo test/greenlock.js" - }, - "directories": { - "example": "examples" - }, - "dependencies": { - "@root/greenlock": "^3.0.25", - "redirect-https": "^1.1.5" - }, - "trulyOptionalDependencies": { - "http-proxy": "^1.17.0", - "express": "^4.16.3", - "express-basic-auth": "^1.2.0", - "finalhandler": "^1.1.1", - "serve-index": "^1.9.1", - "serve-static": "^1.13.2", - "ws": "^5.2.1" - }, - "devDependencies": {}, - "repository": { - "type": "git", - "url": "https://git.rootprojects.org/root/greenlock-express.js.git" - }, - "keywords": [ - "Let's Encrypt", - "ACME", - "greenlock", - "Free SSL", - "Automated HTTPS", - "https", - "tls" - ], - "author": "AJ ONeal (https://solderjs.com/)", - "license": "MPL-2.0", - "bugs": { - "url": "https://git.rootprojects.org/root/greenlock-express.js/issues" - } + "name": "@root/greenlock-express", + "version": "3.0.13", + "description": "Free SSL and managed or automatic HTTPS for node.js with Express, Koa, Connect, Hapi, and all other middleware systems.", + "main": "greenlock-express.js", + "homepage": "https://greenlock.domains", + "files": [ + "*.js", + "lib", + "scripts" + ], + "scripts": { + "start": "node_todo server.js ./config.js", + "test": "node_todo test/greenlock.js" + }, + "directories": { + "example": "examples" + }, + "dependencies": { + "@root/greenlock": "^3.0.25", + "redirect-https": "^1.1.5" + }, + "trulyOptionalDependencies": { + "http-proxy": "^1.17.0", + "express": "^4.16.3", + "express-basic-auth": "^1.2.0", + "finalhandler": "^1.1.1", + "serve-index": "^1.9.1", + "serve-static": "^1.13.2", + "ws": "^5.2.1" + }, + "devDependencies": {}, + "repository": { + "type": "git", + "url": "https://git.rootprojects.org/root/greenlock-express.js.git" + }, + "keywords": [ + "Let's Encrypt", + "ACME", + "greenlock", + "Free SSL", + "Automated HTTPS", + "https", + "tls" + ], + "author": "AJ ONeal (https://solderjs.com/)", + "license": "MPL-2.0", + "bugs": { + "url": "https://git.rootprojects.org/root/greenlock-express.js/issues" + } } diff --git a/servers.js b/servers.js index fc480e2..e8a06e8 100644 --- a/servers.js +++ b/servers.js @@ -9,148 +9,152 @@ var sni = require("./sni.js"); var cluster = require("cluster"); Servers.create = function(greenlock) { - var servers = {}; - var _httpServer; - var _httpsServer; + var servers = {}; + var _httpServer; + var _httpsServer; - function startError(e) { - explainError(e); - process.exit(1); - } + function startError(e) { + explainError(e); + process.exit(1); + } - servers.httpServer = function(defaultApp) { - if (_httpServer) { - return _httpServer; - } + servers.httpServer = function(defaultApp) { + if (_httpServer) { + return _httpServer; + } - _httpServer = http.createServer(HttpMiddleware.create(greenlock, defaultApp)); - _httpServer.once("error", startError); + _httpServer = http.createServer(HttpMiddleware.create(greenlock, defaultApp)); + _httpServer.once("error", startError); - return _httpServer; - }; + return _httpServer; + }; - var _middlewareApp; + var _middlewareApp; - servers.http2Server = function(secureOpts, defaultApp) { - return servers._httpsServer(secureOpts, defaultApp, function(secureOpts, fn) { - secureOpts.allowHTTP1 = true; - return require("http2").createSecureServer(secureOpts, fn); - }); - }; - servers.httpsServer = function(secureOpts, defaultApp) { - return servers._httpsServer(secureOpts, defaultApp, function(secureOpts, fn) { - return require("https").createServer(secureOpts, fn); - }); - }; - servers._httpsServer = function(secureOpts, defaultApp, createSecureServer) { - if (defaultApp) { - // TODO guard against being set twice? - _middlewareApp = defaultApp; - } + servers.http2Server = function(secureOpts, defaultApp) { + return servers._httpsServer(secureOpts, defaultApp, function(secureOpts, fn) { + secureOpts.allowHTTP1 = true; + return require("http2").createSecureServer(secureOpts, fn); + }); + }; + servers.httpsServer = function(secureOpts, defaultApp) { + return servers._httpsServer(secureOpts, defaultApp, function(secureOpts, fn) { + return require("https").createServer(secureOpts, fn); + }); + }; + servers._httpsServer = function(secureOpts, defaultApp, createSecureServer) { + if (defaultApp) { + // TODO guard against being set twice? + _middlewareApp = defaultApp; + } - if (_httpsServer) { - if (secureOpts && Object.keys(secureOpts).length) { - throw new Error("Call glx.httpsServer(tlsOptions) before calling glx.serveApp(app)"); - } - return _httpsServer; - } + if (_httpsServer) { + if (secureOpts && Object.keys(secureOpts).length) { + throw new Error("Call glx.httpsServer(tlsOptions) before calling glx.serveApp(app)"); + } + return _httpsServer; + } - if (!secureOpts) { - secureOpts = {}; - } + if (!secureOpts) { + secureOpts = {}; + } - _httpsServer = createSecureServer( - wrapDefaultSniCallback(greenlock, secureOpts), - HttpsMiddleware.create(greenlock, function(req, res) { - if (!_middlewareApp) { - throw new Error("Set app with `glx.serveApp(app)` or `glx.httpsServer(tlsOptions, app)`"); - } - _middlewareApp(req, res); - }) - ); - _httpsServer.once("error", startError); + _httpsServer = createSecureServer( + wrapDefaultSniCallback(greenlock, secureOpts), + HttpsMiddleware.create(greenlock, function(req, res) { + if (!_middlewareApp) { + throw new Error("Set app with `glx.serveApp(app)` or `glx.httpsServer(tlsOptions, app)`"); + } + _middlewareApp(req, res); + }) + ); + _httpsServer.once("error", startError); - return _httpsServer; - }; + return _httpsServer; + }; - servers.id = function() { - return (cluster.isWorker && cluster.worker.id) || "0"; - }; - servers.serveApp = function(app) { - return new Promise(function(resolve, reject) { - if ("function" !== typeof app) { - reject(new Error("glx.serveApp(app) expects a node/express app in the format `function (req, res) { ... }`")); - return; - } + servers.id = function() { + return (cluster.isWorker && cluster.worker.id) || "0"; + }; + servers.serveApp = function(app) { + return new Promise(function(resolve, reject) { + if ("function" !== typeof app) { + reject( + new Error( + "glx.serveApp(app) expects a node/express app in the format `function (req, res) { ... }`" + ) + ); + return; + } - var id = cluster.isWorker && cluster.worker.id; - var idstr = (id && "#" + id + " ") || ""; - var plainServer = servers.httpServer(require("redirect-https")()); - var plainAddr = "0.0.0.0"; - var plainPort = 80; - plainServer.listen(plainPort, plainAddr, function() { - console.info( - idstr + "Listening on", - plainAddr + ":" + plainPort, - "for ACME challenges, and redirecting to HTTPS" - ); + var id = cluster.isWorker && cluster.worker.id; + var idstr = (id && "#" + id + " ") || ""; + var plainServer = servers.httpServer(require("redirect-https")()); + var plainAddr = "0.0.0.0"; + var plainPort = 80; + plainServer.listen(plainPort, plainAddr, function() { + console.info( + idstr + "Listening on", + plainAddr + ":" + plainPort, + "for ACME challenges, and redirecting to HTTPS" + ); - // TODO fetch greenlock.servername - _middlewareApp = app || _middlewareApp; - var secureServer = servers.httpsServer(null, app); - var secureAddr = "0.0.0.0"; - var securePort = 443; - secureServer.listen(securePort, secureAddr, function() { - console.info(idstr + "Listening on", secureAddr + ":" + securePort, "for secure traffic"); + // TODO fetch greenlock.servername + _middlewareApp = app || _middlewareApp; + var secureServer = servers.httpsServer(null, app); + var secureAddr = "0.0.0.0"; + var securePort = 443; + secureServer.listen(securePort, secureAddr, function() { + console.info(idstr + "Listening on", secureAddr + ":" + securePort, "for secure traffic"); - plainServer.removeListener("error", startError); - secureServer.removeListener("error", startError); - resolve(); - }); - }); - }); - }; + plainServer.removeListener("error", startError); + secureServer.removeListener("error", startError); + resolve(); + }); + }); + }); + }; - return servers; + return servers; }; function explainError(e) { - console.error(); - console.error("Error: " + e.message); - if ("EACCES" === e.errno) { - console.error("You don't have prmission to access '" + e.address + ":" + e.port + "'."); - console.error('You probably need to use "sudo" or "sudo setcap \'cap_net_bind_service=+ep\' $(which node)"'); - } else if ("EADDRINUSE" === e.errno) { - console.error("'" + e.address + ":" + e.port + "' is already being used by some other program."); - console.error("You probably need to stop that program or restart your computer."); - } else { - console.error(e.code + ": '" + e.address + ":" + e.port + "'"); - } - console.error(); + console.error(); + console.error("Error: " + e.message); + if ("EACCES" === e.errno) { + console.error("You don't have prmission to access '" + e.address + ":" + e.port + "'."); + console.error('You probably need to use "sudo" or "sudo setcap \'cap_net_bind_service=+ep\' $(which node)"'); + } else if ("EADDRINUSE" === e.errno) { + console.error("'" + e.address + ":" + e.port + "' is already being used by some other program."); + console.error("You probably need to stop that program or restart your computer."); + } else { + console.error(e.code + ": '" + e.address + ":" + e.port + "'"); + } + console.error(); } function wrapDefaultSniCallback(greenlock, secureOpts) { - // I'm not sure yet if the original SNICallback - // should be called before or after, so I'm just - // going to delay making that choice until I have the use case - /* + // I'm not sure yet if the original SNICallback + // should be called before or after, so I'm just + // going to delay making that choice until I have the use case + /* if (!secureOpts.SNICallback) { secureOpts.SNICallback = function(servername, cb) { cb(null, null); }; } */ - if (secureOpts.SNICallback) { - console.warn(); - console.warn("[warning] Ignoring the given tlsOptions.SNICallback function."); - console.warn(); - console.warn(" We're very open to implementing support for this,"); - console.warn(" we just don't understand the use case yet."); - console.warn(" Please open an issue to discuss. We'd love to help."); - console.warn(); - } + if (secureOpts.SNICallback) { + console.warn(); + console.warn("[warning] Ignoring the given tlsOptions.SNICallback function."); + console.warn(); + console.warn(" We're very open to implementing support for this,"); + console.warn(" we just don't understand the use case yet."); + console.warn(" Please open an issue to discuss. We'd love to help."); + console.warn(); + } - // TODO greenlock.servername for workers - secureOpts.SNICallback = sni.create(greenlock, secureOpts); - return secureOpts; + // TODO greenlock.servername for workers + secureOpts.SNICallback = sni.create(greenlock, secureOpts); + return secureOpts; } diff --git a/single.js b/single.js index 9d529c1..3f7aab9 100644 --- a/single.js +++ b/single.js @@ -6,20 +6,20 @@ var Single = module.exports; var Servers = require("./servers.js"); Single.create = function(opts) { - var greenlock = require("./greenlock.js").create(opts); + var greenlock = require("./greenlock.js").create(opts); - var servers = Servers.create(greenlock); + var servers = Servers.create(greenlock); - var single = { - serve: function(fn) { - fn(servers); - return single; - }, - master: function(/*fn*/) { - // ignore - //fn(master); - return single; - } - }; - return single; + var single = { + serve: function(fn) { + fn(servers); + return single; + }, + master: function(/*fn*/) { + // ignore + //fn(master); + return single; + } + }; + return single; }; diff --git a/sni.js b/sni.js index 069882c..ea79728 100644 --- a/sni.js +++ b/sni.js @@ -13,182 +13,182 @@ var smallStagger = Math.round(Math.PI * (30 * 1000)); //secureOpts.SNICallback = sni.create(greenlock, secureOpts); sni.create = function(greenlock, secureOpts) { - var _cache = {}; - var defaultServername = greenlock.servername || ""; + var _cache = {}; + var defaultServername = greenlock.servername || ""; - if (secureOpts.cert) { - // Note: it's fine if greenlock.servername is undefined, - // but if the caller wants this to auto-renew, they should define it - _cache[defaultServername] = { - refreshAt: 0, - secureContext: tls.createSecureContext(secureOpts) - }; - } + if (secureOpts.cert) { + // Note: it's fine if greenlock.servername is undefined, + // but if the caller wants this to auto-renew, they should define it + _cache[defaultServername] = { + refreshAt: 0, + secureContext: tls.createSecureContext(secureOpts) + }; + } - return getSecureContext; + return getSecureContext; - function notify(ev, args) { - try { - // TODO _notify() or notify()? - (greenlock.notify || greenlock._notify)(ev, args); - } catch (e) { - console.error(e); - console.error(ev, args); - } - } + function notify(ev, args) { + try { + // TODO _notify() or notify()? + (greenlock.notify || greenlock._notify)(ev, args); + } catch (e) { + console.error(e); + console.error(ev, args); + } + } - function getSecureContext(servername, cb) { - //console.log("debug sni", servername); - if ("string" !== typeof servername) { - // this will never happen... right? but stranger things have... - console.error("[sanity fail] non-string servername:", servername); - cb(new Error("invalid servername"), null); - return; - } + function getSecureContext(servername, cb) { + //console.log("debug sni", servername); + if ("string" !== typeof servername) { + // this will never happen... right? but stranger things have... + console.error("[sanity fail] non-string servername:", servername); + cb(new Error("invalid servername"), null); + return; + } - var secureContext = getCachedContext(servername); - if (secureContext) { - //console.log("debug sni got cached context", servername, getCachedMeta(servername)); - cb(null, secureContext); - return; - } + var secureContext = getCachedContext(servername); + if (secureContext) { + //console.log("debug sni got cached context", servername, getCachedMeta(servername)); + cb(null, secureContext); + return; + } - getFreshContext(servername) - .then(function(secureContext) { - if (secureContext) { - //console.log("debug sni got fresh context", servername, getCachedMeta(servername)); - cb(null, secureContext); - return; - } - // Note: this does not replace tlsSocket.setSecureContext() - // as it only works when SNI has been sent - //console.log("debug sni got default context", servername, getCachedMeta(servername)); - cb(null, getDefaultContext()); - }) - .catch(function(err) { - if (!err.context) { - err.context = "sni_callback"; - } - notify("error", err); - //console.log("debug sni error", servername, err); - cb(err); - }); - } + getFreshContext(servername) + .then(function(secureContext) { + if (secureContext) { + //console.log("debug sni got fresh context", servername, getCachedMeta(servername)); + cb(null, secureContext); + return; + } + // Note: this does not replace tlsSocket.setSecureContext() + // as it only works when SNI has been sent + //console.log("debug sni got default context", servername, getCachedMeta(servername)); + cb(null, getDefaultContext()); + }) + .catch(function(err) { + if (!err.context) { + err.context = "sni_callback"; + } + notify("error", err); + //console.log("debug sni error", servername, err); + cb(err); + }); + } - function getCachedMeta(servername) { - var meta = _cache[servername]; - if (!meta) { - if (!_cache[wildname(servername)]) { - return null; - } - } - return meta; - } + function getCachedMeta(servername) { + var meta = _cache[servername]; + if (!meta) { + if (!_cache[wildname(servername)]) { + return null; + } + } + return meta; + } - function getCachedContext(servername) { - var meta = getCachedMeta(servername); - if (!meta) { - return null; - } + function getCachedContext(servername) { + var meta = getCachedMeta(servername); + if (!meta) { + return null; + } - // always renew in background - if (!meta.refreshAt || Date.now() >= meta.refreshAt) { - getFreshContext(servername).catch(function(e) { - if (!e.context) { - e.context = "sni_background_refresh"; - } - notify("error", e); - }); - } + // always renew in background + if (!meta.refreshAt || Date.now() >= meta.refreshAt) { + getFreshContext(servername).catch(function(e) { + if (!e.context) { + e.context = "sni_background_refresh"; + } + notify("error", e); + }); + } - // under normal circumstances this would never be expired - // and, if it is expired, something is so wrong it's probably - // not worth wating for the renewal - it has probably failed - return meta.secureContext; - } + // under normal circumstances this would never be expired + // and, if it is expired, something is so wrong it's probably + // not worth wating for the renewal - it has probably failed + return meta.secureContext; + } - function getFreshContext(servername) { - var meta = getCachedMeta(servername); - if (!meta && !validServername(servername)) { - return Promise.resolve(null); - } + function getFreshContext(servername) { + var meta = getCachedMeta(servername); + if (!meta && !validServername(servername)) { + return Promise.resolve(null); + } - if (meta) { - // prevent stampedes - meta.refreshAt = Date.now() + randomRefreshOffset(); - } + if (meta) { + // prevent stampedes + meta.refreshAt = Date.now() + randomRefreshOffset(); + } - // TODO don't get unknown certs at all, rely on auto-updates from greenlock - // Note: greenlock.get() will return an existing fresh cert or issue a new one - return greenlock.get({ servername: servername }).then(function(result) { - var meta = getCachedMeta(servername); - if (!meta) { - meta = _cache[servername] = { secureContext: { _valid: false } }; - } - // prevent from being punked by bot trolls - meta.refreshAt = Date.now() + smallStagger; + // TODO don't get unknown certs at all, rely on auto-updates from greenlock + // Note: greenlock.get() will return an existing fresh cert or issue a new one + return greenlock.get({ servername: servername }).then(function(result) { + var meta = getCachedMeta(servername); + if (!meta) { + meta = _cache[servername] = { secureContext: { _valid: false } }; + } + // prevent from being punked by bot trolls + meta.refreshAt = Date.now() + smallStagger; - // nothing to do - if (!result) { - return null; - } + // nothing to do + if (!result) { + return null; + } - // we only care about the first one - var pems = result.pems; - var site = result.site; - if (!pems || !pems.cert) { - // nothing to do - // (and the error should have been reported already) - return null; - } + // we only care about the first one + var pems = result.pems; + var site = result.site; + if (!pems || !pems.cert) { + // nothing to do + // (and the error should have been reported already) + return null; + } - meta = { - refreshAt: Date.now() + randomRefreshOffset(), - secureContext: tls.createSecureContext({ - // TODO support passphrase-protected privkeys - key: pems.privkey, - cert: pems.cert + "\n" + pems.chain + "\n" - }) - }; - meta.secureContext._valid = true; + meta = { + refreshAt: Date.now() + randomRefreshOffset(), + secureContext: tls.createSecureContext({ + // TODO support passphrase-protected privkeys + key: pems.privkey, + cert: pems.cert + "\n" + pems.chain + "\n" + }) + }; + meta.secureContext._valid = true; - // copy this same object into every place - (result.altnames || site.altnames || [result.subject || site.subject]).forEach(function(altname) { - _cache[altname] = meta; - }); + // copy this same object into every place + (result.altnames || site.altnames || [result.subject || site.subject]).forEach(function(altname) { + _cache[altname] = meta; + }); - return meta.secureContext; - }); - } + return meta.secureContext; + }); + } - function getDefaultContext() { - return getCachedContext(defaultServername); - } + function getDefaultContext() { + return getCachedContext(defaultServername); + } }; // whenever we need to know when to refresh next function randomRefreshOffset() { - var stagger = Math.round(refreshStagger / 2) - Math.round(Math.random() * refreshStagger); - return refreshOffset + stagger; + var stagger = Math.round(refreshStagger / 2) - Math.round(Math.random() * refreshStagger); + return refreshOffset + stagger; } function validServername(servername) { - // format and (lightly) sanitize sni so that users can be naive - // and not have to worry about SQL injection or fs discovery + // format and (lightly) sanitize sni so that users can be naive + // and not have to worry about SQL injection or fs discovery - servername = (servername || "").toLowerCase(); - // hostname labels allow a-z, 0-9, -, and are separated by dots - // _ is sometimes allowed, but not as a "hostname", and not by Let's Encrypt ACME - // REGEX // https://www.codeproject.com/Questions/1063023/alphanumeric-validation-javascript-without-regex - return servernameRe.test(servername) && -1 === servername.indexOf(".."); + servername = (servername || "").toLowerCase(); + // hostname labels allow a-z, 0-9, -, and are separated by dots + // _ is sometimes allowed, but not as a "hostname", and not by Let's Encrypt ACME + // REGEX // https://www.codeproject.com/Questions/1063023/alphanumeric-validation-javascript-without-regex + return servernameRe.test(servername) && -1 === servername.indexOf(".."); } function wildname(servername) { - return ( - "*." + - servername - .split(".") - .slice(1) - .join(".") - ); + return ( + "*." + + servername + .split(".") + .slice(1) + .join(".") + ); } diff --git a/test/greenlock.js b/test/greenlock.js index b2908ee..1f4861c 100644 --- a/test/greenlock.js +++ b/test/greenlock.js @@ -1,83 +1,83 @@ #!/usr/bin/env node var Greenlock = require("../"); var greenlock = Greenlock.create({ - version: "draft-11", - server: "https://acme-staging-v02.api.letsencrypt.org/directory", - agreeTos: true, - approvedDomains: ["example.com", "www.example.com"], - configDir: require("path").join(require("os").tmpdir(), "acme"), + version: "draft-11", + server: "https://acme-staging-v02.api.letsencrypt.org/directory", + agreeTos: true, + approvedDomains: ["example.com", "www.example.com"], + configDir: require("path").join(require("os").tmpdir(), "acme"), - app: require("express")().use("/", function(req, res) { - res.setHeader("Content-Type", "text/html; charset=utf-8"); - res.end("Hello, World!\n\nšŸ’š šŸ”’.js"); - }) + app: require("express")().use("/", function(req, res) { + res.setHeader("Content-Type", "text/html; charset=utf-8"); + res.end("Hello, World!\n\nšŸ’š šŸ”’.js"); + }) }); var server1 = greenlock.listen(5080, 5443); server1.on("listening", function() { - console.log("### THREE 3333 - All is well server1", this.address()); - setTimeout(function() { - // so that the address() object doesn't disappear - server1.close(); - server1.unencrypted.close(); - }, 10); + console.log("### THREE 3333 - All is well server1", this.address()); + setTimeout(function() { + // so that the address() object doesn't disappear + server1.close(); + server1.unencrypted.close(); + }, 10); }); setTimeout(function() { - var server2 = greenlock.listen(6080, 6443, function() { - console.log("### FIVE 55555 - Started server 2!"); - setTimeout(function() { - server2.close(); - server2.unencrypted.close(); - server6.close(); - server6.unencrypted.close(); - server7.close(); - server7.unencrypted.close(); - setTimeout(function() { - // TODO greenlock needs a close event (and to listen to its server's close event) - process.exit(0); - }, 1000); - }, 1000); - }); - server2.on("listening", function() { - console.log("### FOUR 44444 - All is well server2", server2.address()); - }); + var server2 = greenlock.listen(6080, 6443, function() { + console.log("### FIVE 55555 - Started server 2!"); + setTimeout(function() { + server2.close(); + server2.unencrypted.close(); + server6.close(); + server6.unencrypted.close(); + server7.close(); + server7.unencrypted.close(); + setTimeout(function() { + // TODO greenlock needs a close event (and to listen to its server's close event) + process.exit(0); + }, 1000); + }, 1000); + }); + server2.on("listening", function() { + console.log("### FOUR 44444 - All is well server2", server2.address()); + }); }, 1000); var server3 = greenlock.listen( - 22, - 22, - function() { - console.error("Error: expected to get an error when launching plain server on port 22"); - }, - function() { - console.error("Error: expected to get an error when launching " + server3.type + " server on port 22"); - } + 22, + 22, + function() { + console.error("Error: expected to get an error when launching plain server on port 22"); + }, + function() { + console.error("Error: expected to get an error when launching " + server3.type + " server on port 22"); + } ); server3.unencrypted.on("error", function() { - console.log("Success: caught expected (plain) error"); + console.log("Success: caught expected (plain) error"); }); server3.on("error", function() { - console.log("Success: caught expected " + server3.type + " error"); - //server3.close(); + console.log("Success: caught expected " + server3.type + " error"); + //server3.close(); }); var server4 = greenlock.listen( - 7080, - 7443, - function() { - console.log("Success: server4: plain"); - server4.unencrypted.close(); - }, - function() { - console.log("Success: server4: " + server4.type); - server4.close(); - } + 7080, + 7443, + function() { + console.log("Success: server4: plain"); + server4.unencrypted.close(); + }, + function() { + console.log("Success: server4: " + server4.type); + server4.close(); + } ); var server5 = greenlock.listen(10080, 10443, function() { - console.log("Server 5 with one fn", this.address()); - server5.close(); - server5.unencrypted.close(); + console.log("Server 5 with one fn", this.address()); + server5.close(); + server5.unencrypted.close(); }); var server6 = greenlock.listen("[::]:11080", "[::1]:11443"); diff --git a/worker.js b/worker.js index 5f6a8fa..1c44bea 100644 --- a/worker.js +++ b/worker.js @@ -6,57 +6,57 @@ var messageTimeout = 30 * 1000; var msgPrefix = "greenlock:"; Worker.create = function() { - var greenlock = {}; - ["getAcmeHttp01ChallengeResponse", "get", "notify"].forEach(function(k) { - greenlock[k] = function(args) { - return rpc(k, args); - }; - }); + var greenlock = {}; + ["getAcmeHttp01ChallengeResponse", "get", "notify"].forEach(function(k) { + greenlock[k] = function(args) { + return rpc(k, args); + }; + }); - var worker = { - serve: function(fn) { - var servers = require("./servers.js").create(greenlock); - fn(servers); - return worker; - }, - master: function() { - // ignore - return worker; - } - }; - return worker; + var worker = { + serve: function(fn) { + var servers = require("./servers.js").create(greenlock); + fn(servers); + return worker; + }, + master: function() { + // ignore + return worker; + } + }; + return worker; }; function rpc(funcname, msg) { - return new Promise(function(resolve, reject) { - var rnd = Math.random() - .toString() - .slice(2) - .toString(16); - var id = msgPrefix + rnd; - var timeout; + return new Promise(function(resolve, reject) { + var rnd = Math.random() + .toString() + .slice(2) + .toString(16); + var id = msgPrefix + rnd; + var timeout; - function getResponse(msg) { - if (msg._id !== id) { - return; - } - process.removeListener("message", getResponse); - clearTimeout(timeout); - resolve(msg._result); - } + function getResponse(msg) { + if (msg._id !== id) { + return; + } + process.removeListener("message", getResponse); + clearTimeout(timeout); + resolve(msg._result); + } - // TODO keep a single listener than just responds - // via a collection of callbacks? or leave as is? - process.on("message", getResponse); - process.send({ - _id: id, - _funcname: funcname, - _input: msg - }); + // TODO keep a single listener than just responds + // via a collection of callbacks? or leave as is? + process.on("message", getResponse); + process.send({ + _id: id, + _funcname: funcname, + _input: msg + }); - timeout = setTimeout(function() { - process.removeListener("message", getResponse); - reject(new Error("worker rpc request timeout")); - }, messageTimeout); - }); + timeout = setTimeout(function() { + process.removeListener("message", getResponse); + reject(new Error("worker rpc request timeout")); + }, messageTimeout); + }); }