From fceeb8c72c6e03b5ad53a4d9942558942e736cd4 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 1 Nov 2019 04:12:40 -0600 Subject: [PATCH] v3.0.9: add examples --- README.md | 138 +++++++++++++----- examples/cluster/server.js | 39 +++++ examples/demo.js | 75 ---------- examples/{ => express}/my-express-app.js | 0 examples/express/server.js | 27 ++++ examples/force-renew.js | 30 ---- .../{http-proxy.js => http-proxy/server.js} | 27 ++-- examples/http/server.js | 42 ++++++ examples/http2.js | 70 --------- examples/http2/server.js | 48 ++++++ examples/https/server.js | 49 +++++++ examples/production.js | 88 ----------- examples/quickstart.js | 38 ----- examples/quickstart/README.md | 22 +++ examples/quickstart/server.js | 32 ++++ examples/remote-access.js | 104 ------------- examples/socket.io.js | 32 ---- examples/socket.io/server.js | 49 +++++++ examples/spdy.js | 64 -------- examples/spdy/server.js | 3 + examples/vhost.js | 134 ----------------- examples/websockets.js | 46 ------ examples/websockets/server.js | 42 ++++++ examples/wildcard.js | 77 ---------- master.js | 2 +- package.json | 2 +- 26 files changed, 471 insertions(+), 809 deletions(-) create mode 100644 examples/cluster/server.js delete mode 100644 examples/demo.js rename examples/{ => express}/my-express-app.js (100%) create mode 100644 examples/express/server.js delete mode 100644 examples/force-renew.js rename examples/{http-proxy.js => http-proxy/server.js} (68%) create mode 100644 examples/http/server.js delete mode 100644 examples/http2.js create mode 100644 examples/http2/server.js create mode 100644 examples/https/server.js delete mode 100644 examples/production.js delete mode 100644 examples/quickstart.js create mode 100644 examples/quickstart/README.md create mode 100644 examples/quickstart/server.js delete mode 100644 examples/remote-access.js delete mode 100644 examples/socket.io.js create mode 100644 examples/socket.io/server.js delete mode 100644 examples/spdy.js create mode 100644 examples/spdy/server.js delete mode 100644 examples/vhost.js delete mode 100644 examples/websockets.js create mode 100644 examples/websockets/server.js delete mode 100644 examples/wildcard.js diff --git a/README.md b/README.md index b7059b5..3df9ace 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +# New Documentation & [v2/v3 Migration Guide](https://git.rootprojects.org/root/greenlock.js/src/branch/v3/MIGRATION_GUIDE_V2_V3.md) + +Greenlock v3 just came out of private beta **today** (Nov 1st, 2019). + +The code is complete and we're working on great documentation. + +Many **examples** and **full API** documentation are still coming. + # [Greenlock Express](https://git.rootprojects.org/root/greenlock-express.js) is Let's Encrypt for Node ![Greenlock Logo](https://git.rootprojects.org/root/greenlock.js/raw/branch/master/logo/greenlock-1063x250.png "Greenlock Logo") @@ -8,14 +16,28 @@ Free SSL, Automated HTTPS / HTTP2, served with Node via Express, Koa, hapi, etc. ### Let's Encrypt for Node, Express, etc +Greenlock Express is a **Web Server** with **Fully Automated HTTPS** and renewals. + ```js +var pkg = require("./package.json"); + require("greenlock-express") .init(function getConfig() { - return { package: require("./package.json") }; + // Greenlock Config + + return { + package: { name: pkg.name, version: pkg.version }, + maintainerEmail: pkg.author, + cluster: false + }; }) .serve(httpsWorker); +``` -function httpsWorker(server) { +With **Express**: + +```js +function httpsWorker(glx) { // Works with any Node app (Express, etc) var app = require("./my-express-app.js"); @@ -26,12 +48,27 @@ function httpsWorker(server) { // Serves on 80 and 443 // Get's SSL certificates magically! - server.serveApp(app); + glx.serveApp(app); +} +``` + +Or with **plain** node HTTP: + +```js +function httpsWorker(glx) { + // Serves on 80 and 443 + // Get's SSL certificates magically! + + glx.serveApp(function(req, res) { + res.end("Hello, Encrypted World!"); + }); } ``` Manage via API or the config file: +`~/.config/greenlock/manage.json`: (default filesystem config) + ```json { "subscriberEmail": "letsencrypt-test@therootcompany.com", @@ -75,25 +112,32 @@ Manage via API or the config file: # Plenty of Examples +**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.js) - - [Node's **http2**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/http2.js) - - [Node's https](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/https.js) - - [**WebSockets**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/websockets.js) - - [Socket.IO](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/socket-io.js) - - [Cluster](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/socket-io.js) - - [**Wildcards**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/wildcards/README.md) - - [**Localhost**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/localhost/README.md) - - [**CI/CD**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/ci-cd/README.md) + - [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 + + +- [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) # QuickStart Guide @@ -198,30 +242,13 @@ Listening on 0.0.0.0:443 for secure traffic ## 4. Manage domains -Management can be done via the **CLI** or the JavaScript [**API**](https://git.rootprojects.org/root/greenlock.js/). -Since this is the QuickStart, we'll demo the **CLI**: +The management API is built to work with Databases, S3, etc. -You need to create a Let's Encrypt _subscriber account_, which can be done globally, or per-site. -All individuals, and most businesses, should set this globally: - -```bash -# Set a global subscriber account -npx greenlock config --subscriber-email 'mycompany@example.com' --agree-to-terms true -``` - - - -A Let's Encrypt SSL certificate has a "Subject" (Primary Domain) and up to 100 "Alternative Names" -(of which the first _must_ be the subject). - -```bash -# Add a certificate with specific domains -npx greenlock add --subject example.com --altnames example.com,www.example.com -``` - - +HOWEVER, by default it starts with a simple config file. + `~/.config/greenlock/manager.json`: @@ -238,13 +265,46 @@ This will update the config file (assuming the default fs-based management plugi } ``` +COMING SOON + +Management can be done via the **CLI** or the JavaScript [**API**](https://git.rootprojects.org/root/greenlock.js/). +Since this is the QuickStart, we'll demo the **CLI**: + +You need to create a Let's Encrypt _subscriber account_, which can be done globally, or per-site. +All individuals, and most businesses, should set this globally: + +```bash +# COMING SOON +# (this command should be here by Nov 5th) +# (edit the config by hand for now) +# +# Set a global subscriber account +npx greenlock config --subscriber-email 'mycompany@example.com' --agree-to-terms true +``` + + + +A Let's Encrypt SSL certificate has a "Subject" (Primary Domain) and up to 100 "Alternative Names" +(of which the first _must_ be the subject). + +```bash +# COMING SOON +# (this command should be here by Nov 5th) +# (edit the config by hand for now) +# +# Add a certificate with specific domains +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/README.md) - - [**Localhost**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/localhost/README.md) - - [**CI/CD**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/ci-cd/README.md) + - [**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) # Full Documentation diff --git a/examples/cluster/server.js b/examples/cluster/server.js new file mode 100644 index 0000000..575b602 --- /dev/null +++ b/examples/cluster/server.js @@ -0,0 +1,39 @@ +"use strict"; + +var pkg = require("../../package.json"); +//require("greenlock-express") +require("../../") + .init(function getConfig() { + // Greenlock Config + + 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, + + // 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; + + 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); +} diff --git a/examples/demo.js b/examples/demo.js deleted file mode 100644 index d204bea..0000000 --- a/examples/demo.js +++ /dev/null @@ -1,75 +0,0 @@ -"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/my-express-app.js b/examples/express/my-express-app.js similarity index 100% rename from examples/my-express-app.js rename to examples/express/my-express-app.js diff --git a/examples/express/server.js b/examples/express/server.js new file mode 100644 index 0000000..97f5752 --- /dev/null +++ b/examples/express/server.js @@ -0,0 +1,27 @@ +"use strict"; + +function httpsWorker(glx) { + var app = require("./my-express-app.js"); + + app.get("/hello", function(req, res) { + res.end("Hello, Encrypted World!"); + }); + + // 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 + + return { + package: { name: "http2-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/force-renew.js b/examples/force-renew.js deleted file mode 100644 index 6224758..0000000 --- a/examples/force-renew.js +++ /dev/null @@ -1,30 +0,0 @@ -"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/http-proxy.js b/examples/http-proxy/server.js similarity index 68% rename from examples/http-proxy.js rename to examples/http-proxy/server.js index 04dd42f..3d4cc77 100644 --- a/examples/http-proxy.js +++ b/examples/http-proxy/server.js @@ -1,16 +1,9 @@ "use strict"; -require("@root/greenlock-express") - .init(function getConfig() { - return { package: require("../package.json") }; - }) - .serve(httpsWorker); - function httpsWorker(glx) { - 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) { @@ -20,11 +13,11 @@ function httpsWorker(glx) { return; }); - // We'll proxy websocketts too + // We'll proxy websockets too server.on("upgrade", function(req, socket, head) { proxy.ws(req, socket, head, { ws: true, - target: "ws://localhost:1443" + target: "ws://localhost:3000" }); }); @@ -35,3 +28,17 @@ function httpsWorker(glx) { }); }); } + +var pkg = require("../../package.json"); +//require("greenlock-express") +require("../../") + .init(function getConfig() { + // Greenlock Config + + 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 new file mode 100644 index 0000000..28b3c47 --- /dev/null +++ b/examples/http/server.js @@ -0,0 +1,42 @@ +"use strict"; + +var pkg = require("../../package.json"); + +// The WRONG way: +//var http = require('http'); +//var httpServer = https.createSecureServer(redirectToHttps); +// +// Why is that wrong? +// Greenlock needs to change some low-level http and https options. +// 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) + // + + // 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()); + }); +} + +//require("greenlock-express") +require("../../") + .init(function getConfig() { + // Greenlock Config + + return { + package: { name: "plain-http-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/http2.js b/examples/http2.js deleted file mode 100644 index a890715..0000000 --- a/examples/http2.js +++ /dev/null @@ -1,70 +0,0 @@ -"use strict"; - -//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(redirectHttps); -require("http") - .createServer(acmeChallengeHandler) - .listen(80, function() { - console.log("Listening for ACME http-01 challenges on", this.address()); - }); - -//////////////////////// -// node.js' http2 api // -//////////////////////// - -// http2 is a new API with which you would use hapi or koa, not express -var server = require("http2").createSecureServer(greenlock.tlsOptions); -server.on("error", function(err) { - console.error(err); -}); -// WARNING: Because the middleware don't handle this API style, -// the Host headers are unmodified and potentially dangerous -// (ex: Host: Robert'); DROP TABLE Students;) -server.on("stream", function(stream, headers) { - console.log(headers); - stream.respond({ - "content-type": "text/html", - ":status": 200 - }); - stream.end("Hello, HTTP2 World!"); -}); -server.on("listening", function() { - console.log("Listening for http2 requests on", this.address()); -}); -server.listen(443); diff --git a/examples/http2/server.js b/examples/http2/server.js new file mode 100644 index 0000000..6518b4d --- /dev/null +++ b/examples/http2/server.js @@ -0,0 +1,48 @@ +"use strict"; + +var pkg = require("../../package.json"); + +// The WRONG way: +//var http2 = require('http2'); +//var http2Server = https.createSecureServer(tlsOptions, app); +// +// Why is that wrong? +// Greenlock needs to change some low-level http and https options. +// Use glx.httpsServer(tlsOptions, app) instead. + +function httpsWorker(glx) { + // + // HTTP2 is the default httpsServer for node v12+ + // (HTTPS/1.1 is used for node <= v11) + // + + // Get the raw http2 server: + var http2Server = glx.httpsServer(function(req, res) { + res.end("Hello, Encrypted World!"); + }); + + 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()); + }); +} + +//require("greenlock-express") +require("../../") + .init(function getConfig() { + // Greenlock Config + + 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 new file mode 100644 index 0000000..d4a2c58 --- /dev/null +++ b/examples/https/server.js @@ -0,0 +1,49 @@ +"use strict"; + +var pkg = require("../../package.json"); + +// The WRONG way: +//var https = require('https'); +//var httpsServer = https.createServer(tlsOptions, app); +// +// Why is that wrong? +// Greenlock needs to change some low-level http and https options. +// Use glx.httpsServer(tlsOptions, app) instead. + +function httpsWorker(glx) { + // + // HTTPS/1.1 is only used for node v11 or lower + // (HTTP2 is used for node v12+) + // + // Why not just require('https')? + + // 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()); + }); + + // 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 + + return { + package: { name: "https1-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/production.js b/examples/production.js deleted file mode 100644 index 2bcfda9..0000000 --- a/examples/production.js +++ /dev/null @@ -1,88 +0,0 @@ -"use strict"; - -// -// My Secure Server -// -//var greenlock = require('greenlock-express') -var greenlock = require("../").create({ - // Let's Encrypt v2 is ACME draft 11 - // Note: If at first you don't succeed, stop and switch to staging - // https://acme-staging-v02.api.letsencrypt.org/directory - server: "https://acme-v02.api.letsencrypt.org/directory", - version: "draft-11", - // You MUST have write access to save certs - configDir: "~/.config/acme/", - - // The previous 'simple' example set these values statically, - // but this example uses approveDomains() to set them dynamically - //, email: 'none@see.note.above' - //, agreeTos: false - - // approveDomains is the right place to check a database for - // email addresses with domains and agreements and such - approveDomains: approveDomains, - - app: require("./my-express-app.js"), - - // Get notified of important updates and help me make greenlock better - communityMember: true - - //, debug: true -}); - -var server = greenlock.listen(80, 443); - -// -// My Secure Database Check -// -function approveDomains(opts, certs, cb) { - // Only one domain is listed with *automatic* registration via SNI - // (it's an array because managed registration allows for multiple domains, - // which was the case in the simple example) - console.log(opts.domains); - - // The domains being approved for the first time are listed in opts.domains - // Certs being renewed are listed in certs.altnames - if (certs) { - opts.domains = [certs.subject].concat(certs.altnames); - } - - fooCheckDb(opts.domains, function(err, agree, email) { - if (err) { - cb(err); - return; - } - - // Services SHOULD automatically accept the ToS and use YOUR email - // Clients MUST NOT accept the ToS without asking the user - opts.agreeTos = agree; - opts.email = email; - - // NOTE: you can also change other options such as `challengeType` and `challenge` - // (this would be helpful if you decided you wanted wildcard support as a domain altname) - // opts.challengeType = 'http-01'; - // opts.challenge = require('le-challenge-fs').create({}); - - cb(null, { options: opts, certs: certs }); - }); -} - -// -// My User / Domain Database -// -function fooCheckDb(domains, cb) { - // This is an oversimplified example of how we might implement a check in - // our database if we have different rules for different users and domains - var domains = ["example.com", "www.example.com"]; - var userEmail = "john.doe@example.com"; - var userAgrees = true; - var passCheck = opts.domains.every(function(domain) { - return -1 !== domains.indexOf(domain); - }); - - if (!passCheck) { - cb(new Error("domain not allowed")); - } else { - cb(null, userAgrees, userEmail); - } -} diff --git a/examples/quickstart.js b/examples/quickstart.js deleted file mode 100644 index f13ecc9..0000000 --- a/examples/quickstart.js +++ /dev/null @@ -1,38 +0,0 @@ -"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 - - // You MUST change this to a valid email address - email: "john.doe@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/", - store: require("greenlock-store-fs"), - - app: require("express")().use("/", function(req, res) { - res.setHeader("Content-Type", "text/html; charset=utf-8"); - res.end("Hello, World!\n\nšŸ’š šŸ”’.js"); - }), - - // Get notified of important updates and help me make greenlock better - communityMember: true - - //, debug: true - }) - .listen(80, 443); diff --git a/examples/quickstart/README.md b/examples/quickstart/README.md new file mode 100644 index 0000000..0ebffad --- /dev/null +++ b/examples/quickstart/README.md @@ -0,0 +1,22 @@ +# Quick Start for Let's Encrypt with Node.js + +```js +npm install --save greenlock-express +``` + +Manage via API or the config file: + +`~/.config/greenlock/manage.json`: (default filesystem config) + +```json +{ + "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 new file mode 100644 index 0000000..82192f2 --- /dev/null +++ b/examples/quickstart/server.js @@ -0,0 +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!"); + }; + + // 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 + + 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", + + // Change to true when you're ready to make your app cloud-scale + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/remote-access.js b/examples/remote-access.js deleted file mode 100644 index 4e66cef..0000000 --- a/examples/remote-access.js +++ /dev/null @@ -1,104 +0,0 @@ -"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/socket.io.js b/examples/socket.io.js deleted file mode 100644 index b626025..0000000 --- a/examples/socket.io.js +++ /dev/null @@ -1,32 +0,0 @@ -// First and foremost: -// I'm not a fan of `socket.io` because it's huge and complex. -// I much prefer `ws` because it's very simple and easy. -// That said, it's popular....... -"use strict"; - -//var greenlock = require('greenlock-express'); -var greenlock = require("../"); -var options = require("./greenlock-options.js"); -var socketio = require("socket.io"); -var server; -var io; - -// Any node http app will do - whether express, raw http or whatever -options.app = require("express")().use("/", function(req, res) { - res.setHeader("Content-Type", "text/html; charset=utf-8"); - res.end("Hello, World!\n\nšŸ’š šŸ”’.js"); -}); - -// The server that's handed back from `listen` is a raw https server -server = greenlock.create(options).listen(80, 443); -io = socketio(server); - -// 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); - }); -}); diff --git a/examples/socket.io/server.js b/examples/socket.io/server.js new file mode 100644 index 0000000..fb8be4d --- /dev/null +++ b/examples/socket.io/server.js @@ -0,0 +1,49 @@ +// First and foremost: +// I'm not a fan of `socket.io` because it's huge and complex. +// I much prefer `ws` because it's very simple and easy. +// That said, it's popular....... +"use strict"; + +// Note: You DO NOT NEED socket.io +// You can just use WebSockets +// (see the websocket example) + +function httpsWorker(glx) { + var socketio = require("socket.io"); + var io; + + // we need the raw https server + var server = glx.httpsServer(); + + io = socketio(server); + + // 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); + }); + }); + + // 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 + + return { + package: { name: "socket-io-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/spdy.js b/examples/spdy.js deleted file mode 100644 index 6cb87dd..0000000 --- a/examples/spdy.js +++ /dev/null @@ -1,64 +0,0 @@ -"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/", // MUST have write access - - // 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(redirectHttps); -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 myApp = require("./my-express-app.js"); -var server = require("spdy").createServer(spdyOptions, myApp); -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/spdy/server.js b/examples/spdy/server.js new file mode 100644 index 0000000..0e9c26d --- /dev/null +++ b/examples/spdy/server.js @@ -0,0 +1,3 @@ +// SPDY is dead. It was replaced by HTTP2, which is a native node module +// +// Greenlock uses HTTP2 as the default https server in node v12+ diff --git a/examples/vhost.js b/examples/vhost.js deleted file mode 100644 index 618bf35..0000000 --- a/examples/vhost.js +++ /dev/null @@ -1,134 +0,0 @@ -#!/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/websockets.js b/examples/websockets.js deleted file mode 100644 index 26cbc25..0000000 --- a/examples/websockets.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; - -//////////////////////// -// Greenlock Setup // -//////////////////////// - -//var Greenlock = require('greenlock-express'); -var Greenlock = require("../"); -var greenlock = Greenlock.create({ - // Let's Encrypt v2 is ACME draft 11 - // Note: If at first you don't succeed, stop and switch to staging - // https://acme-staging-v02.api.letsencrypt.org/directory - server: "https://acme-v02.api.letsencrypt.org/directory", - version: "draft-11", - configDir: "~/.config/acme/", - app: require("./my-express-app.js"), - - // You MUST change these to a valid email and domains - email: "john.doe@example.com", - approvedDomains: ["example.com", "www.example.com"], - agreeTos: true, - - // Get notified of important updates and help me make greenlock better - communityMember: true, - telemetry: true - //, debug: true -}); - -var server = greenlock.listen(80, 443); - -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); - }); -}); diff --git a/examples/websockets/server.js b/examples/websockets/server.js new file mode 100644 index 0000000..c5694ad --- /dev/null +++ b/examples/websockets/server.js @@ -0,0 +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); + }); + }); + + // 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 + + return { + package: { name: "websocket-example", version: pkg.version }, + maintainerEmail: "jon@example.com", + cluster: false + }; + }) + .serve(httpsWorker); diff --git a/examples/wildcard.js b/examples/wildcard.js deleted file mode 100644 index 5dbadc9..0000000 --- a/examples/wildcard.js +++ /dev/null @@ -1,77 +0,0 @@ -#!/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/master.js b/master.js index b4e945a..261f124 100644 --- a/master.js +++ b/master.js @@ -66,7 +66,7 @@ Master._spawnWorkers = function(opts, greenlock) { // process rpc messages // start when dead - var numWorkers = parseInt(opts.numWorkers, 10); + var numWorkers = parseInt(opts.workers || opts.numWorkers, 10); if (!numWorkers) { if (numCpus <= 2) { numWorkers = 2; diff --git a/package.json b/package.json index 0b6e7a9..099af9c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@root/greenlock-express", - "version": "3.0.8", + "version": "3.0.9", "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",