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",