make Prettier
This commit is contained in:
parent
a49ccb7398
commit
c73ad565a3
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"printWidth": 120,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"useTabs": true
|
||||||
|
}
|
381
README.md
381
README.md
|
@ -37,30 +37,30 @@ and **node.js middleware systems**.
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
- [x] Automatic HTTPS
|
- [x] Automatic HTTPS
|
||||||
- [x] Free SSL
|
- [x] Free SSL
|
||||||
- [x] Free Wildcard SSL
|
- [x] Free Wildcard SSL
|
||||||
- [x] Multiple domain support (up to 100 altnames per SAN)
|
- [x] Multiple domain support (up to 100 altnames per SAN)
|
||||||
- [x] Dynamic Virtual Hosting (vhost)
|
- [x] Dynamic Virtual Hosting (vhost)
|
||||||
- [x] Automatical renewal (10 to 14 days before expiration)
|
- [x] Automatical renewal (10 to 14 days before expiration)
|
||||||
- [x] Great ACME support
|
- [x] Great ACME support
|
||||||
- [x] ACME draft 11
|
- [x] ACME draft 11
|
||||||
- [x] Let's Encrypt v2
|
- [x] Let's Encrypt v2
|
||||||
- [x] Let's Encrypt v1
|
- [x] Let's Encrypt v1
|
||||||
- [x] Full node.js support
|
- [x] Full node.js support
|
||||||
- [x] node v6+
|
- [x] node v6+
|
||||||
- [x] core https module
|
- [x] core https module
|
||||||
- [x] Express.js
|
- [x] Express.js
|
||||||
- [x] [Koa](https://git.rootprojects.org/root/greenlock-koa.js)
|
- [x] [Koa](https://git.rootprojects.org/root/greenlock-koa.js)
|
||||||
- [x] [hapi](https://git.rootprojects.org/root/greenlock-hapi.js)
|
- [x] [hapi](https://git.rootprojects.org/root/greenlock-hapi.js)
|
||||||
- [x] Extensible Plugin Support
|
- [x] Extensible Plugin Support
|
||||||
- [x] AWS (S3, Route53)
|
- [x] AWS (S3, Route53)
|
||||||
- [x] Azure
|
- [x] Azure
|
||||||
- [x] CloudFlare
|
- [x] CloudFlare
|
||||||
- [x] Consul
|
- [x] Consul
|
||||||
- [x] Digital Ocean
|
- [x] Digital Ocean
|
||||||
- [x] etcd
|
- [x] etcd
|
||||||
- [x] Redis
|
- [x] Redis
|
||||||
|
|
||||||
# Install
|
# Install
|
||||||
|
|
||||||
|
@ -78,18 +78,18 @@ Watch the QuickStart demonstration: [https://youtu.be/e8vaR4CEZ5s](https://youtu
|
||||||
|
|
||||||
<a href="https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk"><img src="https://i.imgur.com/Y8ix6Ts.png" title="QuickStart Video" alt="YouTube Video Preview" /></a>
|
<a href="https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk"><img src="https://i.imgur.com/Y8ix6Ts.png" title="QuickStart Video" alt="YouTube Video Preview" /></a>
|
||||||
|
|
||||||
* [0:00](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk#t=0) - Intro
|
- [0:00](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk#t=0) - Intro
|
||||||
* [2:22](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk#t=142) - Demonstrating QuickStart Example
|
- [2:22](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk#t=142) - Demonstrating QuickStart Example
|
||||||
* [6:37](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk?t=397) - Troubleshooting / Gotchas
|
- [6:37](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk?t=397) - Troubleshooting / Gotchas
|
||||||
|
|
||||||
#### Beyond the QuickStart (Part 2)
|
#### Beyond the QuickStart (Part 2)
|
||||||
|
|
||||||
* [1:00](https://www.youtube.com/watch?v=bTEn93gxY50&index=2&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk&t=60) - Bringing Greenlock into an Existing Express Project
|
- [1:00](https://www.youtube.com/watch?v=bTEn93gxY50&index=2&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk&t=60) - Bringing Greenlock into an Existing Express Project
|
||||||
* [2:26](https://www.youtube.com/watch?v=bTEn93gxY50&index=2&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk&t=146) - The `approveDomains` callback
|
- [2:26](https://www.youtube.com/watch?v=bTEn93gxY50&index=2&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk&t=146) - The `approveDomains` callback
|
||||||
|
|
||||||
#### Security Concerns (Part 3)
|
#### Security Concerns (Part 3)
|
||||||
|
|
||||||
* [0:00](https://www.youtube.com/watch?v=aZgVqPzoZTY&index=3&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk) - Potential Attacks, and Mitigation
|
- [0:00](https://www.youtube.com/watch?v=aZgVqPzoZTY&index=3&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk) - Potential Attacks, and Mitigation
|
||||||
|
|
||||||
### Working Example Code
|
### Working Example Code
|
||||||
|
|
||||||
|
@ -110,35 +110,39 @@ node greenlock-express.js/examples/simple.js
|
||||||
All you have to do is start the webserver and then visit it at its domain name.
|
All you have to do is start the webserver and then visit it at its domain name.
|
||||||
|
|
||||||
`server.js`:
|
`server.js`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
require('greenlock-express').create({
|
require("greenlock-express")
|
||||||
email: 'john.doe@example.com' // The email address of the ACME user / hosting provider
|
.create({
|
||||||
, agreeTos: true // You must accept the ToS as the host which handles the certs
|
email: "john.doe@example.com", // The email address of the ACME user / hosting provider
|
||||||
, configDir: '~/.config/acme/' // Writable directory where certs will be saved
|
agreeTos: true, // You must accept the ToS as the host which handles the certs
|
||||||
, communityMember: true // Join the community to get notified of important updates
|
configDir: "~/.config/acme/", // Writable directory where certs will be saved
|
||||||
, telemetry: true // Contribute telemetry data to the project
|
communityMember: true, // Join the community to get notified of important updates
|
||||||
|
telemetry: true, // Contribute telemetry data to the project
|
||||||
|
|
||||||
// Using your express app:
|
// Using your express app:
|
||||||
// simply export it as-is, then include it here
|
// simply export it as-is, then include it here
|
||||||
, app: require('./app.js')
|
app: require("./app.js")
|
||||||
|
|
||||||
//, debug: true
|
//, debug: true
|
||||||
}).listen(80, 443);
|
})
|
||||||
|
.listen(80, 443);
|
||||||
```
|
```
|
||||||
|
|
||||||
`app.js`:
|
`app.js`:
|
||||||
```js
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var express = require('express');
|
```js
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var express = require("express");
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
app.use('/', function (req, res) {
|
app.use("/", function(req, res) {
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8')
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||||
res.end('Hello, World!\n\n💚 🔒.js');
|
res.end("Hello, World!\n\n💚 🔒.js");
|
||||||
})
|
});
|
||||||
|
|
||||||
// Don't do this:
|
// Don't do this:
|
||||||
// app.listen(3000)
|
// app.listen(3000)
|
||||||
|
@ -166,29 +170,29 @@ You can see our full privacy policy at <https://greenlock.domains/legal/#privacy
|
||||||
|
|
||||||
Double check the following:
|
Double check the following:
|
||||||
|
|
||||||
* **Public Facing IP** for `http-01` challenges
|
- **Public Facing IP** for `http-01` challenges
|
||||||
* Are you running this *as* a public-facing webserver (good)? or localhost (bad)?
|
- Are you running this _as_ a public-facing webserver (good)? or localhost (bad)?
|
||||||
* Does `ifconfig` show a public address (good)? or a private one - 10.x, 192.168.x, etc (bad)?
|
- Does `ifconfig` show a public address (good)? or a private one - 10.x, 192.168.x, etc (bad)?
|
||||||
* If you're on a non-public server, are you using the `dns-01` challenge?
|
- If you're on a non-public server, are you using the `dns-01` challenge?
|
||||||
* **correct ACME version**
|
- **correct ACME version**
|
||||||
* Let's Encrypt **v2** (ACME v2) must use `version: 'draft-11'`
|
- Let's Encrypt **v2** (ACME v2) must use `version: 'draft-11'`
|
||||||
* Let's Encrypt v1 must use `version: 'v01'`
|
- Let's Encrypt v1 must use `version: 'v01'`
|
||||||
* **valid email**
|
- **valid email**
|
||||||
* You MUST set `email` to a **valid address**
|
- You MUST set `email` to a **valid address**
|
||||||
* MX records must validate (`dig MX example.com` for `'john@example.com'`)
|
- MX records must validate (`dig MX example.com` for `'john@example.com'`)
|
||||||
* **valid DNS records**
|
- **valid DNS records**
|
||||||
* Must have public DNS records (test with `dig +trace A example.com; dig +trace www.example.com` for `[ 'example.com', 'www.example.com' ]`)
|
- Must have public DNS records (test with `dig +trace A example.com; dig +trace www.example.com` for `[ 'example.com', 'www.example.com' ]`)
|
||||||
* **write access**
|
- **write access**
|
||||||
* You MUST set `configDir` to a writeable location (test with `touch ~/acme/etc/tmp.tmp`)
|
- You MUST set `configDir` to a writeable location (test with `touch ~/acme/etc/tmp.tmp`)
|
||||||
* **port binding privileges**
|
- **port binding privileges**
|
||||||
* You MUST be able to bind to ports 80 and 443
|
- You MUST be able to bind to ports 80 and 443
|
||||||
* You can do this via `sudo` or [`setcap`](https://gist.github.com/firstdoit/6389682)
|
- You can do this via `sudo` or [`setcap`](https://gist.github.com/firstdoit/6389682)
|
||||||
* **API limits**
|
- **API limits**
|
||||||
* You MUST NOT exceed the API [**usage limits**](https://letsencrypt.org/docs/staging-environment/) per domain, certificate, IP address, etc
|
- You MUST NOT exceed the API [**usage limits**](https://letsencrypt.org/docs/staging-environment/) per domain, certificate, IP address, etc
|
||||||
* **Red Lock, Untrusted**
|
- **Red Lock, Untrusted**
|
||||||
* You MUST use the **production** server url, not staging
|
- You MUST use the **production** server url, not staging
|
||||||
* The API URL should not have 'acme-staging-v02', but should have 'acme-v02'
|
- The API URL should not have 'acme-staging-v02', but should have 'acme-v02'
|
||||||
* Delete the `configDir` used for getting certificates in staging
|
- Delete the `configDir` used for getting certificates in staging
|
||||||
|
|
||||||
### Production vs Staging
|
### Production vs Staging
|
||||||
|
|
||||||
|
@ -210,19 +214,19 @@ https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
|
|
||||||
## Working Examples
|
## Working Examples
|
||||||
|
|
||||||
| Example | Location + Description |
|
| Example | Location + Description |
|
||||||
|:---------------:|:---------:|
|
| :-------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
||||||
| **QuickStart** | [examples/quickstart.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/quickstart.js) uses the fewest options and accepts all default settings. It's guaranteed to work for you. |
|
| **QuickStart** | [examples/quickstart.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/quickstart.js) uses the fewest options and accepts all default settings. It's guaranteed to work for you. |
|
||||||
| Production | [examples/production.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/production.js) shows how to require an express app (or other middleware system), expand the `approveDomains` callback, provides an example database shim, and exposes the server instance. |
|
| Production | [examples/production.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/production.js) shows how to require an express app (or other middleware system), expand the `approveDomains` callback, provides an example database shim, and exposes the server instance. |
|
||||||
| Virtual Hosting | [examples/vhost.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/vhost.js) shows how to dynamically secure and serve domains based on their existance on the file system. |
|
| Virtual Hosting | [examples/vhost.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/vhost.js) shows how to dynamically secure and serve domains based on their existance on the file system. |
|
||||||
| Wildcard Domains | [examples/wildcard.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/wildcard.js) shows how to use the `acme-dns-01-cli` and wildcard cetificates. |
|
| Wildcard Domains | [examples/wildcard.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/wildcard.js) shows how to use the `acme-dns-01-cli` and wildcard cetificates. |
|
||||||
| HTTPS (raw) | [examples/spdy.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/spdy.js) demonstrates how to manually configure a node web server using the node's built-in `http` and `https` modules. |
|
| HTTPS (raw) | [examples/spdy.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/spdy.js) demonstrates how to manually configure a node web server using the node's built-in `http` and `https` modules. |
|
||||||
| HTTP2 (spdy) | Presently spdy is incompatible with node v11, but [examples/spdy.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/spdy.js) demonstrates how to manually configure a node web server with spdy-compatible versions of node and Greenlock. |
|
| HTTP2 (spdy) | Presently spdy is incompatible with node v11, but [examples/spdy.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/spdy.js) demonstrates how to manually configure a node web server with spdy-compatible versions of node and Greenlock. |
|
||||||
| HTTP2 (node) | [examples/http2.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/http2.js) uses node's new HTTP2 module, which is NOT compatible with the existing middleware systems (and is not "stable" as of v10.0). |
|
| HTTP2 (node) | [examples/http2.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/http2.js) uses node's new HTTP2 module, which is NOT compatible with the existing middleware systems (and is not "stable" as of v10.0). |
|
||||||
| WebSockets (ws) | [examples/websockets.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/websockets.js) demonstrates how to use Greenlock express with a websocket server. |
|
| WebSockets (ws) | [examples/websockets.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/websockets.js) demonstrates how to use Greenlock express with a websocket server. |
|
||||||
| socket.io | [examples/socket.io.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/socket.io.js) demonstrates how to use Greenlock express with socket.io (even though `ws` is far simpler, faster, and better and every way). |
|
| socket.io | [examples/socket.io.js](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples/socket.io.js) demonstrates how to use Greenlock express with socket.io (even though `ws` is far simpler, faster, and better and every way). |
|
||||||
| - | Build Your Own <br> Be sure to tell me ([@solderjs](https://twitter.com/@solderjs)) / us ([@GreenlockHTTPS](https://twitter.com/@GreenlockHTTPS)) about it. :) |
|
| - | Build Your Own <br> Be sure to tell me ([@solderjs](https://twitter.com/@solderjs)) / us ([@GreenlockHTTPS](https://twitter.com/@GreenlockHTTPS)) about it. :) |
|
||||||
| Full List | Check out the [examples/](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples) directory |
|
| Full List | Check out the [examples/](https://git.rootprojects.org/root/greenlock-express.js/src/branch/master/examples) directory |
|
||||||
|
|
||||||
# Plugins
|
# Plugins
|
||||||
|
|
||||||
|
@ -230,51 +234,49 @@ https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
|
|
||||||
## HTTP-01 Challenges
|
## HTTP-01 Challenges
|
||||||
|
|
||||||
| | Plugin |
|
| | Plugin |
|
||||||
|:--------------:|:---------:|
|
| :--------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
||||||
| **Default (fs)** | [acme-http-01-fs](https://git.rootprojects.org/root/acme-http-01-webroot.js) |
|
| **Default (fs)** | [acme-http-01-fs](https://git.rootprojects.org/root/acme-http-01-webroot.js) |
|
||||||
| **Manual (cli)** | [acme-http-01-cli](https://git.rootprojects.org/root/acme-http-01-cli.js) |
|
| **Manual (cli)** | [acme-http-01-cli](https://git.rootprojects.org/root/acme-http-01-cli.js) |
|
||||||
| AWS S3 | [acme-http-01-s3](https://git.rootprojects.org/root/acme-http-01-s3.js) |
|
| AWS S3 | [acme-http-01-s3](https://git.rootprojects.org/root/acme-http-01-s3.js) |
|
||||||
| Azure | [kolarcz/node-le-challenge-azure-storage](https://github.com/kolarcz/node-le-challenge-azure-storage) |
|
| Azure | [kolarcz/node-le-challenge-azure-storage](https://github.com/kolarcz/node-le-challenge-azure-storage) |
|
||||||
| - | Build Your Own <br> [acme-http-01-challenge-test](https://git.rootprojects.org/root/acme-challenge-test.js) |
|
| - | Build Your Own <br> [acme-http-01-challenge-test](https://git.rootprojects.org/root/acme-challenge-test.js) |
|
||||||
| Full List | Search [acme-http-01-](https://www.npmjs.com/search?q=acme-http-01-) on npm (or [le-challenge-](https://www.npmjs.com/search?q=le-challenge-) for older versions) |
|
| Full List | Search [acme-http-01-](https://www.npmjs.com/search?q=acme-http-01-) on npm (or [le-challenge-](https://www.npmjs.com/search?q=le-challenge-) for older versions) |
|
||||||
|
|
||||||
|
|
||||||
## DNS-01 Challenges
|
## DNS-01 Challenges
|
||||||
|
|
||||||
| | Plugin |
|
| | Plugin |
|
||||||
|:--------------:|:---------:|
|
| :--------------: | :----------------------------------------------------------------------------------------------------------------------------------------: |
|
||||||
| **Manual (cli)** | [acme-dns-01-cli](https://git.rootprojects.org/root/acme-dns-01-cli.js) |
|
| **Manual (cli)** | [acme-dns-01-cli](https://git.rootprojects.org/root/acme-dns-01-cli.js) |
|
||||||
| AWS Route 53 | [thadeetrompetter/le-challenge-route53](https://github.com/thadeetrompetter/le-challenge-route53) |
|
| AWS Route 53 | [thadeetrompetter/le-challenge-route53](https://github.com/thadeetrompetter/le-challenge-route53) |
|
||||||
| CloudFlare | [buschtoens/le-challenge-cloudflare](https://github.com/buschtoens/le-challenge-cloudflare) |
|
| CloudFlare | [buschtoens/le-challenge-cloudflare](https://github.com/buschtoens/le-challenge-cloudflare) |
|
||||||
| CloudFlare | [llun/le-challenge-cloudflare](https://github.com/llun/le-challenge-cloudflare) |
|
| CloudFlare | [llun/le-challenge-cloudflare](https://github.com/llun/le-challenge-cloudflare) |
|
||||||
| Digital Ocean | [bmv437/le-challenge-digitalocean](https://github.com/bmv437/le-challenge-digitalocean) |
|
| Digital Ocean | [bmv437/le-challenge-digitalocean](https://github.com/bmv437/le-challenge-digitalocean) |
|
||||||
| etcd | [ceecko/le-challenge-etcd](https://github.com/ceecko/le-challenge-etcd) |
|
| etcd | [ceecko/le-challenge-etcd](https://github.com/ceecko/le-challenge-etcd) |
|
||||||
| - | Build Your Own <br> [acme-challenge-test](https://git.rootprojects.org/root/acme-challenge-test.js) |
|
| - | Build Your Own <br> [acme-challenge-test](https://git.rootprojects.org/root/acme-challenge-test.js) |
|
||||||
| Full List | Search [acme-dns-01-](https://www.npmjs.com/search?q=acme-dns-01-) or [le-challenge-](https://www.npmjs.com/search?q=le-challenge-) on npm |
|
| Full List | Search [acme-dns-01-](https://www.npmjs.com/search?q=acme-dns-01-) or [le-challenge-](https://www.npmjs.com/search?q=le-challenge-) on npm |
|
||||||
|
|
||||||
## Account & Certificate Storage
|
## Account & Certificate Storage
|
||||||
|
|
||||||
| | Plugin |
|
| | Plugin |
|
||||||
|:--------------:|:---------:|
|
| :------------------: | :---------------------------------------------------------------------------------------------------: |
|
||||||
| **Simplest** | [greenlock-store-fs](https://git.rootprojects.org/root/greenlock-store-fs.js) |
|
| **Simplest** | [greenlock-store-fs](https://git.rootprojects.org/root/greenlock-store-fs.js) |
|
||||||
| certbot (v2 default) | [le-store-certbot](https://git.coolaj86.com/coolaj86/le-store-certbot.js) |
|
| certbot (v2 default) | [le-store-certbot](https://git.coolaj86.com/coolaj86/le-store-certbot.js) |
|
||||||
| AWS S3 | [gl-store-s3](https://git.rootprojects.org/root/gl-store-s3.js) |
|
| AWS S3 | [gl-store-s3](https://git.rootprojects.org/root/gl-store-s3.js) |
|
||||||
| Consul | [sebastian-software/le-store-consul](https://github.com/sebastian-software/le-store-consul) |
|
| Consul | [sebastian-software/le-store-consul](https://github.com/sebastian-software/le-store-consul) |
|
||||||
| json (fs) | [paulgrove/le-store-simple-fs](https://github.com/paulgrove/le-store-simple-fs)
|
| json (fs) | [paulgrove/le-store-simple-fs](https://github.com/paulgrove/le-store-simple-fs) |
|
||||||
| Redis | [digitalbazaar/le-store-redis](https://github.com/digitalbazaar/le-store-redis) |
|
| Redis | [digitalbazaar/le-store-redis](https://github.com/digitalbazaar/le-store-redis) |
|
||||||
| - | Build Your Own <br> [greenlock-store-test](https://git.rootprojects.org/root/greenlock-store-test.js) |
|
| - | Build Your Own <br> [greenlock-store-test](https://git.rootprojects.org/root/greenlock-store-test.js) |
|
||||||
| Full List | Search [le-store-](https://www.npmjs.com/search?q=le-store-) on npm |
|
| Full List | Search [le-store-](https://www.npmjs.com/search?q=le-store-) on npm |
|
||||||
|
|
||||||
## Auto-SNI
|
## Auto-SNI
|
||||||
|
|
||||||
| | Plugin |
|
| | Plugin |
|
||||||
|:-----------:|:---------:|
|
| :---------: | :-------------------------------------------------------------: |
|
||||||
| **Default** | [le-sni-auto](https://git.coolaj86.com/coolaj86/le-sni-auto.js) |
|
| **Default** | [le-sni-auto](https://git.coolaj86.com/coolaj86/le-sni-auto.js) |
|
||||||
|
|
||||||
(you probably wouldn't need or want to replace this)
|
(you probably wouldn't need or want to replace this)
|
||||||
|
|
||||||
|
|
||||||
**Bugs**: Please report bugs with the community plugins to the appropriate owner first, then here if you don't get a response.
|
**Bugs**: Please report bugs with the community plugins to the appropriate owner first, then here if you don't get a response.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
@ -300,34 +302,35 @@ node greenlock-express.js/examples/normal.js
|
||||||
It looks a little more like this:
|
It looks a little more like this:
|
||||||
|
|
||||||
`serve.js`:
|
`serve.js`:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
// returns an instance of greenlock.js with additional helper methods
|
// returns an instance of greenlock.js with additional helper methods
|
||||||
var glx = require('greenlock-express').create({
|
var glx = require("greenlock-express").create({
|
||||||
server: 'https://acme-v02.api.letsencrypt.org/directory'
|
server: "https://acme-v02.api.letsencrypt.org/directory",
|
||||||
// Note: If at first you don't succeed, stop and switch to staging:
|
// Note: If at first you don't succeed, stop and switch to staging:
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
// https://acme-staging-v02.api.letsencrypt.org/directory
|
||||||
, version: 'draft-11' // Let's Encrypt v2 (ACME v2)
|
version: "draft-11", // Let's Encrypt v2 (ACME v2)
|
||||||
|
|
||||||
// If you wish to replace the default account and domain key storage plugin
|
// If you wish to replace the default account and domain key storage plugin
|
||||||
, store: require('le-store-certbot').create({
|
store: require("le-store-certbot").create({
|
||||||
configDir: require('path').join(require('os').homedir(), 'acme', 'etc')
|
configDir: require("path").join(require("os").homedir(), "acme", "etc"),
|
||||||
, webrootPath: '/tmp/acme-challenges'
|
webrootPath: "/tmp/acme-challenges"
|
||||||
})
|
}),
|
||||||
|
|
||||||
// Contribute telemetry data to the project
|
// Contribute telemetry data to the project
|
||||||
, telemetry: true
|
telemetry: true,
|
||||||
|
|
||||||
// the default servername to use when the client doesn't specify
|
// the default servername to use when the client doesn't specify
|
||||||
// (because some IoT devices don't support servername indication)
|
// (because some IoT devices don't support servername indication)
|
||||||
, servername: 'example.com'
|
servername: "example.com",
|
||||||
|
|
||||||
, approveDomains: approveDomains
|
approveDomains: approveDomains
|
||||||
});
|
});
|
||||||
|
|
||||||
var server = glx.listen(80, 443, function () {
|
var server = glx.listen(80, 443, function() {
|
||||||
console.log("Listening on port 80 for ACME challenges and 443 for express app.");
|
console.log("Listening on port 80 for ACME challenges and 443 for express app.");
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -341,53 +344,54 @@ plainServer.on('error', function (err) { ... });
|
||||||
```
|
```
|
||||||
|
|
||||||
The Automatic Certificate Issuance is initiated via SNI (`httpsOptions.SNICallback`).
|
The Automatic Certificate Issuance is initiated via SNI (`httpsOptions.SNICallback`).
|
||||||
For security, domain validation MUST have an approval callback in *production*.
|
For security, domain validation MUST have an approval callback in _production_.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
var http01 = require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges' });
|
var http01 = require("le-challenge-fs").create({ webrootPath: "/tmp/acme-challenges" });
|
||||||
function approveDomains(opts, certs, cb) {
|
function approveDomains(opts, certs, cb) {
|
||||||
// This is where you check your database and associated
|
// This is where you check your database and associated
|
||||||
// email addresses with domains and agreements and such
|
// email addresses with domains and agreements and such
|
||||||
// if (!isAllowed(opts.domains)) { return cb(new Error("not allowed")); }
|
// if (!isAllowed(opts.domains)) { return cb(new Error("not allowed")); }
|
||||||
|
|
||||||
// The domains being approved for the first time are listed in opts.domains
|
// The domains being approved for the first time are listed in opts.domains
|
||||||
// Certs being renewed are listed in certs.altnames (if that's useful)
|
// Certs being renewed are listed in certs.altnames (if that's useful)
|
||||||
|
|
||||||
// Opt-in to submit stats and get important updates
|
// Opt-in to submit stats and get important updates
|
||||||
opts.communityMember = true;
|
opts.communityMember = true;
|
||||||
|
|
||||||
// If you wish to replace the default challenge plugin, you may do so here
|
// If you wish to replace the default challenge plugin, you may do so here
|
||||||
opts.challenges = { 'http-01': http01 };
|
opts.challenges = { "http-01": http01 };
|
||||||
|
|
||||||
opts.email = 'john.doe@example.com';
|
opts.email = "john.doe@example.com";
|
||||||
opts.agreeTos = true;
|
opts.agreeTos = true;
|
||||||
|
|
||||||
// NOTE: you can also change other options such as `challengeType` and `challenge`
|
// NOTE: you can also change other options such as `challengeType` and `challenge`
|
||||||
// opts.challengeType = 'http-01';
|
// opts.challengeType = 'http-01';
|
||||||
// opts.challenge = require('le-challenge-fs').create({});
|
// opts.challenge = require('le-challenge-fs').create({});
|
||||||
|
|
||||||
cb(null, { options: opts, certs: certs });
|
cb(null, { options: opts, certs: certs });
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// handles acme-challenge and redirects to https
|
// handles acme-challenge and redirects to https
|
||||||
require('http').createServer(glx.middleware(require('redirect-https')())).listen(80, function () {
|
require("http")
|
||||||
console.log("Listening for ACME http-01 challenges on", this.address());
|
.createServer(glx.middleware(require("redirect-https")()))
|
||||||
});
|
.listen(80, function() {
|
||||||
|
console.log("Listening for ACME http-01 challenges on", this.address());
|
||||||
|
});
|
||||||
|
|
||||||
|
var app = require("express")();
|
||||||
|
app.use("/", function(req, res) {
|
||||||
var app = require('express')();
|
res.end("Hello, World!");
|
||||||
app.use('/', function (req, res) {
|
|
||||||
res.end('Hello, World!');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// handles your app
|
// handles your app
|
||||||
require('https').createServer(glx.httpsOptions, app).listen(443, function () {
|
require("https")
|
||||||
console.log("Listening for ACME tls-sni-01 challenges and serve app on", this.address());
|
.createServer(glx.httpsOptions, app)
|
||||||
});
|
.listen(443, function() {
|
||||||
|
console.log("Listening for ACME tls-sni-01 challenges and serve app on", this.address());
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
**Security**:
|
**Security**:
|
||||||
|
@ -395,7 +399,6 @@ require('https').createServer(glx.httpsOptions, app).listen(443, function () {
|
||||||
Greenlock will do a self-check on all domain registrations
|
Greenlock will do a self-check on all domain registrations
|
||||||
to prevent you from hitting rate limits.
|
to prevent you from hitting rate limits.
|
||||||
|
|
||||||
|
|
||||||
# API
|
# API
|
||||||
|
|
||||||
This module is an elaborate ruse (to provide an oversimplified example and to nab some SEO).
|
This module is an elaborate ruse (to provide an oversimplified example and to nab some SEO).
|
||||||
|
@ -405,35 +408,35 @@ The API is actually located at [greenlock.js options](https://git.rootprojects.o
|
||||||
|
|
||||||
The only "API" consists of two options, the rest is just a wrapper around `greenlock.js` to take LOC from 15 to 5:
|
The only "API" consists of two options, the rest is just a wrapper around `greenlock.js` to take LOC from 15 to 5:
|
||||||
|
|
||||||
* `opts.app` An express app in the format `function (req, res) { ... }` (no `next`).
|
- `opts.app` An express app in the format `function (req, res) { ... }` (no `next`).
|
||||||
* `server = glx.listen(plainAddr, tlsAddr, onListen)` Accepts port numbers (or arrays of port numbers) to listen on, returns secure server.
|
- `server = glx.listen(plainAddr, tlsAddr, onListen)` Accepts port numbers (or arrays of port numbers) to listen on, returns secure server.
|
||||||
* `listen(80, 443)`
|
- `listen(80, 443)`
|
||||||
* `listen(80, 443, onListenSecure)`
|
- `listen(80, 443, onListenSecure)`
|
||||||
* `listen(80, 443, onListenPlain, onListenSecure)`
|
- `listen(80, 443, onListenPlain, onListenSecure)`
|
||||||
* `listen('localhost:80', '0.0.0.0:443')`
|
- `listen('localhost:80', '0.0.0.0:443')`
|
||||||
* `listen('[::1]:80', '[::]:443')`
|
- `listen('[::1]:80', '[::]:443')`
|
||||||
* `listen('/tmp/glx.plain.sock', '/tmp/glx.secure.sock')`
|
- `listen('/tmp/glx.plain.sock', '/tmp/glx.secure.sock')`
|
||||||
|
|
||||||
Brief overview of some simple options for `greenlock.js`:
|
Brief overview of some simple options for `greenlock.js`:
|
||||||
|
|
||||||
* `opts.server` set to https://acme-v02.api.letsencrypt.org/directory in production
|
- `opts.server` set to https://acme-v02.api.letsencrypt.org/directory in production
|
||||||
* `opts.version` set to `v01` for Let's Encrypt v1 or `draft-11` for Let's Encrypt v2 (mistakenly called ACME v2)
|
- `opts.version` set to `v01` for Let's Encrypt v1 or `draft-11` for Let's Encrypt v2 (mistakenly called ACME v2)
|
||||||
* `opts.email` The default email to use to accept agreements.
|
- `opts.email` The default email to use to accept agreements.
|
||||||
* `opts.agreeTos` When set to `true`, this always accepts the LetsEncrypt TOS. When a string it checks the agreement url first.
|
- `opts.agreeTos` When set to `true`, this always accepts the LetsEncrypt TOS. When a string it checks the agreement url first.
|
||||||
* `opts.communityMember` Join the community to get notified of important updates and help make greenlock better
|
- `opts.communityMember` Join the community to get notified of important updates and help make greenlock better
|
||||||
* `opts.approveDomains` can be either of:
|
- `opts.approveDomains` can be either of:
|
||||||
* An explicit array of allowed domains such as `[ 'example.com', 'www.example.com' ]`
|
- An explicit array of allowed domains such as `[ 'example.com', 'www.example.com' ]`
|
||||||
* A callback `function (opts, certs, cb) { cb(null, { options: opts, certs: certs }); }` for setting `email`, `agreeTos`, `domains`, etc (as shown in usage example above)
|
- A callback `function (opts, certs, cb) { cb(null, { options: opts, certs: certs }); }` for setting `email`, `agreeTos`, `domains`, etc (as shown in usage example above)
|
||||||
* `opts.renewWithin` is the **maximum** number of days (in ms) before expiration to renew a certificate.
|
- `opts.renewWithin` is the **maximum** number of days (in ms) before expiration to renew a certificate.
|
||||||
* `opts.renewBy` is the **minimum** number of days (in ms) before expiration to renew a certificate.
|
- `opts.renewBy` is the **minimum** number of days (in ms) before expiration to renew a certificate.
|
||||||
|
|
||||||
## Supported ACME versions
|
## Supported ACME versions
|
||||||
|
|
||||||
* Let's Encrypt v1 (aka v01)
|
- Let's Encrypt v1 (aka v01)
|
||||||
* Let's Encrypt v2 (aka v02 or ACME draft 11)
|
- Let's Encrypt v2 (aka v02 or ACME draft 11)
|
||||||
* ACME draft 11 (ACME v2 is a misnomer)
|
- ACME draft 11 (ACME v2 is a misnomer)
|
||||||
* Wildcard domains (via dns-01 challenges)
|
- Wildcard domains (via dns-01 challenges)
|
||||||
* `*.example.com`
|
- `*.example.com`
|
||||||
|
|
||||||
<small>tags: letsencrypt acme free ssl automated https node express.js</small>
|
<small>tags: letsencrypt acme free ssl automated https node express.js</small>
|
||||||
|
|
||||||
|
|
12
config.js
12
config.js
|
@ -1,9 +1,9 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
var path = require('path');
|
var path = require("path");
|
||||||
module.exports = {
|
module.exports = {
|
||||||
email: 'jon.doe@example.com'
|
email: "jon.doe@example.com",
|
||||||
, configDir: path.join(__dirname, 'acme')
|
configDir: path.join(__dirname, "acme"),
|
||||||
, srv: '/srv/www/'
|
srv: "/srv/www/",
|
||||||
, api: '/srv/api/'
|
api: "/srv/api/"
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,59 +1,56 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
// npm install spdy@3.x
|
// npm install spdy@3.x
|
||||||
|
|
||||||
//var Greenlock = require('greenlock-express')
|
//var Greenlock = require('greenlock-express')
|
||||||
var Greenlock = require('../');
|
var Greenlock = require("../");
|
||||||
|
|
||||||
var greenlock = Greenlock.create({
|
var greenlock = Greenlock.create({
|
||||||
|
// Let's Encrypt v2 is ACME draft 11
|
||||||
|
version: "draft-11",
|
||||||
|
|
||||||
// Let's Encrypt v2 is ACME draft 11
|
server: "https://acme-v02.api.letsencrypt.org/directory",
|
||||||
version: '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'
|
// You MUST change this to a valid email address
|
||||||
// Note: If at first you don't succeed, stop and switch to staging
|
email: "jon@example.com",
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
|
||||||
|
|
||||||
// You MUST change this to a valid email address
|
// You MUST NOT build clients that accept the ToS without asking the user
|
||||||
, email: 'jon@example.com'
|
agreeTos: true,
|
||||||
|
|
||||||
// You MUST NOT build clients that accept the ToS without asking the user
|
// You MUST change these to valid domains
|
||||||
, agreeTos: true
|
// NOTE: all domains will validated and listed on the certificate
|
||||||
|
approvedDomains: ["example.com", "www.example.com"],
|
||||||
|
|
||||||
// You MUST change these to valid domains
|
// You MUST have access to write to directory where certs are saved
|
||||||
// NOTE: all domains will validated and listed on the certificate
|
// ex: /home/foouser/acme/etc
|
||||||
, approvedDomains: [ 'example.com', 'www.example.com' ]
|
configDir: "~/.config/acme/",
|
||||||
|
|
||||||
// You MUST have access to write to directory where certs are saved
|
// Get notified of important updates and help me make greenlock better
|
||||||
// ex: /home/foouser/acme/etc
|
communityMember: true
|
||||||
, configDir: '~/.config/acme/'
|
|
||||||
|
|
||||||
// Get notified of important updates and help me make greenlock better
|
|
||||||
, communityMember: true
|
|
||||||
|
|
||||||
//, debug: true
|
|
||||||
|
|
||||||
|
//, debug: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// http-01 Challenges //
|
// http-01 Challenges //
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
// http-01 challenge happens over http/1.1, not http2
|
// http-01 challenge happens over http/1.1, not http2
|
||||||
var redirectHttps = require('redirect-https')();
|
var redirectHttps = require("redirect-https")();
|
||||||
var acmeChallengeHandler = greenlock.middleware(function (req, res) {
|
var acmeChallengeHandler = greenlock.middleware(function(req, res) {
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||||
res.end('<h1>Hello, ⚠️ Insecure World!</h1><a>Visit Secure Site</a>'
|
res.end(
|
||||||
+ '<script>document.querySelector("a").href=window.location.href.replace(/^http/i, "https");</script>'
|
"<h1>Hello, ⚠️ Insecure World!</h1><a>Visit Secure Site</a>" +
|
||||||
);
|
'<script>document.querySelector("a").href=window.location.href.replace(/^http/i, "https");</script>'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
require('http').createServer(acmeChallengeHandler).listen(80, function () {
|
require("http")
|
||||||
console.log("Listening for ACME http-01 challenges on", this.address());
|
.createServer(acmeChallengeHandler)
|
||||||
});
|
.listen(80, function() {
|
||||||
|
console.log("Listening for ACME http-01 challenges on", this.address());
|
||||||
|
});
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// http2 via SPDY h2 //
|
// http2 via SPDY h2 //
|
||||||
|
@ -61,15 +58,18 @@ require('http').createServer(acmeChallengeHandler).listen(80, function () {
|
||||||
|
|
||||||
// spdy is a drop-in replacement for the https API
|
// spdy is a drop-in replacement for the https API
|
||||||
var spdyOptions = Object.assign({}, greenlock.tlsOptions);
|
var spdyOptions = Object.assign({}, greenlock.tlsOptions);
|
||||||
spdyOptions.spdy = { protocols: [ 'h2', 'http/1.1' ], plain: false };
|
spdyOptions.spdy = { protocols: ["h2", "http/1.1"], plain: false };
|
||||||
var server = require('spdy').createServer(spdyOptions, require('express')().use('/', function (req, res) {
|
var server = require("spdy").createServer(
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
spdyOptions,
|
||||||
res.end('<h1>Hello, 🔐 Secure World!</h1>');
|
require("express")().use("/", function(req, res) {
|
||||||
}));
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||||
server.on('error', function (err) {
|
res.end("<h1>Hello, 🔐 Secure World!</h1>");
|
||||||
console.error(err);
|
})
|
||||||
|
);
|
||||||
|
server.on("error", function(err) {
|
||||||
|
console.error(err);
|
||||||
});
|
});
|
||||||
server.on('listening', function () {
|
server.on("listening", function() {
|
||||||
console.log("Listening for SPDY/http2/https requests on", this.address());
|
console.log("Listening for SPDY/http2/https requests on", this.address());
|
||||||
});
|
});
|
||||||
server.listen(443);
|
server.listen(443);
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
//require('greenlock-express')
|
//require('greenlock-express')
|
||||||
require('../').create({
|
require("../")
|
||||||
|
.create({
|
||||||
|
// Let's Encrypt v2 is ACME draft 11
|
||||||
|
version: "draft-11",
|
||||||
|
|
||||||
// Let's Encrypt v2 is ACME draft 11
|
server: "https://acme-v02.api.letsencrypt.org/directory",
|
||||||
version: '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'
|
email: "john.doe@example.com",
|
||||||
// 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,
|
||||||
|
|
||||||
, agreeTos: true
|
approvedDomains: ["example.com", "www.example.com"],
|
||||||
|
|
||||||
, approvedDomains: [ 'example.com', 'www.example.com' ]
|
app: require("express")().use("/", function(req, res) {
|
||||||
|
res.end("Hello, World!");
|
||||||
|
}),
|
||||||
|
|
||||||
, app: require('express')().use('/', function (req, res) {
|
renewWithin: 91 * 24 * 60 * 60 * 1000,
|
||||||
res.end('Hello, World!');
|
renewBy: 90 * 24 * 60 * 60 * 1000,
|
||||||
})
|
|
||||||
|
|
||||||
, renewWithin: (91 * 24 * 60 * 60 * 1000)
|
// Get notified of important updates and help me make greenlock better
|
||||||
, renewBy: (90 * 24 * 60 * 60 * 1000)
|
communityMember: true,
|
||||||
|
debug: true
|
||||||
// Get notified of important updates and help me make greenlock better
|
})
|
||||||
, communityMember: true
|
.listen(80, 443);
|
||||||
, debug: true
|
|
||||||
}).listen(80, 443);
|
|
||||||
|
|
|
@ -1,74 +1,70 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
//var Greenlock = require('greenlock-express')
|
//var Greenlock = require('greenlock-express')
|
||||||
var Greenlock = require('../');
|
var Greenlock = require("../");
|
||||||
|
|
||||||
var greenlock = Greenlock.create({
|
var greenlock = Greenlock.create({
|
||||||
|
// Let's Encrypt v2 is ACME draft 11
|
||||||
|
version: "draft-11",
|
||||||
|
|
||||||
// Let's Encrypt v2 is ACME draft 11
|
server: "https://acme-v02.api.letsencrypt.org/directory",
|
||||||
version: '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'
|
// You MUST change this to a valid email address
|
||||||
// Note: If at first you don't succeed, stop and switch to staging
|
email: "jon@example.com",
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
|
||||||
|
|
||||||
// You MUST change this to a valid email address
|
// You MUST NOT build clients that accept the ToS without asking the user
|
||||||
, email: 'jon@example.com'
|
agreeTos: true,
|
||||||
|
|
||||||
// You MUST NOT build clients that accept the ToS without asking the user
|
// You MUST change these to valid domains
|
||||||
, agreeTos: true
|
// NOTE: all domains will validated and listed on the certificate
|
||||||
|
approvedDomains: ["example.com", "www.example.com"],
|
||||||
|
|
||||||
// You MUST change these to valid domains
|
// You MUST have access to write to directory where certs are saved
|
||||||
// NOTE: all domains will validated and listed on the certificate
|
// ex: /home/foouser/acme/etc
|
||||||
, approvedDomains: [ 'example.com', 'www.example.com' ]
|
configDir: "~/.config/acme/",
|
||||||
|
|
||||||
// You MUST have access to write to directory where certs are saved
|
// Get notified of important updates and help me make greenlock better
|
||||||
// ex: /home/foouser/acme/etc
|
communityMember: true
|
||||||
, configDir: '~/.config/acme/'
|
|
||||||
|
|
||||||
// Get notified of important updates and help me make greenlock better
|
|
||||||
, communityMember: true
|
|
||||||
|
|
||||||
//, debug: true
|
|
||||||
|
|
||||||
|
//, debug: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// http-01 Challenges //
|
// http-01 Challenges //
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
// http-01 challenge happens over http/1.1, not http2
|
// http-01 challenge happens over http/1.1, not http2
|
||||||
var redirectHttps = require('redirect-https')();
|
var redirectHttps = require("redirect-https")();
|
||||||
var acmeChallengeHandler = greenlock.middleware(redirectHttps);
|
var acmeChallengeHandler = greenlock.middleware(redirectHttps);
|
||||||
require('http').createServer(acmeChallengeHandler).listen(80, function () {
|
require("http")
|
||||||
console.log("Listening for ACME http-01 challenges on", this.address());
|
.createServer(acmeChallengeHandler)
|
||||||
});
|
.listen(80, function() {
|
||||||
|
console.log("Listening for ACME http-01 challenges on", this.address());
|
||||||
|
});
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// node.js' http2 api //
|
// node.js' http2 api //
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
// http2 is a new API with which you would use hapi or koa, not express
|
// http2 is a new API with which you would use hapi or koa, not express
|
||||||
var server = require('http2').createSecureServer(greenlock.tlsOptions);
|
var server = require("http2").createSecureServer(greenlock.tlsOptions);
|
||||||
server.on('error', function (err) {
|
server.on("error", function(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
// WARNING: Because the middleware don't handle this API style,
|
// WARNING: Because the middleware don't handle this API style,
|
||||||
// the Host headers are unmodified and potentially dangerous
|
// the Host headers are unmodified and potentially dangerous
|
||||||
// (ex: Host: Robert'); DROP TABLE Students;)
|
// (ex: Host: Robert'); DROP TABLE Students;)
|
||||||
server.on('stream', function (stream, headers) {
|
server.on("stream", function(stream, headers) {
|
||||||
console.log(headers);
|
console.log(headers);
|
||||||
stream.respond({
|
stream.respond({
|
||||||
'content-type': 'text/html'
|
"content-type": "text/html",
|
||||||
, ':status': 200
|
":status": 200
|
||||||
});
|
});
|
||||||
stream.end('Hello, HTTP2 World!');
|
stream.end("Hello, HTTP2 World!");
|
||||||
});
|
});
|
||||||
server.on('listening', function () {
|
server.on("listening", function() {
|
||||||
console.log("Listening for http2 requests on", this.address());
|
console.log("Listening for http2 requests on", this.address());
|
||||||
});
|
});
|
||||||
server.listen(443);
|
server.listen(443);
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
var express = require('express');
|
var express = require("express");
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
app.use('/', function (req, res) {
|
app.use("/", function(req, res) {
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||||
res.end('Hello, World!\n\n💚 🔒.js');
|
res.end("Hello, World!\n\n💚 🔒.js");
|
||||||
});
|
});
|
||||||
|
|
||||||
// DO NOT DO app.listen() unless we're testing this directly
|
// DO NOT DO app.listen() unless we're testing this directly
|
||||||
if (require.main === module) { app.listen(3000); }
|
if (require.main === module) {
|
||||||
|
app.listen(3000);
|
||||||
|
}
|
||||||
|
|
||||||
// Instead do export the app:
|
// Instead do export the app:
|
||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
|
|
@ -1,90 +1,88 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
//
|
//
|
||||||
// My Secure Server
|
// My Secure Server
|
||||||
//
|
//
|
||||||
//var greenlock = require('greenlock-express')
|
//var greenlock = require('greenlock-express')
|
||||||
var greenlock = require('../').create({
|
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/",
|
||||||
|
|
||||||
// Let's Encrypt v2 is ACME draft 11
|
// The previous 'simple' example set these values statically,
|
||||||
// Note: If at first you don't succeed, stop and switch to staging
|
// but this example uses approveDomains() to set them dynamically
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
//, email: 'none@see.note.above'
|
||||||
server: 'https://acme-v02.api.letsencrypt.org/directory'
|
//, agreeTos: false
|
||||||
, version: 'draft-11'
|
|
||||||
// You MUST have write access to save certs
|
|
||||||
, configDir: '~/.config/acme/'
|
|
||||||
|
|
||||||
// The previous 'simple' example set these values statically,
|
// approveDomains is the right place to check a database for
|
||||||
// but this example uses approveDomains() to set them dynamically
|
// email addresses with domains and agreements and such
|
||||||
//, email: 'none@see.note.above'
|
approveDomains: approveDomains,
|
||||||
//, agreeTos: false
|
|
||||||
|
|
||||||
// approveDomains is the right place to check a database for
|
app: require("./my-express-app.js"),
|
||||||
// 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
|
||||||
// Get notified of important updates and help me make greenlock better
|
|
||||||
, communityMember: true
|
|
||||||
|
|
||||||
//, debug: true
|
|
||||||
|
|
||||||
|
//, debug: true
|
||||||
});
|
});
|
||||||
|
|
||||||
var server = greenlock.listen(80, 443);
|
var server = greenlock.listen(80, 443);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// My Secure Database Check
|
// My Secure Database Check
|
||||||
//
|
//
|
||||||
function approveDomains(opts, certs, cb) {
|
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);
|
||||||
|
|
||||||
// Only one domain is listed with *automatic* registration via SNI
|
// The domains being approved for the first time are listed in opts.domains
|
||||||
// (it's an array because managed registration allows for multiple domains,
|
// Certs being renewed are listed in certs.altnames
|
||||||
// which was the case in the simple example)
|
if (certs) {
|
||||||
console.log(opts.domains);
|
opts.domains = [certs.subject].concat(certs.altnames);
|
||||||
|
}
|
||||||
|
|
||||||
// The domains being approved for the first time are listed in opts.domains
|
fooCheckDb(opts.domains, function(err, agree, email) {
|
||||||
// Certs being renewed are listed in certs.altnames
|
if (err) {
|
||||||
if (certs) {
|
cb(err);
|
||||||
opts.domains = [certs.subject].concat(certs.altnames);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fooCheckDb(opts.domains, function (err, agree, email) {
|
// Services SHOULD automatically accept the ToS and use YOUR email
|
||||||
if (err) { cb(err); return; }
|
// Clients MUST NOT accept the ToS without asking the user
|
||||||
|
opts.agreeTos = agree;
|
||||||
|
opts.email = email;
|
||||||
|
|
||||||
// Services SHOULD automatically accept the ToS and use YOUR email
|
// NOTE: you can also change other options such as `challengeType` and `challenge`
|
||||||
// Clients MUST NOT accept the ToS without asking the user
|
// (this would be helpful if you decided you wanted wildcard support as a domain altname)
|
||||||
opts.agreeTos = agree;
|
// opts.challengeType = 'http-01';
|
||||||
opts.email = email;
|
// opts.challenge = require('le-challenge-fs').create({});
|
||||||
|
|
||||||
// NOTE: you can also change other options such as `challengeType` and `challenge`
|
cb(null, { options: opts, certs: certs });
|
||||||
// (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
|
// My User / Domain Database
|
||||||
//
|
//
|
||||||
function fooCheckDb(domains, cb) {
|
function fooCheckDb(domains, cb) {
|
||||||
// This is an oversimplified example of how we might implement a check in
|
// 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
|
// our database if we have different rules for different users and domains
|
||||||
var domains = [ 'example.com', 'www.example.com' ];
|
var domains = ["example.com", "www.example.com"];
|
||||||
var userEmail = 'john.doe@example.com';
|
var userEmail = "john.doe@example.com";
|
||||||
var userAgrees = true;
|
var userAgrees = true;
|
||||||
var passCheck = opts.domains.every(function (domain) {
|
var passCheck = opts.domains.every(function(domain) {
|
||||||
return -1 !== domains.indexOf(domain);
|
return -1 !== domains.indexOf(domain);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!passCheck) {
|
if (!passCheck) {
|
||||||
cb(new Error('domain not allowed'));
|
cb(new Error("domain not allowed"));
|
||||||
} else {
|
} else {
|
||||||
cb(null, userAgrees, userEmail);
|
cb(null, userAgrees, userEmail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,38 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
//require('greenlock-express')
|
//require('greenlock-express')
|
||||||
require('../').create({
|
require("../")
|
||||||
|
.create({
|
||||||
|
// Let's Encrypt v2 is ACME draft 11
|
||||||
|
version: "draft-11",
|
||||||
|
|
||||||
// Let's Encrypt v2 is ACME draft 11
|
server: "https://acme-v02.api.letsencrypt.org/directory",
|
||||||
version: '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'
|
// You MUST change this to a valid email address
|
||||||
// Note: If at first you don't succeed, stop and switch to staging
|
email: "john.doe@example.com",
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
|
||||||
|
|
||||||
// You MUST change this to a valid email address
|
// You MUST NOT build clients that accept the ToS without asking the user
|
||||||
, email: 'john.doe@example.com'
|
agreeTos: true,
|
||||||
|
|
||||||
// You MUST NOT build clients that accept the ToS without asking the user
|
// You MUST change these to valid domains
|
||||||
, agreeTos: true
|
// NOTE: all domains will validated and listed on the certificate
|
||||||
|
approvedDomains: ["example.com", "www.example.com"],
|
||||||
|
|
||||||
// You MUST change these to valid domains
|
// You MUST have access to write to directory where certs are saved
|
||||||
// NOTE: all domains will validated and listed on the certificate
|
// ex: /home/foouser/acme/etc
|
||||||
, approvedDomains: [ 'example.com', 'www.example.com' ]
|
configDir: "~/.config/acme/",
|
||||||
|
store: require("greenlock-store-fs"),
|
||||||
|
|
||||||
// You MUST have access to write to directory where certs are saved
|
app: require("express")().use("/", function(req, res) {
|
||||||
// ex: /home/foouser/acme/etc
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||||
, configDir: '~/.config/acme/'
|
res.end("Hello, World!\n\n💚 🔒.js");
|
||||||
|
}),
|
||||||
|
|
||||||
, app: require('express')().use('/', function (req, res) {
|
// Get notified of important updates and help me make greenlock better
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
communityMember: true
|
||||||
res.end('Hello, World!\n\n💚 🔒.js');
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get notified of important updates and help me make greenlock better
|
//, debug: true
|
||||||
, communityMember: true
|
})
|
||||||
|
.listen(80, 443);
|
||||||
//, debug: true
|
|
||||||
|
|
||||||
}).listen(80, 443);
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
//
|
//
|
||||||
// WARNING: Not for noobs
|
// WARNING: Not for noobs
|
||||||
|
@ -9,87 +9,96 @@
|
||||||
// This demo is used with tunnel-server.js and tunnel-client.js
|
// This demo is used with tunnel-server.js and tunnel-client.js
|
||||||
//
|
//
|
||||||
|
|
||||||
var email = 'john.doe@gmail.com';
|
var email = "john.doe@gmail.com";
|
||||||
var domains = [ 'example.com' ];
|
var domains = ["example.com"];
|
||||||
var agreeLeTos = true;
|
var agreeLeTos = true;
|
||||||
//var secret = "My Little Brony";
|
//var secret = "My Little Brony";
|
||||||
var secret = require('crypto').randomBytes(16).toString('hex');
|
var secret = require("crypto")
|
||||||
|
.randomBytes(16)
|
||||||
|
.toString("hex");
|
||||||
|
|
||||||
require('../').create({
|
require("../")
|
||||||
version: 'draft-11'
|
.create({
|
||||||
|
version: "draft-11",
|
||||||
|
|
||||||
, server: 'https://acme-v02.api.letsencrypt.org/directory'
|
server: "https://acme-v02.api.letsencrypt.org/directory",
|
||||||
// Note: If at first you don't succeed, stop and switch to staging
|
// Note: If at first you don't succeed, stop and switch to staging
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
// 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);
|
|
||||||
|
|
||||||
|
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) {
|
function remoteAccess(secret) {
|
||||||
var express = require('express');
|
var express = require("express");
|
||||||
var basicAuth = require('express-basic-auth');
|
var basicAuth = require("express-basic-auth");
|
||||||
var serveIndex = require('serve-index');
|
var serveIndex = require("serve-index");
|
||||||
|
|
||||||
var rootIndex = serveIndex('/', { hidden: true, icons: true, view: 'details' });
|
var rootIndex = serveIndex("/", { hidden: true, icons: true, view: "details" });
|
||||||
var rootFs = express.static('/', { dotfiles: 'allow', redirect: true, index: false });
|
var rootFs = express.static("/", { dotfiles: "allow", redirect: true, index: false });
|
||||||
|
|
||||||
var userIndex = serveIndex(require('os').homedir(), { hidden: true, icons: true, view: 'details' });
|
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 userFs = express.static(require("os").homedir(), { dotfiles: "allow", redirect: true, index: false });
|
||||||
|
|
||||||
var app = express();
|
var app = express();
|
||||||
var realm = 'Login Required';
|
var realm = "Login Required";
|
||||||
|
|
||||||
var myAuth = basicAuth({
|
var myAuth = basicAuth({
|
||||||
users: { 'root': secret, 'user': secret }
|
users: { root: secret, user: secret },
|
||||||
, challenge: true
|
challenge: true,
|
||||||
, realm: realm
|
realm: realm,
|
||||||
, unauthorizedResponse: function (/*req*/) {
|
unauthorizedResponse: function(/*req*/) {
|
||||||
return 'Unauthorized <a href="/">Home</a>';
|
return 'Unauthorized <a href="/">Home</a>';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/', function (req, res) {
|
app.get("/", function(req, res) {
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||||
res.end(
|
res.end('<a href="/browse/">View Files</a>' + " | " + '<a href="/logout/">Logout</a>');
|
||||||
'<a href="/browse/">View Files</a>'
|
});
|
||||||
+ ' | '
|
app.use("/logout", function(req, res) {
|
||||||
+ '<a href="/logout/">Logout</a>'
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||||
);
|
res.setHeader("WWW-Authenticate", 'Basic realm="' + realm + '"');
|
||||||
});
|
res.statusCode = 401;
|
||||||
app.use('/logout', function (req, res) {
|
//res.setHeader('Location', '/');
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
res.end('Logged out | <a href="/">Home</a>');
|
||||||
res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
|
});
|
||||||
res.statusCode = 401;
|
app.use("/browse", myAuth);
|
||||||
//res.setHeader('Location', '/');
|
app.use("/browse", function(req, res, next) {
|
||||||
res.end('Logged out | <a href="/">Home</a>');
|
if ("root" === req.auth.user) {
|
||||||
});
|
rootFs(req, res, function() {
|
||||||
app.use('/browse', myAuth);
|
rootIndex(req, res, next);
|
||||||
app.use('/browse', function (req, res, next) {
|
});
|
||||||
if ('root' === req.auth.user) { rootFs(req, res, function () { rootIndex(req, res, next); }); return; }
|
return;
|
||||||
if ('user' === req.auth.user) { userFs(req, res, function () { userIndex(req, res, next); }); return; }
|
}
|
||||||
res.end('Sad Panda');
|
if ("user" === req.auth.user) {
|
||||||
});
|
userFs(req, res, function() {
|
||||||
|
userIndex(req, res, next);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.end("Sad Panda");
|
||||||
|
});
|
||||||
|
|
||||||
console.log('');
|
console.log("");
|
||||||
console.log('');
|
console.log("");
|
||||||
console.log('Usernames are\n');
|
console.log("Usernames are\n");
|
||||||
console.log('\troot');
|
console.log("\troot");
|
||||||
console.log('\tuser');
|
console.log("\tuser");
|
||||||
console.log('');
|
console.log("");
|
||||||
console.log('Password (for both) is\n');
|
console.log("Password (for both) is\n");
|
||||||
console.log('\t' + secret);
|
console.log("\t" + secret);
|
||||||
console.log('');
|
console.log("");
|
||||||
console.log("Shhhh... It's a secret to everybody!");
|
console.log("Shhhh... It's a secret to everybody!");
|
||||||
console.log('');
|
console.log("");
|
||||||
console.log('');
|
console.log("");
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
// I'm not a fan of `socket.io` because it's huge and complex.
|
// 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.
|
// I much prefer `ws` because it's very simple and easy.
|
||||||
// That said, it's popular.......
|
// That said, it's popular.......
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
//var greenlock = require('greenlock-express');
|
//var greenlock = require('greenlock-express');
|
||||||
var greenlock = require('../');
|
var greenlock = require("../");
|
||||||
var options = require('./greenlock-options.js');
|
var options = require("./greenlock-options.js");
|
||||||
var socketio = require('socket.io');
|
var socketio = require("socket.io");
|
||||||
var server;
|
var server;
|
||||||
var io;
|
var io;
|
||||||
|
|
||||||
// Any node http app will do - whether express, raw http or whatever
|
// Any node http app will do - whether express, raw http or whatever
|
||||||
options.app = require('express')().use('/', function (req, res) {
|
options.app = require("express")().use("/", function(req, res) {
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||||
res.end('Hello, World!\n\n💚 🔒.js');
|
res.end("Hello, World!\n\n💚 🔒.js");
|
||||||
});
|
});
|
||||||
|
|
||||||
// The server that's handed back from `listen` is a raw https server
|
// The server that's handed back from `listen` is a raw https server
|
||||||
|
@ -22,11 +22,11 @@ server = greenlock.create(options).listen(80, 443);
|
||||||
io = socketio(server);
|
io = socketio(server);
|
||||||
|
|
||||||
// Then you do your socket.io stuff
|
// Then you do your socket.io stuff
|
||||||
io.on('connection', function (socket) {
|
io.on("connection", function(socket) {
|
||||||
console.log('a user connected');
|
console.log("a user connected");
|
||||||
socket.emit('Welcome');
|
socket.emit("Welcome");
|
||||||
|
|
||||||
socket.on('chat message', function (msg) {
|
socket.on("chat message", function(msg) {
|
||||||
socket.broadcast.emit('chat message', msg);
|
socket.broadcast.emit("chat message", msg);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,54 +1,50 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
// npm install spdy@3.x
|
// npm install spdy@3.x
|
||||||
|
|
||||||
//var Greenlock = require('greenlock-express')
|
//var Greenlock = require('greenlock-express')
|
||||||
var Greenlock = require('../');
|
var Greenlock = require("../");
|
||||||
|
|
||||||
var greenlock = Greenlock.create({
|
var greenlock = Greenlock.create({
|
||||||
|
// Let's Encrypt v2 is ACME draft 11
|
||||||
|
version: "draft-11",
|
||||||
|
|
||||||
// Let's Encrypt v2 is ACME draft 11
|
server: "https://acme-v02.api.letsencrypt.org/directory",
|
||||||
version: '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'
|
// You MUST change this to a valid email address
|
||||||
// Note: If at first you don't succeed, stop and switch to staging
|
email: "jon@example.com",
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
|
||||||
|
|
||||||
// You MUST change this to a valid email address
|
// You MUST NOT build clients that accept the ToS without asking the user
|
||||||
, email: 'jon@example.com'
|
agreeTos: true,
|
||||||
|
|
||||||
// You MUST NOT build clients that accept the ToS without asking the user
|
// You MUST change these to valid domains
|
||||||
, agreeTos: true
|
// NOTE: all domains will validated and listed on the certificate
|
||||||
|
approvedDomains: ["example.com", "www.example.com"],
|
||||||
|
|
||||||
// You MUST change these to valid domains
|
// You MUST have access to write to directory where certs are saved
|
||||||
// NOTE: all domains will validated and listed on the certificate
|
// ex: /home/foouser/acme/etc
|
||||||
, approvedDomains: [ 'example.com', 'www.example.com' ]
|
configDir: "~/.config/acme/", // MUST have write access
|
||||||
|
|
||||||
// You MUST have access to write to directory where certs are saved
|
// Get notified of important updates and help me make greenlock better
|
||||||
// ex: /home/foouser/acme/etc
|
communityMember: true
|
||||||
, configDir: '~/.config/acme/' // MUST have write access
|
|
||||||
|
|
||||||
// Get notified of important updates and help me make greenlock better
|
|
||||||
, communityMember: true
|
|
||||||
|
|
||||||
//, debug: true
|
|
||||||
|
|
||||||
|
//, debug: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// http-01 Challenges //
|
// http-01 Challenges //
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
// http-01 challenge happens over http/1.1, not http2
|
// http-01 challenge happens over http/1.1, not http2
|
||||||
var redirectHttps = require('redirect-https')();
|
var redirectHttps = require("redirect-https")();
|
||||||
var acmeChallengeHandler = greenlock.middleware(redirectHttps);
|
var acmeChallengeHandler = greenlock.middleware(redirectHttps);
|
||||||
require('http').createServer(acmeChallengeHandler).listen(80, function () {
|
require("http")
|
||||||
console.log("Listening for ACME http-01 challenges on", this.address());
|
.createServer(acmeChallengeHandler)
|
||||||
});
|
.listen(80, function() {
|
||||||
|
console.log("Listening for ACME http-01 challenges on", this.address());
|
||||||
|
});
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// http2 via SPDY h2 //
|
// http2 via SPDY h2 //
|
||||||
|
@ -56,13 +52,13 @@ require('http').createServer(acmeChallengeHandler).listen(80, function () {
|
||||||
|
|
||||||
// spdy is a drop-in replacement for the https API
|
// spdy is a drop-in replacement for the https API
|
||||||
var spdyOptions = Object.assign({}, greenlock.tlsOptions);
|
var spdyOptions = Object.assign({}, greenlock.tlsOptions);
|
||||||
spdyOptions.spdy = { protocols: [ 'h2', 'http/1.1' ], plain: false };
|
spdyOptions.spdy = { protocols: ["h2", "http/1.1"], plain: false };
|
||||||
var myApp = require('./my-express-app.js');
|
var myApp = require("./my-express-app.js");
|
||||||
var server = require('spdy').createServer(spdyOptions, myApp);
|
var server = require("spdy").createServer(spdyOptions, myApp);
|
||||||
server.on('error', function (err) {
|
server.on("error", function(err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
});
|
});
|
||||||
server.on('listening', function () {
|
server.on("listening", function() {
|
||||||
console.log("Listening for SPDY/http2/https requests on", this.address());
|
console.log("Listening for SPDY/http2/https requests on", this.address());
|
||||||
});
|
});
|
||||||
server.listen(443);
|
server.listen(443);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
// vhost example //
|
// vhost example //
|
||||||
|
@ -11,118 +11,124 @@
|
||||||
|
|
||||||
// The prefix where sites go by name.
|
// The prefix where sites go by name.
|
||||||
// For example: whatever.com may live in /srv/www/whatever.com, thus /srv/www is our path
|
// 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 srv = process.argv[3] || "/srv/www/";
|
||||||
|
|
||||||
var path = require('path');
|
var path = require("path");
|
||||||
var fs = require('fs').promises;
|
var fs = require("fs").promises;
|
||||||
var finalhandler = require('finalhandler');
|
var finalhandler = require("finalhandler");
|
||||||
var serveStatic = require('serve-static');
|
var serveStatic = require("serve-static");
|
||||||
|
|
||||||
//var glx = require('greenlock-express')
|
//var glx = require('greenlock-express')
|
||||||
var glx = require('./').create({
|
var glx = require("./").create({
|
||||||
|
version: "draft-11", // Let's Encrypt v2 is ACME draft 11
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
, server: 'https://acme-v02.api.letsencrypt.org/directory' // If at first you don't succeed, stop and switch to staging
|
configDir: process.argv[4] || "~/.config/acme/", // You MUST have access to write to directory where certs
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
// are saved. ex: /home/foouser/.config/acme
|
||||||
|
|
||||||
, configDir: process.argv[4] || '~/.config/acme/' // You MUST have access to write to directory where certs
|
approveDomains: myApproveDomains, // Greenlock's wraps around tls.SNICallback. Check the
|
||||||
// are saved. ex: /home/foouser/.config/acme
|
// domain name here and reject invalid ones
|
||||||
|
|
||||||
, approveDomains: myApproveDomains // Greenlock's wraps around tls.SNICallback. Check the
|
app: myVhostApp, // Any node-style http app (i.e. express, koa, hapi, rill)
|
||||||
// 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
|
||||||
/* CHANGE TO A VALID EMAIL */
|
agreeTos: true // Accept Let's Encrypt ToS
|
||||||
, email: process.argv[2] || 'jon.doe@example.com' // Email for Let's Encrypt account and Greenlock Security
|
//, communityMember: true // Join Greenlock to get important updates, no spam
|
||||||
, agreeTos: true // Accept Let's Encrypt ToS
|
|
||||||
//, communityMember: true // Join Greenlock to get important updates, no spam
|
|
||||||
|
|
||||||
//, debug: true
|
|
||||||
|
|
||||||
|
//, debug: true
|
||||||
});
|
});
|
||||||
|
|
||||||
var server = glx.listen(80, 443);
|
var server = glx.listen(80, 443);
|
||||||
server.on('listening', function () {
|
server.on("listening", function() {
|
||||||
console.info(server.type + " listening on", server.address());
|
console.info(server.type + " listening on", server.address());
|
||||||
});
|
});
|
||||||
|
|
||||||
function myApproveDomains(opts, certs, cb) {
|
function myApproveDomains(opts, certs, cb) {
|
||||||
console.log('sni:', opts.domain);
|
console.log("sni:", opts.domain);
|
||||||
// In this example the filesystem is our "database".
|
// In this example the filesystem is our "database".
|
||||||
// We check in /srv/www for whatever.com and if it exists, it's allowed
|
// 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
|
// SECURITY Greenlock validates opts.domains ahead-of-time so you don't have to
|
||||||
return checkWwws(opts.domains[0]).then(function () {
|
return checkWwws(opts.domains[0])
|
||||||
//opts.email = email;
|
.then(function() {
|
||||||
opts.agreeTos = true;
|
//opts.email = email;
|
||||||
cb(null, { options: opts, certs: certs });
|
opts.agreeTos = true;
|
||||||
}).catch(cb);
|
cb(null, { options: opts, certs: certs });
|
||||||
|
})
|
||||||
|
.catch(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkWwws(_hostname) {
|
function checkWwws(_hostname) {
|
||||||
if (!_hostname) {
|
if (!_hostname) {
|
||||||
// SECURITY, don't allow access to the 'srv' root
|
// SECURITY, don't allow access to the 'srv' root
|
||||||
// (greenlock-express uses middleware to check '..', etc)
|
// (greenlock-express uses middleware to check '..', etc)
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
var hostname = _hostname;
|
var hostname = _hostname;
|
||||||
var _hostdir = path.join(srv, hostname);
|
var _hostdir = path.join(srv, hostname);
|
||||||
var hostdir = _hostdir;
|
var hostdir = _hostdir;
|
||||||
// TODO could test for www/no-www both in directory
|
// TODO could test for www/no-www both in directory
|
||||||
return fs.readdir(hostdir).then(function () {
|
return fs
|
||||||
// TODO check for some sort of htaccess.json and use email in that
|
.readdir(hostdir)
|
||||||
// NOTE: you can also change other options such as `challengeType` and `challenge`
|
.then(function() {
|
||||||
// opts.challengeType = 'http-01';
|
// TODO check for some sort of htaccess.json and use email in that
|
||||||
// opts.challenge = require('le-challenge-fs').create({});
|
// NOTE: you can also change other options such as `challengeType` and `challenge`
|
||||||
return hostname;
|
// opts.challengeType = 'http-01';
|
||||||
}).catch(function () {
|
// opts.challenge = require('le-challenge-fs').create({});
|
||||||
if ('www.' === hostname.slice(0, 4)) {
|
return hostname;
|
||||||
// Assume we'll redirect to non-www if it's available.
|
})
|
||||||
hostname = hostname.slice(4);
|
.catch(function() {
|
||||||
hostdir = path.join(srv, hostname);
|
if ("www." === hostname.slice(0, 4)) {
|
||||||
return fs.readdir(hostdir).then(function () {
|
// Assume we'll redirect to non-www if it's available.
|
||||||
// TODO list both domains?
|
hostname = hostname.slice(4);
|
||||||
return hostname;
|
hostdir = path.join(srv, hostname);
|
||||||
});
|
return fs.readdir(hostdir).then(function() {
|
||||||
} else {
|
// TODO list both domains?
|
||||||
// Or check and see if perhaps we should redirect non-www to www
|
return hostname;
|
||||||
hostname = 'www.' + hostname;
|
});
|
||||||
hostdir = path.join(srv, hostname);
|
} else {
|
||||||
return fs.readdir(hostdir).then(function () {
|
// Or check and see if perhaps we should redirect non-www to www
|
||||||
// TODO list both domains?
|
hostname = "www." + hostname;
|
||||||
return hostname;
|
hostdir = path.join(srv, hostname);
|
||||||
});
|
return fs.readdir(hostdir).then(function() {
|
||||||
}
|
// TODO list both domains?
|
||||||
}).catch(function () {
|
return hostname;
|
||||||
throw new Error("rejecting '" + _hostname + "' because '" + _hostdir + "' could not be read");
|
});
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
throw new Error("rejecting '" + _hostname + "' because '" + _hostdir + "' could not be read");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function myVhostApp(req, res) {
|
function myVhostApp(req, res) {
|
||||||
// SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to
|
// SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to
|
||||||
// (also: only domains approved above will get here)
|
// (also: only domains approved above will get here)
|
||||||
console.log('vhost:', req.headers.host);
|
console.log("vhost:", req.headers.host);
|
||||||
if (!req.headers.host) {
|
if (!req.headers.host) {
|
||||||
// SECURITY, don't allow access to the 'srv' root
|
// SECURITY, don't allow access to the 'srv' root
|
||||||
// (greenlock-express uses middleware to check '..', etc)
|
// (greenlock-express uses middleware to check '..', etc)
|
||||||
return res.end();
|
return res.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We could cache wether or not a host exists for some amount of time
|
// We could cache wether or not a host exists for some amount of time
|
||||||
var fin = finalhandler(req, res);
|
var fin = finalhandler(req, res);
|
||||||
return checkWwws(req.headers.host).then(function (hostname) {
|
return checkWwws(req.headers.host)
|
||||||
if (hostname !== req.headers.host) {
|
.then(function(hostname) {
|
||||||
res.statusCode = 302;
|
if (hostname !== req.headers.host) {
|
||||||
res.setHeader('Location', 'https://' + hostname);
|
res.statusCode = 302;
|
||||||
// SECURITY this is safe only because greenlock disallows invalid hostnames
|
res.setHeader("Location", "https://" + hostname);
|
||||||
res.end("<!-- redirecting to https://" + hostname + "-->");
|
// SECURITY this is safe only because greenlock disallows invalid hostnames
|
||||||
return;
|
res.end("<!-- redirecting to https://" + hostname + "-->");
|
||||||
}
|
return;
|
||||||
var serve = serveStatic(path.join(srv, hostname), { redirect: true });
|
}
|
||||||
serve(req, res, fin);
|
var serve = serveStatic(path.join(srv, hostname), { redirect: true });
|
||||||
}).catch(function () {
|
serve(req, res, fin);
|
||||||
fin();
|
})
|
||||||
});
|
.catch(function() {
|
||||||
|
fin();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,46 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// Greenlock Setup //
|
// Greenlock Setup //
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
//var Greenlock = require('greenlock-express');
|
//var Greenlock = require('greenlock-express');
|
||||||
var Greenlock = require('../');
|
var Greenlock = require("../");
|
||||||
var greenlock = Greenlock.create({
|
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"),
|
||||||
|
|
||||||
// Let's Encrypt v2 is ACME draft 11
|
// You MUST change these to a valid email and domains
|
||||||
// Note: If at first you don't succeed, stop and switch to staging
|
email: "john.doe@example.com",
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
approvedDomains: ["example.com", "www.example.com"],
|
||||||
server: 'https://acme-v02.api.letsencrypt.org/directory'
|
agreeTos: true,
|
||||||
, version: 'draft-11'
|
|
||||||
, configDir: '~/.config/acme/'
|
|
||||||
, app: require('./my-express-app.js')
|
|
||||||
|
|
||||||
// You MUST change these to a valid email and domains
|
// Get notified of important updates and help me make greenlock better
|
||||||
, email: 'john.doe@example.com'
|
communityMember: true,
|
||||||
, approvedDomains: [ 'example.com', 'www.example.com' ]
|
telemetry: true
|
||||||
, agreeTos: true
|
//, debug: 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 server = greenlock.listen(80, 443);
|
||||||
|
|
||||||
var WebSocket = require('ws');
|
var WebSocket = require("ws");
|
||||||
var ws = new WebSocket.Server({ server: server });
|
var ws = new WebSocket.Server({ server: server });
|
||||||
ws.on('connection', function (ws, req) {
|
ws.on("connection", function(ws, req) {
|
||||||
// inspect req.headers.authorization (or cookies) for session info
|
// inspect req.headers.authorization (or cookies) for session info
|
||||||
ws.send("[Secure Echo Server] Hello!\nAuth: '" + (req.headers.authorization || 'none') + "'\n"
|
ws.send(
|
||||||
+ "Cookie: '" + (req.headers.cookie || 'none') + "'\n");
|
"[Secure Echo Server] Hello!\nAuth: '" +
|
||||||
ws.on('message', function (data) { ws.send(data); });
|
(req.headers.authorization || "none") +
|
||||||
|
"'\n" +
|
||||||
|
"Cookie: '" +
|
||||||
|
(req.headers.cookie || "none") +
|
||||||
|
"'\n"
|
||||||
|
);
|
||||||
|
ws.on("message", function(data) {
|
||||||
|
ws.send(data);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
'use strict';
|
"use strict";
|
||||||
/*global Promise*/
|
/*global Promise*/
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
|
@ -11,60 +11,67 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
//var glx = require('greenlock-express')
|
//var glx = require('greenlock-express')
|
||||||
var glx = require('../').create({
|
var glx = require("../").create({
|
||||||
|
version: "draft-11", // Let's Encrypt v2 is ACME draft 11
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
, server: 'https://acme-staging-v02.api.letsencrypt.org/directory'
|
configDir: "~/acme/", // You MUST have access to write to directory where certs
|
||||||
//, server: 'https://acme-v02.api.letsencrypt.org/directory' // If at first you don't succeed, stop and switch to staging
|
// are saved. ex: /home/foouser/.config/acme
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
|
||||||
|
|
||||||
, configDir: '~/acme/' // You MUST have access to write to directory where certs
|
approveDomains: myApproveDomains, // Greenlock's wraps around tls.SNICallback. Check the
|
||||||
// are saved. ex: /home/foouser/.config/acme
|
// domain name here and reject invalid ones
|
||||||
|
|
||||||
, approveDomains: myApproveDomains // Greenlock's wraps around tls.SNICallback. Check the
|
app: require("./my-express-app.js"), // Any node-style http app (i.e. express, koa, hapi, rill)
|
||||||
// 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
|
||||||
|
|
||||||
/* CHANGE TO A VALID EMAIL */
|
//, debug: true
|
||||||
, email: 'jon.doe@example.com' // Email for Let's Encrypt account and Greenlock Security
|
store: require("le-store-fs")
|
||||||
, 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);
|
var server = glx.listen(80, 443);
|
||||||
server.on('listening', function () {
|
server.on("listening", function() {
|
||||||
console.info(server.type + " listening on", server.address());
|
console.info(server.type + " listening on", server.address());
|
||||||
});
|
});
|
||||||
|
|
||||||
function myApproveDomains(opts) {
|
function myApproveDomains(opts) {
|
||||||
console.log('sni:', opts.domain);
|
console.log("sni:", opts.domain);
|
||||||
|
|
||||||
// must be 'example.com' or start with 'example.com'
|
// must be 'example.com' or start with 'example.com'
|
||||||
if ('example.com' !== opts.domain
|
if (
|
||||||
&& 'example.com' !== opts.domain.split('.').slice(1).join('.')) {
|
"example.com" !== opts.domain &&
|
||||||
return Promise.reject(new Error("we don't serve your kind here: " + 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
|
// the primary domain for the cert
|
||||||
opts.subject = 'example.com';
|
opts.subject = "example.com";
|
||||||
// the altnames (including the primary)
|
// the altnames (including the primary)
|
||||||
opts.domains = [ opts.subject, '*.example.com' ];
|
opts.domains = [opts.subject, "*.example.com"];
|
||||||
|
|
||||||
if (!opts.challenges) { opts.challenges = {}; }
|
if (!opts.challenges) {
|
||||||
opts.challenges['http-01'] = require('le-challenge-fs').create({});
|
opts.challenges = {};
|
||||||
// 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.
|
opts.challenges["http-01"] = require("le-challenge-fs").create({});
|
||||||
// That could take several seconds to a few minutes.
|
// Note: When implementing a dns-01 plugin you should make it check in a loop
|
||||||
opts.challenges['dns-01'] = require('le-challenge-dns').create({});
|
// 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
|
// explicitly set account id and certificate.id
|
||||||
opts.account = { id: opts.email };
|
opts.account = { id: opts.email };
|
||||||
opts.certificate = { id: opts.subject };
|
opts.certificate = { id: opts.subject };
|
||||||
|
|
||||||
return Promise.resolve(opts);
|
return Promise.resolve(opts);
|
||||||
}
|
}
|
||||||
|
|
522
index.js
522
index.js
|
@ -1,265 +1,321 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
var PromiseA;
|
var PromiseA;
|
||||||
try {
|
try {
|
||||||
PromiseA = require('bluebird');
|
PromiseA = require("bluebird");
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
PromiseA = global.Promise;
|
PromiseA = global.Promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// opts.approveDomains(options, certs, cb)
|
// opts.approveDomains(options, certs, cb)
|
||||||
module.exports.create = function (opts) {
|
module.exports.create = function(opts) {
|
||||||
// accept all defaults for greenlock.challenges, greenlock.store, greenlock.middleware
|
// accept all defaults for greenlock.challenges, greenlock.store, greenlock.middleware
|
||||||
if (!opts._communityPackage) {
|
if (!opts._communityPackage) {
|
||||||
opts._communityPackage = 'greenlock-express.js';
|
opts._communityPackage = "greenlock-express.js";
|
||||||
opts._communityPackageVersion = require('./package.json').version;
|
opts._communityPackageVersion = require("./package.json").version;
|
||||||
}
|
}
|
||||||
|
|
||||||
function explainError(e) {
|
function explainError(e) {
|
||||||
console.error('Error:' + e.message);
|
console.error("Error:" + e.message);
|
||||||
if ('EACCES' === e.errno) {
|
if ("EACCES" === e.errno) {
|
||||||
console.error("You don't have prmission to access '" + e.address + ":" + e.port + "'.");
|
console.error("You don't have prmission to access '" + e.address + ":" + e.port + "'.");
|
||||||
console.error("You probably need to use \"sudo\" or \"sudo setcap 'cap_net_bind_service=+ep' $(which node)\"");
|
console.error('You probably need to use "sudo" or "sudo setcap \'cap_net_bind_service=+ep\' $(which node)"');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ('EADDRINUSE' === e.errno) {
|
if ("EADDRINUSE" === e.errno) {
|
||||||
console.error("'" + e.address + ":" + e.port + "' is already being used by some other program.");
|
console.error("'" + e.address + ":" + e.port + "' is already being used by some other program.");
|
||||||
console.error("You probably need to stop that program or restart your computer.");
|
console.error("You probably need to stop that program or restart your computer.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.error(e.code + ": '" + e.address + ":" + e.port + "'");
|
console.error(e.code + ": '" + e.address + ":" + e.port + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createPlain(plainPort) {
|
function _createPlain(plainPort) {
|
||||||
if (!plainPort) { plainPort = 80; }
|
if (!plainPort) {
|
||||||
|
plainPort = 80;
|
||||||
|
}
|
||||||
|
|
||||||
var parts = String(plainPort).split(':');
|
var parts = String(plainPort).split(":");
|
||||||
var p = parts.pop();
|
var p = parts.pop();
|
||||||
var addr = parts.join(':').replace(/^\[/, '').replace(/\]$/, '');
|
var addr = parts
|
||||||
var args = [];
|
.join(":")
|
||||||
var httpType;
|
.replace(/^\[/, "")
|
||||||
var server;
|
.replace(/\]$/, "");
|
||||||
var validHttpPort = (parseInt(p, 10) >= 0);
|
var args = [];
|
||||||
|
var httpType;
|
||||||
|
var server;
|
||||||
|
var validHttpPort = parseInt(p, 10) >= 0;
|
||||||
|
|
||||||
if (addr) { args[1] = addr; }
|
if (addr) {
|
||||||
if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) {
|
args[1] = addr;
|
||||||
console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe");
|
}
|
||||||
}
|
if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) {
|
||||||
|
console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe");
|
||||||
|
}
|
||||||
|
|
||||||
server = require('http').createServer(
|
server = require("http").createServer(
|
||||||
greenlock.middleware.sanitizeHost(greenlock.middleware(require('redirect-https')()))
|
greenlock.middleware.sanitizeHost(greenlock.middleware(require("redirect-https")()))
|
||||||
);
|
);
|
||||||
httpType = 'http';
|
httpType = "http";
|
||||||
|
|
||||||
return { server: server, listen: function () { return new PromiseA(function (resolve, reject) {
|
return {
|
||||||
args[0] = p;
|
server: server,
|
||||||
args.push(function () {
|
listen: function() {
|
||||||
if (!greenlock.servername) {
|
return new PromiseA(function(resolve, reject) {
|
||||||
if (Array.isArray(greenlock.approvedDomains) && greenlock.approvedDomains.length) {
|
args[0] = p;
|
||||||
greenlock.servername = greenlock.approvedDomains[0];
|
args.push(function() {
|
||||||
}
|
if (!greenlock.servername) {
|
||||||
if (Array.isArray(greenlock.approveDomains) && greenlock.approvedDomains.length) {
|
if (Array.isArray(greenlock.approvedDomains) && greenlock.approvedDomains.length) {
|
||||||
greenlock.servername = greenlock.approvedDomains[0];
|
greenlock.servername = greenlock.approvedDomains[0];
|
||||||
}
|
}
|
||||||
}
|
if (Array.isArray(greenlock.approveDomains) && greenlock.approvedDomains.length) {
|
||||||
|
greenlock.servername = greenlock.approvedDomains[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!greenlock.servername) {
|
if (!greenlock.servername) {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return greenlock.check({ domains: [ greenlock.servername ] }).then(function (certs) {
|
return greenlock
|
||||||
if (certs) {
|
.check({ domains: [greenlock.servername] })
|
||||||
return {
|
.then(function(certs) {
|
||||||
key: Buffer.from(certs.privkey, 'ascii')
|
if (certs) {
|
||||||
, cert: Buffer.from(certs.cert + '\r\n' + certs.chain, 'ascii')
|
return {
|
||||||
};
|
key: Buffer.from(certs.privkey, "ascii"),
|
||||||
}
|
cert: Buffer.from(certs.cert + "\r\n" + certs.chain, "ascii")
|
||||||
console.info("Fetching certificate for '%s' to use as default for HTTPS server...", greenlock.servername);
|
};
|
||||||
return new PromiseA(function (resolve, reject) {
|
}
|
||||||
// using SNICallback because all options will be set
|
console.info(
|
||||||
greenlock.tlsOptions.SNICallback(greenlock.servername, function (err/*, secureContext*/) {
|
"Fetching certificate for '%s' to use as default for HTTPS server...",
|
||||||
if (err) { reject(err); return; }
|
greenlock.servername
|
||||||
return greenlock.check({ domains: [ greenlock.servername ] }).then(function (certs) {
|
);
|
||||||
resolve({
|
return new PromiseA(function(resolve, reject) {
|
||||||
key: Buffer.from(certs.privkey, 'ascii')
|
// using SNICallback because all options will be set
|
||||||
, cert: Buffer.from(certs.cert + '\r\n' + certs.chain, 'ascii')
|
greenlock.tlsOptions.SNICallback(greenlock.servername, function(err /*, secureContext*/) {
|
||||||
});
|
if (err) {
|
||||||
}).catch(reject);
|
reject(err);
|
||||||
});
|
return;
|
||||||
});
|
}
|
||||||
}).then(resolve).catch(reject);
|
return greenlock
|
||||||
});
|
.check({ domains: [greenlock.servername] })
|
||||||
server.listen.apply(server, args).on('error', function (e) {
|
.then(function(certs) {
|
||||||
if (server.listenerCount('error') < 2) {
|
resolve({
|
||||||
console.warn("Did not successfully create http server and bind to port '" + p + "':");
|
key: Buffer.from(certs.privkey, "ascii"),
|
||||||
explainError(e);
|
cert: Buffer.from(certs.cert + "\r\n" + certs.chain, "ascii")
|
||||||
process.exit(41);
|
});
|
||||||
}
|
})
|
||||||
});
|
.catch(reject);
|
||||||
}); } };
|
});
|
||||||
}
|
});
|
||||||
|
})
|
||||||
|
.then(resolve)
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
server.listen.apply(server, args).on("error", function(e) {
|
||||||
|
if (server.listenerCount("error") < 2) {
|
||||||
|
console.warn("Did not successfully create http server and bind to port '" + p + "':");
|
||||||
|
explainError(e);
|
||||||
|
process.exit(41);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function _create(port) {
|
function _create(port) {
|
||||||
if (!port) { port = 443; }
|
if (!port) {
|
||||||
|
port = 443;
|
||||||
|
}
|
||||||
|
|
||||||
var parts = String(port).split(':');
|
var parts = String(port).split(":");
|
||||||
var p = parts.pop();
|
var p = parts.pop();
|
||||||
var addr = parts.join(':').replace(/^\[/, '').replace(/\]$/, '');
|
var addr = parts
|
||||||
var args = [];
|
.join(":")
|
||||||
var httpType;
|
.replace(/^\[/, "")
|
||||||
var server;
|
.replace(/\]$/, "");
|
||||||
var validHttpPort = (parseInt(p, 10) >= 0);
|
var args = [];
|
||||||
|
var httpType;
|
||||||
|
var server;
|
||||||
|
var validHttpPort = parseInt(p, 10) >= 0;
|
||||||
|
|
||||||
if (addr) { args[1] = addr; }
|
if (addr) {
|
||||||
if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) {
|
args[1] = addr;
|
||||||
console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe");
|
}
|
||||||
}
|
if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) {
|
||||||
|
console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe");
|
||||||
|
}
|
||||||
|
|
||||||
var https;
|
var https;
|
||||||
try {
|
try {
|
||||||
https = require('spdy');
|
https = require("spdy");
|
||||||
greenlock.tlsOptions.spdy = { protocols: [ 'h2', 'http/1.1' ], plain: false };
|
greenlock.tlsOptions.spdy = { protocols: ["h2", "http/1.1"], plain: false };
|
||||||
httpType = 'http2 (spdy/h2)';
|
httpType = "http2 (spdy/h2)";
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
https = require('https');
|
https = require("https");
|
||||||
httpType = 'https';
|
httpType = "https";
|
||||||
}
|
}
|
||||||
var sniCallback = greenlock.tlsOptions.SNICallback;
|
var sniCallback = greenlock.tlsOptions.SNICallback;
|
||||||
greenlock.tlsOptions.SNICallback = function (domain, cb) {
|
greenlock.tlsOptions.SNICallback = function(domain, cb) {
|
||||||
sniCallback(domain, function (err, context) {
|
sniCallback(domain, function(err, context) {
|
||||||
cb(err, context);
|
cb(err, context);
|
||||||
|
|
||||||
if (!context || server._hasDefaultSecureContext) { return; }
|
if (!context || server._hasDefaultSecureContext) {
|
||||||
if (!domain) { domain = greenlock.servername; }
|
return;
|
||||||
if (!domain) { return; }
|
}
|
||||||
|
if (!domain) {
|
||||||
|
domain = greenlock.servername;
|
||||||
|
}
|
||||||
|
if (!domain) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return greenlock.check({ domains: [ domain ] }).then(function (certs) {
|
return greenlock
|
||||||
// ignore the case that check doesn't have all the right args here
|
.check({ domains: [domain] })
|
||||||
// to get the same certs that it just got (eventually the right ones will come in)
|
.then(function(certs) {
|
||||||
if (!certs) { return; }
|
// ignore the case that check doesn't have all the right args here
|
||||||
if (server.setSecureContext) {
|
// to get the same certs that it just got (eventually the right ones will come in)
|
||||||
// only available in node v11.0+
|
if (!certs) {
|
||||||
server.setSecureContext({
|
return;
|
||||||
key: Buffer.from(certs.privkey, 'ascii')
|
}
|
||||||
, cert: Buffer.from(certs.cert + '\r\n' + certs.chain, 'ascii')
|
if (server.setSecureContext) {
|
||||||
});
|
// only available in node v11.0+
|
||||||
console.info("Using '%s' as default certificate", domain);
|
server.setSecureContext({
|
||||||
} else {
|
key: Buffer.from(certs.privkey, "ascii"),
|
||||||
console.info("Setting default certificates dynamically requires node v11.0+. Skipping.");
|
cert: Buffer.from(certs.cert + "\r\n" + certs.chain, "ascii")
|
||||||
}
|
});
|
||||||
server._hasDefaultSecureContext = true;
|
console.info("Using '%s' as default certificate", domain);
|
||||||
}).catch(function (/*e*/) {
|
} else {
|
||||||
// this may be that the test.example.com was requested, but it's listed
|
console.info("Setting default certificates dynamically requires node v11.0+. Skipping.");
|
||||||
// on the cert for demo.example.com which is in its own directory, not the other
|
}
|
||||||
//console.warn("Unusual error: couldn't get newly authorized certificate:");
|
server._hasDefaultSecureContext = true;
|
||||||
//console.warn(e.message);
|
})
|
||||||
});
|
.catch(function(/*e*/) {
|
||||||
});
|
// this may be that the test.example.com was requested, but it's listed
|
||||||
};
|
// on the cert for demo.example.com which is in its own directory, not the other
|
||||||
if (greenlock.tlsOptions.cert) {
|
//console.warn("Unusual error: couldn't get newly authorized certificate:");
|
||||||
server._hasDefaultSecureContext = true;
|
//console.warn(e.message);
|
||||||
if (greenlock.tlsOptions.cert.toString('ascii').split("BEGIN").length < 3) {
|
});
|
||||||
console.warn("Invalid certificate file. 'tlsOptions.cert' should contain cert.pem (certificate file) *and* chain.pem (intermediate certificates) seperated by an extra newline (CRLF)");
|
});
|
||||||
}
|
};
|
||||||
}
|
if (greenlock.tlsOptions.cert) {
|
||||||
server = https.createServer(
|
server._hasDefaultSecureContext = true;
|
||||||
greenlock.tlsOptions
|
if (greenlock.tlsOptions.cert.toString("ascii").split("BEGIN").length < 3) {
|
||||||
, greenlock.middleware.sanitizeHost(function (req, res) {
|
console.warn(
|
||||||
try {
|
"Invalid certificate file. 'tlsOptions.cert' should contain cert.pem (certificate file) *and* chain.pem (intermediate certificates) seperated by an extra newline (CRLF)"
|
||||||
greenlock.app(req, res);
|
);
|
||||||
} catch(e) {
|
}
|
||||||
console.error("[error] [greenlock.app] Your HTTP handler had an uncaught error:");
|
}
|
||||||
console.error(e);
|
server = https.createServer(
|
||||||
try {
|
greenlock.tlsOptions,
|
||||||
res.statusCode = 500;
|
greenlock.middleware.sanitizeHost(function(req, res) {
|
||||||
res.end("Internal Server Error: [Greenlock] HTTP exception logged for user-provided handler.");
|
try {
|
||||||
} catch(e) {
|
greenlock.app(req, res);
|
||||||
// ignore
|
} catch (e) {
|
||||||
// (headers may have already been sent, etc)
|
console.error("[error] [greenlock.app] Your HTTP handler had an uncaught error:");
|
||||||
}
|
console.error(e);
|
||||||
}
|
try {
|
||||||
})
|
res.statusCode = 500;
|
||||||
);
|
res.end("Internal Server Error: [Greenlock] HTTP exception logged for user-provided handler.");
|
||||||
server.type = httpType;
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
// (headers may have already been sent, etc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
server.type = httpType;
|
||||||
|
|
||||||
return { server: server, listen: function () { return new PromiseA(function (resolve) {
|
return {
|
||||||
args[0] = p;
|
server: server,
|
||||||
args.push(function () { resolve(/*server*/); });
|
listen: function() {
|
||||||
server.listen.apply(server, args).on('error', function (e) {
|
return new PromiseA(function(resolve) {
|
||||||
if (server.listenerCount('error') < 2) {
|
args[0] = p;
|
||||||
console.warn("Did not successfully create http server and bind to port '" + p + "':");
|
args.push(function() {
|
||||||
explainError(e);
|
resolve(/*server*/);
|
||||||
process.exit(41);
|
});
|
||||||
}
|
server.listen.apply(server, args).on("error", function(e) {
|
||||||
});
|
if (server.listenerCount("error") < 2) {
|
||||||
}); } };
|
console.warn("Did not successfully create http server and bind to port '" + p + "':");
|
||||||
}
|
explainError(e);
|
||||||
|
process.exit(41);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: 'greenlock' is just 'opts' renamed
|
// NOTE: 'greenlock' is just 'opts' renamed
|
||||||
var greenlock = require('greenlock').create(opts);
|
var greenlock = require("greenlock").create(opts);
|
||||||
|
|
||||||
if (!opts.app) {
|
if (!opts.app) {
|
||||||
opts.app = function (req, res) {
|
opts.app = function(req, res) {
|
||||||
res.end("Hello, World!\nWith Love,\nGreenlock for Express.js");
|
res.end("Hello, World!\nWith Love,\nGreenlock for Express.js");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.listen = function (plainPort, port, fnPlain, fn) {
|
opts.listen = function(plainPort, port, fnPlain, fn) {
|
||||||
var server;
|
var server;
|
||||||
var plainServer;
|
var plainServer;
|
||||||
|
|
||||||
// If there is only one handler for the `listening` (i.e. TCP bound) event
|
// If there is only one handler for the `listening` (i.e. TCP bound) event
|
||||||
// then we want to use it as HTTPS (backwards compat)
|
// then we want to use it as HTTPS (backwards compat)
|
||||||
if (!fn) {
|
if (!fn) {
|
||||||
fn = fnPlain;
|
fn = fnPlain;
|
||||||
fnPlain = null;
|
fnPlain = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var obj1 = _createPlain(plainPort, true);
|
var obj1 = _createPlain(plainPort, true);
|
||||||
var obj2 = _create(port, false);
|
var obj2 = _create(port, false);
|
||||||
|
|
||||||
plainServer = obj1.server;
|
plainServer = obj1.server;
|
||||||
server = obj2.server;
|
server = obj2.server;
|
||||||
|
|
||||||
server.then = obj1.listen().then(function (tlsOptions) {
|
server.then = obj1.listen().then(function(tlsOptions) {
|
||||||
if (tlsOptions) {
|
if (tlsOptions) {
|
||||||
if (server.setSecureContext) {
|
if (server.setSecureContext) {
|
||||||
// only available in node v11.0+
|
// only available in node v11.0+
|
||||||
server.setSecureContext(tlsOptions);
|
server.setSecureContext(tlsOptions);
|
||||||
console.info("Using '%s' as default certificate", greenlock.servername);
|
console.info("Using '%s' as default certificate", greenlock.servername);
|
||||||
} else {
|
} else {
|
||||||
console.info("Setting default certificates dynamically requires node v11.0+. Skipping.");
|
console.info("Setting default certificates dynamically requires node v11.0+. Skipping.");
|
||||||
}
|
}
|
||||||
server._hasDefaultSecureContext = true;
|
server._hasDefaultSecureContext = true;
|
||||||
}
|
}
|
||||||
return obj2.listen().then(function () {
|
return obj2.listen().then(function() {
|
||||||
// Report plain http status
|
// Report plain http status
|
||||||
if ('function' === typeof fnPlain) {
|
if ("function" === typeof fnPlain) {
|
||||||
fnPlain.apply(plainServer);
|
fnPlain.apply(plainServer);
|
||||||
} else if (!fn && !plainServer.listenerCount('listening') && !server.listenerCount('listening')) {
|
} else if (!fn && !plainServer.listenerCount("listening") && !server.listenerCount("listening")) {
|
||||||
console.info('[:' + (plainServer.address().port || plainServer.address())
|
console.info(
|
||||||
+ "] Handling ACME challenges and redirecting to " + server.type);
|
"[:" +
|
||||||
}
|
(plainServer.address().port || plainServer.address()) +
|
||||||
|
"] Handling ACME challenges and redirecting to " +
|
||||||
|
server.type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Report h2/https status
|
// Report h2/https status
|
||||||
if ('function' === typeof fn) {
|
if ("function" === typeof fn) {
|
||||||
fn.apply(server);
|
fn.apply(server);
|
||||||
} else if (!server.listenerCount('listening')) {
|
} else if (!server.listenerCount("listening")) {
|
||||||
console.info('[:' + (server.address().port || server.address()) + "] Serving " + server.type);
|
console.info("[:" + (server.address().port || server.address()) + "] Serving " + server.type);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}).then;
|
}).then;
|
||||||
|
|
||||||
server.unencrypted = plainServer;
|
server.unencrypted = plainServer;
|
||||||
return server;
|
return server;
|
||||||
};
|
};
|
||||||
opts.middleware.acme = function (opts) {
|
opts.middleware.acme = function(opts) {
|
||||||
return greenlock.middleware.sanitizeHost(greenlock.middleware(require('redirect-https')(opts)));
|
return greenlock.middleware.sanitizeHost(greenlock.middleware(require("redirect-https")(opts)));
|
||||||
};
|
};
|
||||||
opts.middleware.secure = function (app) {
|
opts.middleware.secure = function(app) {
|
||||||
return greenlock.middleware.sanitizeHost(app);
|
return greenlock.middleware.sanitizeHost(app);
|
||||||
};
|
};
|
||||||
|
|
||||||
return greenlock;
|
return greenlock;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,37 +1,37 @@
|
||||||
'use strict';
|
"use strict";
|
||||||
|
|
||||||
function requireBluebird() {
|
function requireBluebird() {
|
||||||
try {
|
try {
|
||||||
return require('bluebird');
|
return require("bluebird");
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
console.error("");
|
console.error("");
|
||||||
console.error("DON'T PANIC. You're running an old version of node with incomplete Promise support.");
|
console.error("DON'T PANIC. You're running an old version of node with incomplete Promise support.");
|
||||||
console.error("EASY FIX: `npm install --save bluebird`");
|
console.error("EASY FIX: `npm install --save bluebird`");
|
||||||
console.error("");
|
console.error("");
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('undefined' === typeof Promise) {
|
if ("undefined" === typeof Promise) {
|
||||||
global.Promise = requireBluebird();
|
global.Promise = requireBluebird();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('function' !== typeof require('util').promisify) {
|
if ("function" !== typeof require("util").promisify) {
|
||||||
require('util').promisify = requireBluebird().promisify;
|
require("util").promisify = requireBluebird().promisify;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!console.debug) {
|
if (!console.debug) {
|
||||||
console.debug = console.log;
|
console.debug = console.log;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fs = require('fs');
|
var fs = require("fs");
|
||||||
var fsAsync = {};
|
var fsAsync = {};
|
||||||
Object.keys(fs).forEach(function (key) {
|
Object.keys(fs).forEach(function(key) {
|
||||||
var fn = fs[key];
|
var fn = fs[key];
|
||||||
if ('function' !== typeof fn || !/[a-z]/.test(key[0])) {
|
if ("function" !== typeof fn || !/[a-z]/.test(key[0])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fsAsync[key] = require('util').promisify(fn);
|
fsAsync[key] = require("util").promisify(fn);
|
||||||
});
|
});
|
||||||
|
|
||||||
exports.fsAsync = fsAsync;
|
exports.fsAsync = fsAsync;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
96
package.json
96
package.json
|
@ -1,50 +1,50 @@
|
||||||
{
|
{
|
||||||
"name": "greenlock-express",
|
"name": "greenlock-express",
|
||||||
"version": "2.7.9",
|
"version": "2.7.9",
|
||||||
"description": "Free SSL and managed or automatic HTTPS for node.js with Express, Koa, Connect, Hapi, and all other middleware systems.",
|
"description": "Free SSL and managed or automatic HTTPS for node.js with Express, Koa, Connect, Hapi, and all other middleware systems.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"homepage": "https://greenlock.domains",
|
"homepage": "https://greenlock.domains",
|
||||||
"directories": {
|
"directories": {
|
||||||
"example": "examples"
|
"example": "examples"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"greenlock": "^2.7.24",
|
"greenlock": "^2.7.24",
|
||||||
"redirect-https": "^1.1.5"
|
"redirect-https": "^1.1.5"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib"
|
"lib"
|
||||||
],
|
],
|
||||||
"trulyOptionalDependencies": {
|
"trulyOptionalDependencies": {
|
||||||
"spdy": "^3.4.7"
|
"spdy": "^3.4.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"express": "^4.16.3",
|
"express": "^4.16.3",
|
||||||
"express-basic-auth": "^1.1.5",
|
"express-basic-auth": "^1.1.5",
|
||||||
"finalhandler": "^1.1.1",
|
"finalhandler": "^1.1.1",
|
||||||
"serve-index": "^1.9.1",
|
"serve-index": "^1.9.1",
|
||||||
"serve-static": "^1.13.2",
|
"serve-static": "^1.13.2",
|
||||||
"ws": "^5.2.1"
|
"ws": "^5.2.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node server.js ./config.js",
|
"start": "node server.js ./config.js",
|
||||||
"test": "node test/greenlock.js"
|
"test": "node test/greenlock.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.rootprojects.org/root/greenlock-express.js.git"
|
"url": "https://git.rootprojects.org/root/greenlock-express.js.git"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"Let's Encrypt",
|
"Let's Encrypt",
|
||||||
"ACME",
|
"ACME",
|
||||||
"greenlock",
|
"greenlock",
|
||||||
"Free SSL",
|
"Free SSL",
|
||||||
"Automated HTTPS",
|
"Automated HTTPS",
|
||||||
"https",
|
"https",
|
||||||
"tls"
|
"tls"
|
||||||
],
|
],
|
||||||
"author": "AJ ONeal <solderjs@gmail.com> (https://solderjs.com/)",
|
"author": "AJ ONeal <solderjs@gmail.com> (https://solderjs.com/)",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://git.rootprojects.org/root/greenlock-express.js/issues"
|
"url": "https://git.rootprojects.org/root/greenlock-express.js/issues"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
336
server.js
336
server.js
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
'use strict';
|
"use strict";
|
||||||
/*global Promise*/
|
/*global Promise*/
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
|
@ -15,189 +15,219 @@
|
||||||
// ex: /srv/api/api.example.com
|
// ex: /srv/api/api.example.com
|
||||||
//
|
//
|
||||||
|
|
||||||
var configpath = process.argv[2] || './config.js';
|
var configpath = process.argv[2] || "./config.js";
|
||||||
var config = require(configpath);
|
var config = require(configpath);
|
||||||
// The prefix where sites go by name.
|
// The prefix where sites go by name.
|
||||||
// For example: whatever.com may live in /srv/www/whatever.com, thus /srv/www is our path
|
// For example: whatever.com may live in /srv/www/whatever.com, thus /srv/www is our path
|
||||||
|
|
||||||
var path = require('path');
|
var path = require("path");
|
||||||
var fs = require('./lib/compat.js').fsAsync;
|
var fs = require("./lib/compat.js").fsAsync;
|
||||||
var finalhandler = require('finalhandler');
|
var finalhandler = require("finalhandler");
|
||||||
var serveStatic = require('serve-static');
|
var serveStatic = require("serve-static");
|
||||||
|
|
||||||
//var glx = require('greenlock-express')
|
//var glx = require('greenlock-express')
|
||||||
var glx = require('./').create({
|
var glx = require("./").create({
|
||||||
|
version: "draft-11", // Let's Encrypt v2 is ACME draft 11
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
//, server: 'https://acme-staging-v02.api.letsencrypt.org/directory'
|
configDir: config.configDir, // You MUST have access to write to directory where certs
|
||||||
, server: 'https://acme-v02.api.letsencrypt.org/directory' // If at first you don't succeed, stop and switch to staging
|
// are saved. ex: /home/foouser/.config/acme
|
||||||
// https://acme-staging-v02.api.letsencrypt.org/directory
|
|
||||||
|
|
||||||
, configDir: config.configDir // You MUST have access to write to directory where certs
|
approveDomains: myApproveDomains, // Greenlock's wraps around tls.SNICallback. Check the
|
||||||
// are saved. ex: /home/foouser/.config/acme
|
// domain name here and reject invalid ones
|
||||||
|
|
||||||
, approveDomains: myApproveDomains // Greenlock's wraps around tls.SNICallback. Check the
|
app: myVhostApp, // Any node-style http app (i.e. express, koa, hapi, rill)
|
||||||
// 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: config.email, // Email for Let's Encrypt account and Greenlock Security
|
||||||
/* CHANGE TO A VALID EMAIL */
|
agreeTos: true, // Accept Let's Encrypt ToS
|
||||||
, email: config.email // Email for Let's Encrypt account and Greenlock Security
|
//, communityMember: true // Join Greenlock to get important updates, no spam
|
||||||
, agreeTos: true // Accept Let's Encrypt ToS
|
|
||||||
//, communityMember: true // Join Greenlock to get important updates, no spam
|
|
||||||
|
|
||||||
//, debug: true
|
|
||||||
, store: require('greenlock-store-fs')
|
|
||||||
|
|
||||||
|
//, debug: true
|
||||||
|
store: require("greenlock-store-fs")
|
||||||
});
|
});
|
||||||
|
|
||||||
var server = glx.listen(80, 443);
|
var server = glx.listen(80, 443);
|
||||||
server.on('listening', function () {
|
server.on("listening", function() {
|
||||||
console.info(server.type + " listening on", server.address());
|
console.info(server.type + " listening on", server.address());
|
||||||
});
|
});
|
||||||
|
|
||||||
function myApproveDomains(opts) {
|
function myApproveDomains(opts) {
|
||||||
console.info("SNI:", opts.domain);
|
console.info("SNI:", opts.domain);
|
||||||
// In this example the filesystem is our "database".
|
// In this example the filesystem is our "database".
|
||||||
// We check in /srv/www for whatever.com and if it exists, it's allowed
|
// 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
|
// SECURITY Greenlock validates opts.domains ahead-of-time so you don't have to
|
||||||
|
|
||||||
var domains = [];
|
var domains = [];
|
||||||
var domain = opts.domain.replace(/^(www|api)\./, '');
|
var domain = opts.domain.replace(/^(www|api)\./, "");
|
||||||
return checkWwws(domain).then(function (hostname) {
|
return checkWwws(domain)
|
||||||
// this is either example.com or www.example.com
|
.then(function(hostname) {
|
||||||
domains.push(hostname);
|
// this is either example.com or www.example.com
|
||||||
if ('api.' + domain !== opts.domain) {
|
domains.push(hostname);
|
||||||
if (!domains.includes(opts.domain)) {
|
if ("api." + domain !== opts.domain) {
|
||||||
domains.push(opts.domain)
|
if (!domains.includes(opts.domain)) {
|
||||||
}
|
domains.push(opts.domain);
|
||||||
}
|
}
|
||||||
}).catch(function () {
|
}
|
||||||
// ignore error
|
})
|
||||||
return null;
|
.catch(function() {
|
||||||
}).then(function () {
|
// ignore error
|
||||||
// check for api prefix
|
return null;
|
||||||
var apiname = domain;
|
})
|
||||||
if (domains.length) {
|
.then(function() {
|
||||||
apiname = 'api.' + domain;
|
// check for api prefix
|
||||||
}
|
var apiname = domain;
|
||||||
return checkApi(apiname).then(function (app) {
|
if (domains.length) {
|
||||||
if (!app) { return null; }
|
apiname = "api." + domain;
|
||||||
domains.push(apiname);
|
}
|
||||||
}).catch(function () {
|
return checkApi(apiname)
|
||||||
return null;
|
.then(function(app) {
|
||||||
});
|
if (!app) {
|
||||||
}).then(function () {
|
return null;
|
||||||
if (0 === domains.length) {
|
}
|
||||||
return Promise.reject(new Error("no bare, www., or api. domain matching '" + opts.domain + "'"));
|
domains.push(apiname);
|
||||||
}
|
})
|
||||||
|
.catch(function() {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
if (0 === domains.length) {
|
||||||
|
return Promise.reject(new Error("no bare, www., or api. domain matching '" + opts.domain + "'"));
|
||||||
|
}
|
||||||
|
|
||||||
console.info('Approved domains:', domains);
|
console.info("Approved domains:", domains);
|
||||||
opts.domains = domains;
|
opts.domains = domains;
|
||||||
//opts.email = email;
|
//opts.email = email;
|
||||||
opts.agreeTos = true;
|
opts.agreeTos = true;
|
||||||
// pick the shortest (bare) or latest (www. instead of api.) to be the subject
|
// pick the shortest (bare) or latest (www. instead of api.) to be the subject
|
||||||
opts.subject = opts.domains.sort(function (a, b) {
|
opts.subject = opts.domains.sort(function(a, b) {
|
||||||
var len = a.length - b.length;
|
var len = a.length - b.length;
|
||||||
if (0 !== len) { return len; }
|
if (0 !== len) {
|
||||||
if (a < b) { return 1; } else { return -1; }
|
return len;
|
||||||
})[0];
|
}
|
||||||
|
if (a < b) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
})[0];
|
||||||
|
|
||||||
if (!opts.challenges) { opts.challenges = {}; }
|
if (!opts.challenges) {
|
||||||
opts.challenges['http-01'] = require('le-challenge-fs');
|
opts.challenges = {};
|
||||||
//opts.challenges['dns-01'] = require('le-challenge-dns');
|
}
|
||||||
|
opts.challenges["http-01"] = require("le-challenge-fs");
|
||||||
|
//opts.challenges['dns-01'] = require('le-challenge-dns');
|
||||||
|
|
||||||
// explicitly set account id and certificate.id
|
// explicitly set account id and certificate.id
|
||||||
opts.account = { id: opts.email };
|
opts.account = { id: opts.email };
|
||||||
opts.certificate = { id: opts.subject };
|
opts.certificate = { id: opts.subject };
|
||||||
|
|
||||||
return Promise.resolve(opts);
|
return Promise.resolve(opts);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkApi(hostname) {
|
function checkApi(hostname) {
|
||||||
var apipath = path.join(config.api, hostname);
|
var apipath = path.join(config.api, hostname);
|
||||||
var link = '';
|
var link = "";
|
||||||
return fs.stat(apipath).then(function (stats) {
|
return fs
|
||||||
if (stats.isDirectory()) {
|
.stat(apipath)
|
||||||
return require(apipath);
|
.then(function(stats) {
|
||||||
}
|
if (stats.isDirectory()) {
|
||||||
return fs.readFile(apipath, 'utf8').then(function (txt) {
|
return require(apipath);
|
||||||
var linkpath = txt.split('\n')[0];
|
}
|
||||||
link = (' => ' + linkpath + ' ');
|
return fs.readFile(apipath, "utf8").then(function(txt) {
|
||||||
return require(linkpath);
|
var linkpath = txt.split("\n")[0];
|
||||||
});
|
link = " => " + linkpath + " ";
|
||||||
}).catch(function (e) {
|
return require(linkpath);
|
||||||
if ('ENOENT' === e.code) { return null; }
|
});
|
||||||
console.error(e);
|
})
|
||||||
throw new Error("rejecting '" + hostname + "' because '" + apipath + link + "' failed at require()");
|
.catch(function(e) {
|
||||||
});
|
if ("ENOENT" === e.code) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
console.error(e);
|
||||||
|
throw new Error("rejecting '" + hostname + "' because '" + apipath + link + "' failed at require()");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkWwws(_hostname) {
|
function checkWwws(_hostname) {
|
||||||
if (!_hostname) {
|
if (!_hostname) {
|
||||||
// SECURITY don't serve the whole config.srv
|
// SECURITY don't serve the whole config.srv
|
||||||
return Promise.reject(new Error("missing hostname"));
|
return Promise.reject(new Error("missing hostname"));
|
||||||
}
|
}
|
||||||
var hostname = _hostname;
|
var hostname = _hostname;
|
||||||
var hostdir = path.join(config.srv, hostname);
|
var hostdir = path.join(config.srv, hostname);
|
||||||
// TODO could test for www/no-www both in directory
|
// TODO could test for www/no-www both in directory
|
||||||
return fs.readdir(hostdir).then(function () {
|
return fs
|
||||||
// TODO check for some sort of htaccess.json and use email in that
|
.readdir(hostdir)
|
||||||
// NOTE: you can also change other options such as `challengeType` and `challenge`
|
.then(function() {
|
||||||
// opts.challengeType = 'http-01';
|
// TODO check for some sort of htaccess.json and use email in that
|
||||||
// opts.challenge = require('le-challenge-fs').create({});
|
// NOTE: you can also change other options such as `challengeType` and `challenge`
|
||||||
return hostname;
|
// opts.challengeType = 'http-01';
|
||||||
}).catch(function () {
|
// opts.challenge = require('le-challenge-fs').create({});
|
||||||
if ('www.' === hostname.slice(0, 4)) {
|
return hostname;
|
||||||
// Assume we'll redirect to non-www if it's available.
|
})
|
||||||
hostname = hostname.slice(4);
|
.catch(function() {
|
||||||
hostdir = path.join(config.srv, hostname);
|
if ("www." === hostname.slice(0, 4)) {
|
||||||
return fs.readdir(hostdir).then(function () {
|
// Assume we'll redirect to non-www if it's available.
|
||||||
return hostname;
|
hostname = hostname.slice(4);
|
||||||
});
|
hostdir = path.join(config.srv, hostname);
|
||||||
} else {
|
return fs.readdir(hostdir).then(function() {
|
||||||
// Or check and see if perhaps we should redirect non-www to www
|
return hostname;
|
||||||
hostname = 'www.' + hostname;
|
});
|
||||||
hostdir = path.join(config.srv, hostname);
|
} else {
|
||||||
return fs.readdir(hostdir).then(function () {
|
// Or check and see if perhaps we should redirect non-www to www
|
||||||
return hostname;
|
hostname = "www." + hostname;
|
||||||
});
|
hostdir = path.join(config.srv, hostname);
|
||||||
}
|
return fs.readdir(hostdir).then(function() {
|
||||||
}).catch(function () {
|
return hostname;
|
||||||
throw new Error("rejecting '" + _hostname + "' because '" + hostdir + "' could not be read");
|
});
|
||||||
});
|
}
|
||||||
|
})
|
||||||
|
.catch(function() {
|
||||||
|
throw new Error("rejecting '" + _hostname + "' because '" + hostdir + "' could not be read");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function myVhostApp(req, res) {
|
function myVhostApp(req, res) {
|
||||||
// SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to
|
// SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to
|
||||||
// (also: only domains approved above will get here)
|
// (also: only domains approved above will get here)
|
||||||
console.info(req.method, (req.headers.host||'') + req.url);
|
console.info(req.method, (req.headers.host || "") + req.url);
|
||||||
Object.keys(req.headers).forEach(function (key) {
|
Object.keys(req.headers).forEach(function(key) {
|
||||||
console.info(key, req.headers[key])
|
console.info(key, req.headers[key]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// We could cache wether or not a host exists for some amount of time
|
// We could cache wether or not a host exists for some amount of time
|
||||||
var fin = finalhandler(req, res);
|
var fin = finalhandler(req, res);
|
||||||
return checkWwws(req.headers.host).then(function (hostname) {
|
return checkWwws(req.headers.host)
|
||||||
if (hostname !== req.headers.host) {
|
.then(function(hostname) {
|
||||||
res.statusCode = 302;
|
if (hostname !== req.headers.host) {
|
||||||
res.setHeader('Location', 'https://' + hostname);
|
res.statusCode = 302;
|
||||||
// SECURITY this is safe only because greenlock disallows invalid hostnames
|
res.setHeader("Location", "https://" + hostname);
|
||||||
res.end("<!-- redirecting to https://" + hostname + "-->");
|
// SECURITY this is safe only because greenlock disallows invalid hostnames
|
||||||
return;
|
res.end("<!-- redirecting to https://" + hostname + "-->");
|
||||||
}
|
return;
|
||||||
var serve = serveStatic(path.join(config.srv, hostname), { redirect: true });
|
}
|
||||||
serve(req, res, fin);
|
var serve = serveStatic(path.join(config.srv, hostname), { redirect: true });
|
||||||
}).catch(function (err) {
|
serve(req, res, fin);
|
||||||
return checkApi(req.headers.host).then(function (app) {
|
})
|
||||||
if (app) { app(req, res); return; }
|
.catch(function(err) {
|
||||||
console.error("none found", err);
|
return checkApi(req.headers.host)
|
||||||
fin();
|
.then(function(app) {
|
||||||
}).catch(function (err) {
|
if (app) {
|
||||||
console.error("api crashed error", err);
|
app(req, res);
|
||||||
fin(err);
|
return;
|
||||||
});
|
}
|
||||||
});
|
console.error("none found", err);
|
||||||
|
fin();
|
||||||
|
})
|
||||||
|
.catch(function(err) {
|
||||||
|
console.error("api crashed error", err);
|
||||||
|
fin(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,75 +1,85 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
var Greenlock = require('../');
|
var Greenlock = require("../");
|
||||||
var greenlock = Greenlock.create({
|
var greenlock = Greenlock.create({
|
||||||
version: 'draft-11'
|
version: "draft-11",
|
||||||
, server: 'https://acme-staging-v02.api.letsencrypt.org/directory'
|
server: "https://acme-staging-v02.api.letsencrypt.org/directory",
|
||||||
, agreeTos: true
|
agreeTos: true,
|
||||||
, approvedDomains: [ 'example.com', 'www.example.com' ]
|
approvedDomains: ["example.com", "www.example.com"],
|
||||||
, configDir: require('path').join(require('os').tmpdir(), 'acme')
|
configDir: require("path").join(require("os").tmpdir(), "acme"),
|
||||||
|
|
||||||
, app: require('express')().use('/', function (req, res) {
|
app: require("express")().use("/", function(req, res) {
|
||||||
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||||
res.end('Hello, World!\n\n💚 🔒.js');
|
res.end("Hello, World!\n\n💚 🔒.js");
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
var server1 = greenlock.listen(5080, 5443);
|
var server1 = greenlock.listen(5080, 5443);
|
||||||
server1.on('listening', function () {
|
server1.on("listening", function() {
|
||||||
console.log("### THREE 3333 - All is well server1", this.address());
|
console.log("### THREE 3333 - All is well server1", this.address());
|
||||||
setTimeout(function () {
|
setTimeout(function() {
|
||||||
// so that the address() object doesn't disappear
|
// so that the address() object doesn't disappear
|
||||||
server1.close();
|
server1.close();
|
||||||
server1.unencrypted.close();
|
server1.unencrypted.close();
|
||||||
}, 10);
|
}, 10);
|
||||||
});
|
});
|
||||||
setTimeout(function () {
|
setTimeout(function() {
|
||||||
var server2 = greenlock.listen(6080, 6443, function () {
|
var server2 = greenlock.listen(6080, 6443, function() {
|
||||||
console.log("### FIVE 55555 - Started server 2!");
|
console.log("### FIVE 55555 - Started server 2!");
|
||||||
setTimeout(function () {
|
setTimeout(function() {
|
||||||
server2.close();
|
server2.close();
|
||||||
server2.unencrypted.close();
|
server2.unencrypted.close();
|
||||||
server6.close();
|
server6.close();
|
||||||
server6.unencrypted.close();
|
server6.unencrypted.close();
|
||||||
server7.close();
|
server7.close();
|
||||||
server7.unencrypted.close();
|
server7.unencrypted.close();
|
||||||
setTimeout(function () {
|
setTimeout(function() {
|
||||||
// TODO greenlock needs a close event (and to listen to its server's close event)
|
// TODO greenlock needs a close event (and to listen to its server's close event)
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
server2.on('listening', function () {
|
server2.on("listening", function() {
|
||||||
console.log("### FOUR 44444 - All is well server2", server2.address());
|
console.log("### FOUR 44444 - All is well server2", server2.address());
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
var server3 = greenlock.listen(22, 22, function () {
|
var server3 = greenlock.listen(
|
||||||
console.error("Error: expected to get an error when launching plain server on port 22");
|
22,
|
||||||
}, function () {
|
22,
|
||||||
console.error("Error: expected to get an error when launching " + server3.type + " server on port 22");
|
function() {
|
||||||
|
console.error("Error: expected to get an error when launching plain server on port 22");
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
console.error("Error: expected to get an error when launching " + server3.type + " server on port 22");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
server3.unencrypted.on("error", function() {
|
||||||
|
console.log("Success: caught expected (plain) error");
|
||||||
});
|
});
|
||||||
server3.unencrypted.on('error', function () {
|
server3.on("error", function() {
|
||||||
console.log("Success: caught expected (plain) error");
|
console.log("Success: caught expected " + server3.type + " error");
|
||||||
});
|
//server3.close();
|
||||||
server3.on('error', function () {
|
|
||||||
console.log("Success: caught expected " + server3.type + " error");
|
|
||||||
//server3.close();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var server4 = greenlock.listen(7080, 7443, function () {
|
var server4 = greenlock.listen(
|
||||||
console.log('Success: server4: plain');
|
7080,
|
||||||
server4.unencrypted.close();
|
7443,
|
||||||
}, function () {
|
function() {
|
||||||
console.log('Success: server4: ' + server4.type);
|
console.log("Success: server4: plain");
|
||||||
server4.close();
|
server4.unencrypted.close();
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
console.log("Success: server4: " + server4.type);
|
||||||
|
server4.close();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var server5 = greenlock.listen(10080, 10443, function() {
|
||||||
|
console.log("Server 5 with one fn", this.address());
|
||||||
|
server5.close();
|
||||||
|
server5.unencrypted.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
var server5 = greenlock.listen(10080, 10443, function () {
|
var server6 = greenlock.listen("[::]:11080", "[::1]:11443");
|
||||||
console.log("Server 5 with one fn", this.address());
|
|
||||||
server5.close();
|
|
||||||
server5.unencrypted.close();
|
|
||||||
});
|
|
||||||
|
|
||||||
var server6 = greenlock.listen('[::]:11080', '[::1]:11443');
|
var server7 = greenlock.listen("/tmp/gl.plain.sock", "/tmp/gl.sec.sock");
|
||||||
|
|
||||||
var server7 = greenlock.listen('/tmp/gl.plain.sock', '/tmp/gl.sec.sock');
|
|
||||||
|
|
Loading…
Reference in New Issue