mirror of
				https://github.com/therootcompany/greenlock-express.js.git
				synced 2024-11-16 17:28:59 +00:00 
			
		
		
		
	make Prettier
This commit is contained in:
		
							parent
							
								
									a49ccb7398
								
							
						
					
					
						commit
						c73ad565a3
					
				
							
								
								
									
										7
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | |||||||
							
								
								
									
										1182
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1182
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user