|
|
@ -1,39 +1,21 @@ |
|
|
|
# [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") |
|
|
|
|
|
|
|
| Built by [Root](https://therootcompany.com) for [Hub](https://rootprojects.org/hub/) |
|
|
|
| Built by [Root](https://therootcompany.com) for [Hub](https://rootprojects.org/hub/) | |
|
|
|
|
|
|
|
Free SSL, Automated HTTPS / HTTP2, served with Node via Express, Koa, hapi, etc. |
|
|
|
![Greenlock Logo](https://git.rootprojects.org/root/greenlock.js/raw/branch/master/logo/greenlock-1063x250.png "Greenlock Logo") |
|
|
|
|
|
|
|
### Let's Encrypt for Node and Express (and Koa, hapi, rill, etc) |
|
|
|
### Free SSL for Node Web Servers |
|
|
|
|
|
|
|
Greenlock Express is a **Web Server** with **Fully Automated HTTPS** and renewals. |
|
|
|
|
|
|
|
You define your app, and let Greenlock handle issuing and renewing Free SSL Certificates. |
|
|
|
|
|
|
|
**Cloud-ready** with Node `cluster`. |
|
|
|
|
|
|
|
# Serve your Sites with Free SSL |
|
|
|
|
|
|
|
- 1. Create a Project with Greenlock Express |
|
|
|
- 2. Initialize and Setup |
|
|
|
- 3. Add Domains, and Hello, World! |
|
|
|
You define your app and let Greenlock handle issuing and renewing Free SSL Certificates. |
|
|
|
|
|
|
|
```bash |
|
|
|
npm init |
|
|
|
``` |
|
|
|
|
|
|
|
```bash |
|
|
|
npm install --save greenlock-express@v4 |
|
|
|
``` |
|
|
|
|
|
|
|
```bash |
|
|
|
npx greenlock init --config-dir ./greenlock.d --maintainer-email 'jon@example.com' |
|
|
|
``` |
|
|
|
|
|
|
|
<details> |
|
|
|
<summary>server.js</summary> |
|
|
|
`server.js`: |
|
|
|
|
|
|
|
```js |
|
|
|
"use strict"; |
|
|
@ -43,9 +25,10 @@ var app = require("./app.js"); |
|
|
|
require("greenlock-express") |
|
|
|
.init({ |
|
|
|
packageRoot: __dirname, |
|
|
|
configDir: "./greenlock.d", |
|
|
|
|
|
|
|
// contact for security and critical bug notices |
|
|
|
configDir: "./greenlock.d", |
|
|
|
maintainerEmail: "jon@example.com", |
|
|
|
|
|
|
|
// whether or not to run at cloudscale |
|
|
|
cluster: false |
|
|
@ -55,52 +38,12 @@ require("greenlock-express") |
|
|
|
.serve(app); |
|
|
|
``` |
|
|
|
|
|
|
|
</details> |
|
|
|
|
|
|
|
<details> |
|
|
|
<summary>app.js</summary> |
|
|
|
|
|
|
|
```js |
|
|
|
"use strict"; |
|
|
|
|
|
|
|
// Here's a vanilla HTTP app to start, |
|
|
|
// but feel free to replace it with Express, Koa, etc |
|
|
|
var app = function(req, res) { |
|
|
|
res.end("Hello, Encrypted World!"); |
|
|
|
}; |
|
|
|
|
|
|
|
module.exports = app; |
|
|
|
``` |
|
|
|
|
|
|
|
</details> |
|
|
|
|
|
|
|
```bash |
|
|
|
npx greenlock add --subject example.com --altnames example.com |
|
|
|
``` |
|
|
|
|
|
|
|
<details> |
|
|
|
<summary>greenlock.d/config.json</summary> |
|
|
|
|
|
|
|
<!-- TODO update manager to write array rather than object --> |
|
|
|
`./greenlock.d/config.json`: |
|
|
|
|
|
|
|
```json |
|
|
|
{ "sites": [{ "subject": "example.com", "altnames": ["example.com"] }] } |
|
|
|
``` |
|
|
|
|
|
|
|
</details> |
|
|
|
|
|
|
|
```bash |
|
|
|
npm start -- --staging |
|
|
|
``` |
|
|
|
|
|
|
|
```txt |
|
|
|
> my-project@1.0.0 start /srv/www/my-project |
|
|
|
> node server.js |
|
|
|
|
|
|
|
Listening on 0.0.0.0:80 for ACME challenges and HTTPS redirects |
|
|
|
Listening on 0.0.0.0:443 for secure traffic |
|
|
|
``` |
|
|
|
|
|
|
|
# Let's Encrypt for... |
|
|
|
|
|
|
|
- IoT |
|
|
@ -123,281 +66,124 @@ Listening on 0.0.0.0:443 for secure traffic |
|
|
|
- [x] **Wildcard** SSL |
|
|
|
- [x] **Localhost** certificates |
|
|
|
- [x] HTTPS-enabled Secure **WebSockets** (`wss://`) |
|
|
|
- [x] **Cloud-ready** with Node `cluster`. |
|
|
|
- [x] Fully customizable |
|
|
|
- [x] **Reasonable defaults** |
|
|
|
- [x] Domain Management |
|
|
|
- [x] Key and Certificate Management |
|
|
|
- [x] ACME Challenge Plugins |
|
|
|
|
|
|
|
# QuickStart Guide |
|
|
|
# Compatibility |
|
|
|
|
|
|
|
Easy as 1, 2, 3... 4 |
|
|
|
Works with _any_ node http app, including |
|
|
|
|
|
|
|
<details> |
|
|
|
<summary>1. Create a node project</summary> |
|
|
|
- [x] Express |
|
|
|
- [x] Koa |
|
|
|
- [x] hapi |
|
|
|
- [x] rill |
|
|
|
- [x] http2 |
|
|
|
- [x] cluster |
|
|
|
- [x] etc... |
|
|
|
|
|
|
|
## 1. Create a node project |
|
|
|
# QuickStart: Serve Sites with Free SSL |
|
|
|
|
|
|
|
Create an empty node project. |
|
|
|
Easy as 1, 2, 3... 4 |
|
|
|
|
|
|
|
Be sure to fill out the package name, version, and an author email. |
|
|
|
1. Create a Project with Greenlock Express |
|
|
|
- `server.js` |
|
|
|
- `app.js` |
|
|
|
2. Setup the config file (or database) |
|
|
|
- `greenlock.d/config.json` |
|
|
|
3. Add Domains |
|
|
|
- `npx greenlock add --subject example.com --altnames example.com` |
|
|
|
4. Hello, World! |
|
|
|
- `npm start -- --staging` |
|
|
|
|
|
|
|
```bash |
|
|
|
mkdir ~/my-project |
|
|
|
pushd ~/my-project |
|
|
|
npm init |
|
|
|
npm install --save greenlock-express@v4 |
|
|
|
``` |
|
|
|
|
|
|
|
</details> |
|
|
|
|
|
|
|
<details> |
|
|
|
<summary>2. Create an http app (i.e. express)</summary> |
|
|
|
|
|
|
|
## 2. Create an http app (i.e. express) |
|
|
|
You can use **local file storage** or a **database**. The default is to use file storage. |
|
|
|
|
|
|
|
This example is shown with Express, but any node app will do. Greenlock |
|
|
|
works with everything. |
|
|
|
(or any node-style http app) |
|
|
|
|
|
|
|
`my-express-app.js`: |
|
|
|
|
|
|
|
```js |
|
|
|
"use strict"; |
|
|
|
|
|
|
|
// A plain, node-style app |
|
|
|
|
|
|
|
function myPlainNodeHttpApp(req, res) { |
|
|
|
res.end("Hello, Encrypted World!"); |
|
|
|
} |
|
|
|
|
|
|
|
// Wrap that plain app in express, |
|
|
|
// because that's what you're used to |
|
|
|
|
|
|
|
var express = require("express"); |
|
|
|
var app = express(); |
|
|
|
app.get("/", myPlainNodeHttpApp); |
|
|
|
|
|
|
|
// export the app normally |
|
|
|
// do not .listen() |
|
|
|
|
|
|
|
module.exports = app; |
|
|
|
```bash |
|
|
|
npx greenlock init --config-dir ./greenlock.d --maintainer-email 'jon@example.com' |
|
|
|
``` |
|
|
|
|
|
|
|
</details> |
|
|
|
|
|
|
|
<details> |
|
|
|
<summary>3. Serve with Greenlock Express</summary> |
|
|
|
|
|
|
|
## 3. Serve with Greenlock Express |
|
|
|
|
|
|
|
Greenlock Express is designed with these goals in mind: |
|
|
|
|
|
|
|
- Simplicity and ease-of-use |
|
|
|
- Performance and scalability |
|
|
|
- Configurability and control |
|
|
|
|
|
|
|
You can start with **near-zero configuration** and |
|
|
|
slowly add options for greater performance and customization |
|
|
|
later, if you need them. |
|
|
|
|
|
|
|
`server.js`: |
|
|
|
<summary>server.js</summary> |
|
|
|
|
|
|
|
```js |
|
|
|
"use strict"; |
|
|
|
|
|
|
|
//var pkg = require("./package.json"); |
|
|
|
var app = require("./app.js"); |
|
|
|
|
|
|
|
require("greenlock-express") |
|
|
|
.init({ |
|
|
|
// name & version for ACME client user agent |
|
|
|
//packageAgent: pkg.name + "/" + pkg.version, |
|
|
|
|
|
|
|
// contact for security and critical bug notices |
|
|
|
maintainerEmail: pkg.author, |
|
|
|
|
|
|
|
// where to find .greenlockrc and set default paths |
|
|
|
packageRoot: __dirname, |
|
|
|
|
|
|
|
// where config and certificate stuff go |
|
|
|
// contact for security and critical bug notices |
|
|
|
configDir: "./greenlock.d", |
|
|
|
|
|
|
|
// whether or not to run at cloudscale |
|
|
|
cluster: false |
|
|
|
}) |
|
|
|
// Serves on 80 and 443 |
|
|
|
// Get's SSL certificates magically! |
|
|
|
.serve(app); |
|
|
|
``` |
|
|
|
|
|
|
|
And start your server: |
|
|
|
|
|
|
|
```bash |
|
|
|
# Allow non-root node to use ports 80 (HTTP) and 443 (HTTPS) |
|
|
|
sudo setcap 'cap_net_bind_service=+ep' $(which node) |
|
|
|
``` |
|
|
|
|
|
|
|
```bash |
|
|
|
# `npm start` will call `node ./server.js` by default |
|
|
|
npm start |
|
|
|
``` |
|
|
|
|
|
|
|
```bash |
|
|
|
# use --staging to use the development API until you're ready to get real certificates |
|
|
|
npm start -- --staging |
|
|
|
``` |
|
|
|
|
|
|
|
```txt |
|
|
|
Greenlock v4.0.0 |
|
|
|
Greenlock Config Dir/File: ./greenlock.d/config.json |
|
|
|
|
|
|
|
Listening on 0.0.0.0:80 for ACME challenges and HTTPS redirects |
|
|
|
Listening on 0.0.0.0:443 for secure traffic |
|
|
|
``` |
|
|
|
|
|
|
|
</details> |
|
|
|
|
|
|
|
<details> |
|
|
|
<summary>4. Manage SSL Certificates and Domains</summary> |
|
|
|
|
|
|
|
## 4. Manage domains |
|
|
|
|
|
|
|
The management API is built to work with Databases, S3, etc. |
|
|
|
|
|
|
|
By default, it's just a simple config file and directory. |
|
|
|
|
|
|
|
```bash |
|
|
|
# see which manager and what options are in use |
|
|
|
cat .greenlockrc |
|
|
|
``` |
|
|
|
|
|
|
|
<details> |
|
|
|
<summary>Example Output</summary> |
|
|
|
|
|
|
|
```json |
|
|
|
{ |
|
|
|
"manager": { |
|
|
|
"module": "@greenlock/manager" |
|
|
|
}, |
|
|
|
"configDir": "./greenlock.d" |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
</details> |
|
|
|
|
|
|
|
```bash |
|
|
|
# show the global defaults |
|
|
|
npx greenlock defaults |
|
|
|
``` |
|
|
|
<summary>app.js</summary> |
|
|
|
|
|
|
|
```js |
|
|
|
var defaults = await greenlock.defaults(); |
|
|
|
``` |
|
|
|
"use strict"; |
|
|
|
|
|
|
|
<details> |
|
|
|
<summary>Example Output</summary> |
|
|
|
// Here's a vanilla HTTP app to start, |
|
|
|
// but feel free to replace it with Express, Koa, etc |
|
|
|
var app = function(req, res) { |
|
|
|
res.end("Hello, Encrypted World!"); |
|
|
|
}; |
|
|
|
|
|
|
|
```json |
|
|
|
{ |
|
|
|
"store": { |
|
|
|
"module": "greenlock-store-fs", |
|
|
|
"basePath": "./greenlock.d" |
|
|
|
}, |
|
|
|
"challenges": { |
|
|
|
"http-01": { |
|
|
|
"module": "acme-http-01-standalone" |
|
|
|
} |
|
|
|
}, |
|
|
|
"renewOffset": "-45d", |
|
|
|
"renewStagger": "3d", |
|
|
|
"accountKeyType": "EC-P256", |
|
|
|
"serverKeyType": "RSA-2048", |
|
|
|
"subscriberEmail": "jon@example.com", |
|
|
|
"agreeToTerms": true |
|
|
|
} |
|
|
|
module.exports = app; |
|
|
|
``` |
|
|
|
|
|
|
|
</details> |
|
|
|
|
|
|
|
```bash |
|
|
|
# show per-site configs |
|
|
|
npx greenlock config --subject example.com |
|
|
|
``` |
|
|
|
|
|
|
|
```js |
|
|
|
greenlock.sites.get({ subject: "example.com" }); |
|
|
|
npx greenlock add --subject example.com --altnames example.com |
|
|
|
``` |
|
|
|
|
|
|
|
<details> |
|
|
|
<summary>Example Output</summary> |
|
|
|
<summary>greenlock.d/config.json</summary> |
|
|
|
|
|
|
|
<!-- TODO update manager to write array rather than object --> |
|
|
|
|
|
|
|
```json |
|
|
|
{ |
|
|
|
"subject": "example.com", |
|
|
|
"altnames": ["example.com"], |
|
|
|
"renewAt": 1576638107754, |
|
|
|
"defaults": { |
|
|
|
"store": { |
|
|
|
"module": "greenlock-store-fs", |
|
|
|
"basePath": "./greenlock.d" |
|
|
|
}, |
|
|
|
"challenges": { |
|
|
|
"http-01": { |
|
|
|
"module": "acme-http-01-standalone" |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
{ "sites": [{ "subject": "example.com", "altnames": ["example.com"] }] } |
|
|
|
``` |
|
|
|
|
|
|
|
</details> |
|
|
|
|
|
|
|
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 |
|
|
|
# Set a global subscriber account |
|
|
|
npx greenlock defaults --subscriber-email 'mycompany@example.com' --agree-to-terms true |
|
|
|
``` |
|
|
|
|
|
|
|
```js |
|
|
|
greenlock.manager.defaults({ |
|
|
|
subscriberEmail: "mycompany@example.com", |
|
|
|
agreeToTerms: true |
|
|
|
}); |
|
|
|
npm start -- --staging |
|
|
|
``` |
|
|
|
|
|
|
|
<!-- todo print where the key was saved --> |
|
|
|
|
|
|
|
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 |
|
|
|
``` |
|
|
|
```txt |
|
|
|
> my-project@1.0.0 start /srv/www/my-project |
|
|
|
> node server.js |
|
|
|
|
|
|
|
```js |
|
|
|
greenlock.sites.add({ |
|
|
|
subject: "example.com", |
|
|
|
altnames: ["example.com"] |
|
|
|
}); |
|
|
|
Listening on 0.0.0.0:80 for ACME challenges and HTTPS redirects |
|
|
|
Listening on 0.0.0.0:443 for secure traffic |
|
|
|
``` |
|
|
|
|
|
|
|
<!-- todo print where the cert was saved --> |
|
|
|
|
|
|
|
Note: **Localhost**, **Wildcard**, and Certificates for Private Networks require |
|
|
|
[**DNS validation**](https://git.rootprojects.org/root/greenlock-exp). |
|
|
|
|
|
|
|
- DNS Validation |
|
|
|
- [**Wildcards**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/wildcards/) (coming soon) |
|
|
|
- [**Localhost**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/localhost/) (coming soon) |
|
|
|
- [**CI/CD**](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/ci-cd/) (coming soon) |
|
|
|
## Walkthrough |
|
|
|
|
|
|
|
</details> |
|
|
|
Read the [WALKTHROUGH](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/WALKTHROUGH.md) |
|
|
|
|
|
|
|
# Plenty of Examples |
|
|
|
# Examples |
|
|
|
|
|
|
|
- [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/) |
|
|
@ -411,6 +197,22 @@ Note: **Localhost**, **Wildcard**, and Certificates for Private Networks require |
|
|
|
- [**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/) |
|
|
|
|
|
|
|
# Using a Database, S3, etc |
|
|
|
|
|
|
|
If you have a small site, the default file storage will work well for you. |
|
|
|
|
|
|
|
If you have many sites with many users, you'll probably want to store config in a database of some sort. |
|
|
|
|
|
|
|
See the section on **Custom** callbacks and plugins below. |
|
|
|
|
|
|
|
# Advanced Configuration |
|
|
|
|
|
|
|
All of the advanced configuration is done by replacing the default behavior with callbacks. |
|
|
|
|
|
|
|
You can whip up your own, or you can use something that's published to npm. |
|
|
|
|
|
|
|
See the section on **Custom** callbacks and plugins below. |
|
|
|
|
|
|
|
# Easy to Customize |
|
|
|
|
|
|
|
<!-- greenlock-manager-test => greenlock-manager-custom --> |
|
|
|