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 | ||||
| 
 | ||||
|   - [x] Automatic HTTPS | ||||
|     - [x] Free SSL | ||||
|     - [x] Free Wildcard SSL | ||||
|     - [x] Multiple domain support (up to 100 altnames per SAN) | ||||
|     - [x] Dynamic Virtual Hosting (vhost) | ||||
|     - [x] Automatical renewal (10 to 14 days before expiration) | ||||
|   - [x] Great ACME support | ||||
|     - [x] ACME draft 11 | ||||
|     - [x] Let's Encrypt v2 | ||||
|     - [x] Let's Encrypt v1 | ||||
|   - [x] Full node.js support | ||||
|     - [x] node v6+ | ||||
|     - [x] core https module | ||||
|     - [x] Express.js | ||||
|     - [x] [Koa](https://git.rootprojects.org/root/greenlock-koa.js) | ||||
|     - [x] [hapi](https://git.rootprojects.org/root/greenlock-hapi.js) | ||||
|   - [x] Extensible Plugin Support | ||||
|     - [x] AWS (S3, Route53) | ||||
|     - [x] Azure | ||||
|     - [x] CloudFlare | ||||
|     - [x] Consul | ||||
|     - [x] Digital Ocean | ||||
|     - [x] etcd | ||||
|     - [x] Redis | ||||
| - [x] Automatic HTTPS | ||||
|   - [x] Free SSL | ||||
|   - [x] Free Wildcard SSL | ||||
|   - [x] Multiple domain support (up to 100 altnames per SAN) | ||||
|   - [x] Dynamic Virtual Hosting (vhost) | ||||
|   - [x] Automatical renewal (10 to 14 days before expiration) | ||||
| - [x] Great ACME support | ||||
|   - [x] ACME draft 11 | ||||
|   - [x] Let's Encrypt v2 | ||||
|   - [x] Let's Encrypt v1 | ||||
| - [x] Full node.js support | ||||
|   - [x] node v6+ | ||||
|   - [x] core https module | ||||
|   - [x] Express.js | ||||
|   - [x] [Koa](https://git.rootprojects.org/root/greenlock-koa.js) | ||||
|   - [x] [hapi](https://git.rootprojects.org/root/greenlock-hapi.js) | ||||
| - [x] Extensible Plugin Support | ||||
|   - [x] AWS (S3, Route53) | ||||
|   - [x] Azure | ||||
|   - [x] CloudFlare | ||||
|   - [x] Consul | ||||
|   - [x] Digital Ocean | ||||
|   - [x] etcd | ||||
|   - [x] Redis | ||||
| 
 | ||||
| # 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> | ||||
| 
 | ||||
| * [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 | ||||
| * [6:37](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk?t=397) - Troubleshooting / Gotchas | ||||
| - [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 | ||||
| - [6:37](https://www.youtube.com/watch?v=e8vaR4CEZ5s&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk?t=397) - Troubleshooting / Gotchas | ||||
| 
 | ||||
| #### 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 | ||||
| * [2:26](https://www.youtube.com/watch?v=bTEn93gxY50&index=2&list=PLZaEVINf2Bq_lrS-OOzTUJB4q3HxarlXk&t=146) - The `approveDomains` callback | ||||
| - [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 | ||||
| 
 | ||||
| #### 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 | ||||
| 
 | ||||
| @ -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. | ||||
| 
 | ||||
| `server.js`: | ||||
| 
 | ||||
| ```javascript | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| require('greenlock-express').create({ | ||||
|   email: 'john.doe@example.com'     // The email address of the ACME user / hosting provider | ||||
| , agreeTos: true                    // You must accept the ToS as the host which handles the certs | ||||
| , configDir: '~/.config/acme/'      // Writable directory where certs will be saved | ||||
| , communityMember: true             // Join the community to get notified of important updates | ||||
| , telemetry: true                   // Contribute telemetry data to the project | ||||
| require("greenlock-express") | ||||
| 	.create({ | ||||
| 		email: "john.doe@example.com", // The email address of the ACME user / hosting provider | ||||
| 		agreeTos: true, // You must accept the ToS as the host which handles the certs | ||||
| 		configDir: "~/.config/acme/", // Writable directory where certs will be saved | ||||
| 		communityMember: true, // Join the community to get notified of important updates | ||||
| 		telemetry: true, // Contribute telemetry data to the project | ||||
| 
 | ||||
|   // Using your express app: | ||||
|   // simply export it as-is, then include it here | ||||
| , app: require('./app.js') | ||||
| 		// Using your express app: | ||||
| 		// simply export it as-is, then include it here | ||||
| 		app: require("./app.js") | ||||
| 
 | ||||
| //, debug: true | ||||
| }).listen(80, 443); | ||||
| 		//, debug: true | ||||
| 	}) | ||||
| 	.listen(80, 443); | ||||
| ``` | ||||
| 
 | ||||
| `app.js`: | ||||
| ```js | ||||
| 'use strict'; | ||||
| 
 | ||||
| var express = require('express'); | ||||
| ```js | ||||
| "use strict"; | ||||
| 
 | ||||
| var express = require("express"); | ||||
| var app = express(); | ||||
| 
 | ||||
| app.use('/', function (req, res) { | ||||
|   res.setHeader('Content-Type', 'text/html; charset=utf-8') | ||||
|   res.end('Hello, World!\n\n💚 🔒.js'); | ||||
| }) | ||||
| app.use("/", function(req, res) { | ||||
| 	res.setHeader("Content-Type", "text/html; charset=utf-8"); | ||||
| 	res.end("Hello, World!\n\n💚 🔒.js"); | ||||
| }); | ||||
| 
 | ||||
| // Don't do this: | ||||
| // app.listen(3000) | ||||
| @ -166,29 +170,29 @@ You can see our full privacy policy at <https://greenlock.domains/legal/#privacy | ||||
| 
 | ||||
| Double check the following: | ||||
| 
 | ||||
| * **Public Facing IP** for `http-01` challenges | ||||
|   * 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)? | ||||
|   * If you're on a non-public server, are you using the `dns-01` challenge? | ||||
| * **correct ACME version** | ||||
|   * Let's Encrypt **v2** (ACME v2) must use `version: 'draft-11'` | ||||
|   * Let's Encrypt v1 must use `version: 'v01'` | ||||
| * **valid email** | ||||
|   * You MUST set `email` to a **valid address** | ||||
|   * MX records must validate (`dig MX example.com` for `'john@example.com'`) | ||||
| * **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' ]`) | ||||
| * **write access** | ||||
|   * You MUST set `configDir` to a writeable location (test with `touch ~/acme/etc/tmp.tmp`) | ||||
| * **port binding privileges** | ||||
|   * 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) | ||||
| * **API limits** | ||||
|   * You MUST NOT exceed the API [**usage limits**](https://letsencrypt.org/docs/staging-environment/) per domain, certificate, IP address, etc | ||||
| * **Red Lock, Untrusted** | ||||
|   * You MUST use the **production** server url, not staging | ||||
|   * The API URL should not have 'acme-staging-v02', but should have 'acme-v02' | ||||
|   * Delete the `configDir` used for getting certificates in staging | ||||
| - **Public Facing IP** for `http-01` challenges | ||||
|   - 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)? | ||||
|   - If you're on a non-public server, are you using the `dns-01` challenge? | ||||
| - **correct ACME version** | ||||
|   - Let's Encrypt **v2** (ACME v2) must use `version: 'draft-11'` | ||||
|   - Let's Encrypt v1 must use `version: 'v01'` | ||||
| - **valid email** | ||||
|   - You MUST set `email` to a **valid address** | ||||
|   - MX records must validate (`dig MX example.com` for `'john@example.com'`) | ||||
| - **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' ]`) | ||||
| - **write access** | ||||
|   - You MUST set `configDir` to a writeable location (test with `touch ~/acme/etc/tmp.tmp`) | ||||
| - **port binding privileges** | ||||
|   - 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) | ||||
| - **API limits** | ||||
|   - You MUST NOT exceed the API [**usage limits**](https://letsencrypt.org/docs/staging-environment/) per domain, certificate, IP address, etc | ||||
| - **Red Lock, Untrusted** | ||||
|   - You MUST use the **production** server url, not staging | ||||
|   - The API URL should not have 'acme-staging-v02', but should have 'acme-v02' | ||||
|   - Delete the `configDir` used for getting certificates in staging | ||||
| 
 | ||||
| ### Production vs Staging | ||||
| 
 | ||||
| @ -210,19 +214,19 @@ https://acme-staging-v02.api.letsencrypt.org/directory | ||||
| 
 | ||||
| ## Working Examples | ||||
| 
 | ||||
| | 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. | | ||||
| | 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. | | ||||
| | 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. | | ||||
| | 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). | | ||||
| | 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). | | ||||
| | - | 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 | | ||||
| |        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.                                          | | ||||
| |      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.                                             | | ||||
| | 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.                                      | | ||||
| |   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).                             | | ||||
| | 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).                         | | ||||
| |           -           |                                                                    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                                                                                         | | ||||
| 
 | ||||
| # Plugins | ||||
| 
 | ||||
| @ -230,51 +234,49 @@ https://acme-staging-v02.api.letsencrypt.org/directory | ||||
| 
 | ||||
| ## HTTP-01 Challenges | ||||
| 
 | ||||
| |                | Plugin    | | ||||
| |:--------------:|:---------:| | ||||
| | **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) | | ||||
| | 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) | | ||||
| | - | 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) | | ||||
| 
 | ||||
| |                  |                                                                              Plugin                                                                               | | ||||
| | :--------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------: | | ||||
| | **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)                                             | | ||||
| |      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)                               | | ||||
| |        -         |                            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) | | ||||
| 
 | ||||
| ## DNS-01 Challenges | ||||
| 
 | ||||
| |                | Plugin    | | ||||
| |:--------------:|:---------:| | ||||
| | **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) | | ||||
| | CloudFlare     | [buschtoens/le-challenge-cloudflare](https://github.com/buschtoens/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) | | ||||
| | 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) | | ||||
| | 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 | | ||||
| |                  |                                                                   Plugin                                                                   | | ||||
| | :--------------: | :----------------------------------------------------------------------------------------------------------------------------------------: | | ||||
| | **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)                      | | ||||
| |    CloudFlare    |                        [buschtoens/le-challenge-cloudflare](https://github.com/buschtoens/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)                           | | ||||
| |       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)                     | | ||||
| |    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 | ||||
| 
 | ||||
| |                | Plugin    | | ||||
| |:--------------:|:---------:| | ||||
| | **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) | | ||||
| | 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) | | ||||
| | 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) | | ||||
| | - | 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 | | ||||
| |                      |                                                Plugin                                                 | | ||||
| | :------------------: | :---------------------------------------------------------------------------------------------------: | | ||||
| |     **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)               | | ||||
| |        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)      | | ||||
| |      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)            | | ||||
| |          -           | 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                  | | ||||
| 
 | ||||
| ## Auto-SNI | ||||
| 
 | ||||
| |             | Plugin    | | ||||
| |:-----------:|:---------:| | ||||
| |             |                             Plugin                              | | ||||
| | :---------: | :-------------------------------------------------------------: | | ||||
| | **Default** | [le-sni-auto](https://git.coolaj86.com/coolaj86/le-sni-auto.js) | | ||||
| 
 | ||||
| (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. | ||||
| 
 | ||||
| # Usage | ||||
| @ -300,34 +302,35 @@ node greenlock-express.js/examples/normal.js | ||||
| It looks a little more like this: | ||||
| 
 | ||||
| `serve.js`: | ||||
| 
 | ||||
| ```javascript | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| // returns an instance of greenlock.js with additional helper methods | ||||
| var glx = require('greenlock-express').create({ | ||||
|   server: 'https://acme-v02.api.letsencrypt.org/directory' | ||||
|   // Note: If at first you don't succeed, stop and switch to staging: | ||||
|   // https://acme-staging-v02.api.letsencrypt.org/directory | ||||
| , version: 'draft-11' // Let's Encrypt v2 (ACME v2) | ||||
| var glx = require("greenlock-express").create({ | ||||
| 	server: "https://acme-v02.api.letsencrypt.org/directory", | ||||
| 	// Note: If at first you don't succeed, stop and switch to staging: | ||||
| 	// https://acme-staging-v02.api.letsencrypt.org/directory | ||||
| 	version: "draft-11", // Let's Encrypt v2 (ACME v2) | ||||
| 
 | ||||
|   // If you wish to replace the default account and domain key storage plugin | ||||
| , store: require('le-store-certbot').create({ | ||||
|     configDir: require('path').join(require('os').homedir(), 'acme', 'etc') | ||||
|   , webrootPath: '/tmp/acme-challenges' | ||||
|   }) | ||||
| 	// If you wish to replace the default account and domain key storage plugin | ||||
| 	store: require("le-store-certbot").create({ | ||||
| 		configDir: require("path").join(require("os").homedir(), "acme", "etc"), | ||||
| 		webrootPath: "/tmp/acme-challenges" | ||||
| 	}), | ||||
| 
 | ||||
|   // Contribute telemetry data to the project | ||||
| , telemetry: true | ||||
| 	// Contribute telemetry data to the project | ||||
| 	telemetry: true, | ||||
| 
 | ||||
|   // the default servername to use when the client doesn't specify | ||||
|   // (because some IoT devices don't support servername indication) | ||||
| , servername: 'example.com' | ||||
| 	// the default servername to use when the client doesn't specify | ||||
| 	// (because some IoT devices don't support servername indication) | ||||
| 	servername: "example.com", | ||||
| 
 | ||||
| , approveDomains: approveDomains | ||||
| 	approveDomains: approveDomains | ||||
| }); | ||||
| 
 | ||||
| var server = glx.listen(80, 443, function () { | ||||
|   console.log("Listening on port 80 for ACME challenges and 443 for express app."); | ||||
| var server = glx.listen(80, 443, function() { | ||||
| 	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`). | ||||
| For security, domain validation MUST have an approval callback in *production*. | ||||
| For security, domain validation MUST have an approval callback in _production_. | ||||
| 
 | ||||
| ```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) { | ||||
|   // This is where you check your database and associated | ||||
|   // email addresses with domains and agreements and such | ||||
|   // if (!isAllowed(opts.domains)) { return cb(new Error("not allowed")); } | ||||
| 	// This is where you check your database and associated | ||||
| 	// email addresses with domains and agreements and such | ||||
| 	// if (!isAllowed(opts.domains)) { return cb(new Error("not allowed")); } | ||||
| 
 | ||||
|   // 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) | ||||
| 	// 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) | ||||
| 
 | ||||
|   // Opt-in to submit stats and get important updates | ||||
|   opts.communityMember = true; | ||||
| 	// Opt-in to submit stats and get important updates | ||||
| 	opts.communityMember = true; | ||||
| 
 | ||||
|   // If you wish to replace the default challenge plugin, you may do so here | ||||
|   opts.challenges = { 'http-01': http01 }; | ||||
| 	// If you wish to replace the default challenge plugin, you may do so here | ||||
| 	opts.challenges = { "http-01": http01 }; | ||||
| 
 | ||||
|   opts.email = 'john.doe@example.com'; | ||||
|   opts.agreeTos = true; | ||||
| 	opts.email = "john.doe@example.com"; | ||||
| 	opts.agreeTos = true; | ||||
| 
 | ||||
|   // NOTE: you can also change other options such as `challengeType` and `challenge` | ||||
|   // opts.challengeType = 'http-01'; | ||||
|   // opts.challenge = require('le-challenge-fs').create({}); | ||||
| 	// NOTE: you can also change other options such as `challengeType` and `challenge` | ||||
| 	// opts.challengeType = 'http-01'; | ||||
| 	// opts.challenge = require('le-challenge-fs').create({}); | ||||
| 
 | ||||
|   cb(null, { options: opts, certs: certs }); | ||||
| 	cb(null, { options: opts, certs: certs }); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ```javascript | ||||
| // handles acme-challenge and redirects to https | ||||
| require('http').createServer(glx.middleware(require('redirect-https')())).listen(80, function () { | ||||
|   console.log("Listening for ACME http-01 challenges on", this.address()); | ||||
| }); | ||||
| require("http") | ||||
| 	.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) { | ||||
|   res.end('Hello, World!'); | ||||
| var app = require("express")(); | ||||
| app.use("/", function(req, res) { | ||||
| 	res.end("Hello, World!"); | ||||
| }); | ||||
| 
 | ||||
| // handles your app | ||||
| require('https').createServer(glx.httpsOptions, app).listen(443, function () { | ||||
|   console.log("Listening for ACME tls-sni-01 challenges and serve app on", this.address()); | ||||
| }); | ||||
| require("https") | ||||
| 	.createServer(glx.httpsOptions, app) | ||||
| 	.listen(443, function() { | ||||
| 		console.log("Listening for ACME tls-sni-01 challenges and serve app on", this.address()); | ||||
| 	}); | ||||
| ``` | ||||
| 
 | ||||
| **Security**: | ||||
| @ -395,7 +399,6 @@ require('https').createServer(glx.httpsOptions, app).listen(443, function () { | ||||
| Greenlock will do a self-check on all domain registrations | ||||
| to prevent you from hitting rate limits. | ||||
| 
 | ||||
| 
 | ||||
| # API | ||||
| 
 | ||||
| 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: | ||||
| 
 | ||||
| * `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. | ||||
|   * `listen(80, 443)` | ||||
|   * `listen(80, 443, onListenSecure)` | ||||
|   * `listen(80, 443, onListenPlain, onListenSecure)` | ||||
|   * `listen('localhost:80', '0.0.0.0:443')` | ||||
|   * `listen('[::1]:80', '[::]:443')` | ||||
|   * `listen('/tmp/glx.plain.sock', '/tmp/glx.secure.sock')` | ||||
| - `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. | ||||
|   - `listen(80, 443)` | ||||
|   - `listen(80, 443, onListenSecure)` | ||||
|   - `listen(80, 443, onListenPlain, onListenSecure)` | ||||
|   - `listen('localhost:80', '0.0.0.0:443')` | ||||
|   - `listen('[::1]:80', '[::]:443')` | ||||
|   - `listen('/tmp/glx.plain.sock', '/tmp/glx.secure.sock')` | ||||
| 
 | ||||
| Brief overview of some simple options for `greenlock.js`: | ||||
| 
 | ||||
| * `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.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.communityMember` Join the community to get notified of important updates and help make greenlock better | ||||
| * `opts.approveDomains` can be either of: | ||||
|   * 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) | ||||
| * `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.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.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.communityMember` Join the community to get notified of important updates and help make greenlock better | ||||
| - `opts.approveDomains` can be either of: | ||||
|   - 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) | ||||
| - `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. | ||||
| 
 | ||||
| ## Supported ACME versions | ||||
| 
 | ||||
| * Let's Encrypt v1 (aka v01) | ||||
| * Let's Encrypt v2 (aka v02 or ACME draft 11) | ||||
| * ACME draft 11 (ACME v2 is a misnomer) | ||||
| * Wildcard domains (via dns-01 challenges) | ||||
|   * `*.example.com` | ||||
| - Let's Encrypt v1 (aka v01) | ||||
| - Let's Encrypt v2 (aka v02 or ACME draft 11) | ||||
| - ACME draft 11 (ACME v2 is a misnomer) | ||||
| - Wildcard domains (via dns-01 challenges) | ||||
|   - `*.example.com` | ||||
| 
 | ||||
| <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 = { | ||||
|   email: 'jon.doe@example.com' | ||||
| , configDir: path.join(__dirname, 'acme') | ||||
| , srv: '/srv/www/' | ||||
| , api: '/srv/api/' | ||||
| 	email: "jon.doe@example.com", | ||||
| 	configDir: path.join(__dirname, "acme"), | ||||
| 	srv: "/srv/www/", | ||||
| 	api: "/srv/api/" | ||||
| }; | ||||
|  | ||||
| @ -1,59 +1,56 @@ | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| // npm install spdy@3.x
 | ||||
| 
 | ||||
| //var Greenlock = require('greenlock-express')
 | ||||
| var Greenlock = require('../'); | ||||
| var Greenlock = require("../"); | ||||
| 
 | ||||
| var greenlock = Greenlock.create({ | ||||
| 	// Let's Encrypt v2 is ACME draft 11
 | ||||
| 	version: "draft-11", | ||||
| 
 | ||||
|   // Let's Encrypt v2 is ACME draft 11
 | ||||
|   version: 'draft-11' | ||||
| 	server: "https://acme-v02.api.letsencrypt.org/directory", | ||||
| 	// Note: If at first you don't succeed, stop and switch to staging
 | ||||
| 	// https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 
 | ||||
| , server: 'https://acme-v02.api.letsencrypt.org/directory' | ||||
|   // Note: If at first you don't succeed, stop and switch to staging
 | ||||
|   // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 	// You MUST change this to a valid email address
 | ||||
| 	email: "jon@example.com", | ||||
| 
 | ||||
|   // You MUST change this to a valid email address
 | ||||
| , email: 'jon@example.com' | ||||
| 	// You MUST NOT build clients that accept the ToS without asking the user
 | ||||
| 	agreeTos: true, | ||||
| 
 | ||||
|   // You MUST NOT build clients that accept the ToS without asking the user
 | ||||
| , agreeTos: true | ||||
| 	// You MUST change these to valid domains
 | ||||
| 	// NOTE: all domains will validated and listed on the certificate
 | ||||
| 	approvedDomains: ["example.com", "www.example.com"], | ||||
| 
 | ||||
|   // You MUST change these to valid domains
 | ||||
|   // NOTE: all domains will validated and listed on the certificate
 | ||||
| , approvedDomains: [ 'example.com', 'www.example.com' ] | ||||
| 	// You MUST have access to write to directory where certs are saved
 | ||||
| 	// ex: /home/foouser/acme/etc
 | ||||
| 	configDir: "~/.config/acme/", | ||||
| 
 | ||||
|   // You MUST have access to write to directory where certs are saved
 | ||||
|   // ex: /home/foouser/acme/etc
 | ||||
| , configDir: '~/.config/acme/' | ||||
| 
 | ||||
|   // Get notified of important updates and help me make greenlock better
 | ||||
| , communityMember: true | ||||
| 
 | ||||
| //, debug: true
 | ||||
| 	// Get notified of important updates and help me make greenlock better
 | ||||
| 	communityMember: true | ||||
| 
 | ||||
| 	//, debug: true
 | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ////////////////////////
 | ||||
| // http-01 Challenges //
 | ||||
| ////////////////////////
 | ||||
| 
 | ||||
| // http-01 challenge happens over http/1.1, not http2
 | ||||
| var redirectHttps = require('redirect-https')(); | ||||
| var acmeChallengeHandler = greenlock.middleware(function (req, res) { | ||||
|   res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||
|   res.end('<h1>Hello, ⚠️ Insecure World!</h1><a>Visit Secure Site</a>' | ||||
|     + '<script>document.querySelector("a").href=window.location.href.replace(/^http/i, "https");</script>' | ||||
|   ); | ||||
| var redirectHttps = require("redirect-https")(); | ||||
| var acmeChallengeHandler = greenlock.middleware(function(req, res) { | ||||
| 	res.setHeader("Content-Type", "text/html; charset=utf-8"); | ||||
| 	res.end( | ||||
| 		"<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 () { | ||||
|   console.log("Listening for ACME http-01 challenges on", this.address()); | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| require("http") | ||||
| 	.createServer(acmeChallengeHandler) | ||||
| 	.listen(80, function() { | ||||
| 		console.log("Listening for ACME http-01 challenges on", this.address()); | ||||
| 	}); | ||||
| 
 | ||||
| ////////////////////////
 | ||||
| // 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
 | ||||
| var spdyOptions = Object.assign({}, greenlock.tlsOptions); | ||||
| spdyOptions.spdy = { protocols: [ 'h2', 'http/1.1' ], plain: false }; | ||||
| var server = require('spdy').createServer(spdyOptions, require('express')().use('/', function (req, res) { | ||||
|   res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||
|   res.end('<h1>Hello, 🔐 Secure World!</h1>'); | ||||
| })); | ||||
| server.on('error', function (err) { | ||||
|   console.error(err); | ||||
| spdyOptions.spdy = { protocols: ["h2", "http/1.1"], plain: false }; | ||||
| var server = require("spdy").createServer( | ||||
| 	spdyOptions, | ||||
| 	require("express")().use("/", function(req, res) { | ||||
| 		res.setHeader("Content-Type", "text/html; charset=utf-8"); | ||||
| 		res.end("<h1>Hello, 🔐 Secure World!</h1>"); | ||||
| 	}) | ||||
| ); | ||||
| server.on("error", function(err) { | ||||
| 	console.error(err); | ||||
| }); | ||||
| server.on('listening', function () { | ||||
|   console.log("Listening for SPDY/http2/https requests on", this.address()); | ||||
| server.on("listening", function() { | ||||
| 	console.log("Listening for SPDY/http2/https requests on", this.address()); | ||||
| }); | ||||
| server.listen(443); | ||||
|  | ||||
| @ -1,29 +1,30 @@ | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| //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
 | ||||
|   version: 'draft-11' | ||||
| 		server: "https://acme-v02.api.letsencrypt.org/directory", | ||||
| 		// Note: If at first you don't succeed, stop and switch to staging
 | ||||
| 		// https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 
 | ||||
| , server: 'https://acme-v02.api.letsencrypt.org/directory' | ||||
|   // Note: If at first you don't succeed, stop and switch to staging
 | ||||
|   // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 		email: "john.doe@example.com", | ||||
| 
 | ||||
| , 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) { | ||||
|     res.end('Hello, World!'); | ||||
|   }) | ||||
| 		renewWithin: 91 * 24 * 60 * 60 * 1000, | ||||
| 		renewBy: 90 * 24 * 60 * 60 * 1000, | ||||
| 
 | ||||
| , renewWithin: (91 * 24 * 60 * 60 * 1000) | ||||
| , renewBy: (90 * 24 * 60 * 60 * 1000) | ||||
| 
 | ||||
|   // Get notified of important updates and help me make greenlock better
 | ||||
| , communityMember: true | ||||
| , debug: true | ||||
| }).listen(80, 443); | ||||
| 		// Get notified of important updates and help me make greenlock better
 | ||||
| 		communityMember: true, | ||||
| 		debug: true | ||||
| 	}) | ||||
| 	.listen(80, 443); | ||||
|  | ||||
| @ -1,74 +1,70 @@ | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| //var Greenlock = require('greenlock-express')
 | ||||
| var Greenlock = require('../'); | ||||
| var Greenlock = require("../"); | ||||
| 
 | ||||
| var greenlock = Greenlock.create({ | ||||
| 	// Let's Encrypt v2 is ACME draft 11
 | ||||
| 	version: "draft-11", | ||||
| 
 | ||||
|   // Let's Encrypt v2 is ACME draft 11
 | ||||
|   version: 'draft-11' | ||||
| 	server: "https://acme-v02.api.letsencrypt.org/directory", | ||||
| 	// Note: If at first you don't succeed, stop and switch to staging
 | ||||
| 	// https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 
 | ||||
| , server: 'https://acme-v02.api.letsencrypt.org/directory' | ||||
|   // Note: If at first you don't succeed, stop and switch to staging
 | ||||
|   // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 	// You MUST change this to a valid email address
 | ||||
| 	email: "jon@example.com", | ||||
| 
 | ||||
|   // You MUST change this to a valid email address
 | ||||
| , email: 'jon@example.com' | ||||
| 	// You MUST NOT build clients that accept the ToS without asking the user
 | ||||
| 	agreeTos: true, | ||||
| 
 | ||||
|   // You MUST NOT build clients that accept the ToS without asking the user
 | ||||
| , agreeTos: true | ||||
| 	// You MUST change these to valid domains
 | ||||
| 	// NOTE: all domains will validated and listed on the certificate
 | ||||
| 	approvedDomains: ["example.com", "www.example.com"], | ||||
| 
 | ||||
|   // You MUST change these to valid domains
 | ||||
|   // NOTE: all domains will validated and listed on the certificate
 | ||||
| , approvedDomains: [ 'example.com', 'www.example.com' ] | ||||
| 	// You MUST have access to write to directory where certs are saved
 | ||||
| 	// ex: /home/foouser/acme/etc
 | ||||
| 	configDir: "~/.config/acme/", | ||||
| 
 | ||||
|   // You MUST have access to write to directory where certs are saved
 | ||||
|   // ex: /home/foouser/acme/etc
 | ||||
| , configDir: '~/.config/acme/' | ||||
| 
 | ||||
|   // Get notified of important updates and help me make greenlock better
 | ||||
| , communityMember: true | ||||
| 
 | ||||
| //, debug: true
 | ||||
| 	// Get notified of important updates and help me make greenlock better
 | ||||
| 	communityMember: true | ||||
| 
 | ||||
| 	//, debug: true
 | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ////////////////////////
 | ||||
| // http-01 Challenges //
 | ||||
| ////////////////////////
 | ||||
| 
 | ||||
| // http-01 challenge happens over http/1.1, not http2
 | ||||
| var redirectHttps = require('redirect-https')(); | ||||
| var redirectHttps = require("redirect-https")(); | ||||
| var acmeChallengeHandler = greenlock.middleware(redirectHttps); | ||||
| require('http').createServer(acmeChallengeHandler).listen(80, function () { | ||||
|   console.log("Listening for ACME http-01 challenges on", this.address()); | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| require("http") | ||||
| 	.createServer(acmeChallengeHandler) | ||||
| 	.listen(80, function() { | ||||
| 		console.log("Listening for ACME http-01 challenges on", this.address()); | ||||
| 	}); | ||||
| 
 | ||||
| ////////////////////////
 | ||||
| // node.js' http2 api //
 | ||||
| ////////////////////////
 | ||||
| 
 | ||||
| // http2 is a new API with which you would use hapi or koa, not express
 | ||||
| var server = require('http2').createSecureServer(greenlock.tlsOptions); | ||||
| server.on('error', function (err) { | ||||
|   console.error(err); | ||||
| var server = require("http2").createSecureServer(greenlock.tlsOptions); | ||||
| server.on("error", function(err) { | ||||
| 	console.error(err); | ||||
| }); | ||||
| // WARNING: Because the middleware don't handle this API style,
 | ||||
| // the Host headers are unmodified and potentially dangerous
 | ||||
| // (ex: Host: Robert'); DROP TABLE Students;)
 | ||||
| server.on('stream', function (stream, headers) { | ||||
|   console.log(headers); | ||||
|   stream.respond({ | ||||
|     'content-type': 'text/html' | ||||
|   , ':status': 200 | ||||
|   }); | ||||
|   stream.end('Hello, HTTP2 World!'); | ||||
| server.on("stream", function(stream, headers) { | ||||
| 	console.log(headers); | ||||
| 	stream.respond({ | ||||
| 		"content-type": "text/html", | ||||
| 		":status": 200 | ||||
| 	}); | ||||
| 	stream.end("Hello, HTTP2 World!"); | ||||
| }); | ||||
| server.on('listening', function () { | ||||
|   console.log("Listening for http2 requests on", this.address()); | ||||
| server.on("listening", function() { | ||||
| 	console.log("Listening for http2 requests on", this.address()); | ||||
| }); | ||||
| server.listen(443); | ||||
|  | ||||
| @ -1,15 +1,17 @@ | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| var express = require('express'); | ||||
| var express = require("express"); | ||||
| var app = express(); | ||||
| 
 | ||||
| app.use('/', function (req, res) { | ||||
|   res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||
|   res.end('Hello, World!\n\n💚 🔒.js'); | ||||
| app.use("/", function(req, res) { | ||||
| 	res.setHeader("Content-Type", "text/html; charset=utf-8"); | ||||
| 	res.end("Hello, World!\n\n💚 🔒.js"); | ||||
| }); | ||||
| 
 | ||||
| // 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:
 | ||||
| module.exports = app; | ||||
|  | ||||
| @ -1,90 +1,88 @@ | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| //
 | ||||
| // My Secure Server
 | ||||
| //
 | ||||
| //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
 | ||||
|   // Note: If at first you don't succeed, stop and switch to staging
 | ||||
|   // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
|   server: 'https://acme-v02.api.letsencrypt.org/directory' | ||||
| , version: 'draft-11' | ||||
|   // You MUST have write access to save certs
 | ||||
| , configDir: '~/.config/acme/' | ||||
| 	// The previous 'simple' example set these values statically,
 | ||||
| 	// but this example uses approveDomains() to set them dynamically
 | ||||
| 	//, email: 'none@see.note.above'
 | ||||
| 	//, agreeTos: false
 | ||||
| 
 | ||||
| // The previous 'simple' example set these values statically,
 | ||||
| // but this example uses approveDomains() to set them dynamically
 | ||||
| //, email: 'none@see.note.above'
 | ||||
| //, agreeTos: false
 | ||||
| 	// approveDomains is the right place to check a database for
 | ||||
| 	// email addresses with domains and agreements and such
 | ||||
| 	approveDomains: approveDomains, | ||||
| 
 | ||||
|   // approveDomains is the right place to check a database for
 | ||||
|   // email addresses with domains and agreements and such
 | ||||
| , approveDomains: approveDomains | ||||
| 	app: require("./my-express-app.js"), | ||||
| 
 | ||||
| , app: require('./my-express-app.js') | ||||
| 
 | ||||
|   // Get notified of important updates and help me make greenlock better
 | ||||
| , communityMember: true | ||||
| 
 | ||||
| //, debug: true
 | ||||
| 	// Get notified of important updates and help me make greenlock better
 | ||||
| 	communityMember: true | ||||
| 
 | ||||
| 	//, debug: true
 | ||||
| }); | ||||
| 
 | ||||
| var server = greenlock.listen(80, 443); | ||||
| 
 | ||||
| 
 | ||||
| //
 | ||||
| // My Secure Database Check
 | ||||
| //
 | ||||
| function approveDomains(opts, certs, cb) { | ||||
| 	// Only one domain is listed with *automatic* registration via SNI
 | ||||
| 	// (it's an array because managed registration allows for multiple domains,
 | ||||
| 	//                                which was the case in the simple example)
 | ||||
| 	console.log(opts.domains); | ||||
| 
 | ||||
|   // Only one domain is listed with *automatic* registration via SNI
 | ||||
|   // (it's an array because managed registration allows for multiple domains,
 | ||||
|   //                                which was the case in the simple example)
 | ||||
|   console.log(opts.domains); | ||||
| 	// The domains being approved for the first time are listed in opts.domains
 | ||||
| 	// Certs being renewed are listed in certs.altnames
 | ||||
| 	if (certs) { | ||||
| 		opts.domains = [certs.subject].concat(certs.altnames); | ||||
| 	} | ||||
| 
 | ||||
|   // The domains being approved for the first time are listed in opts.domains
 | ||||
|   // Certs being renewed are listed in certs.altnames
 | ||||
|   if (certs) { | ||||
|     opts.domains = [certs.subject].concat(certs.altnames); | ||||
|   } | ||||
| 	fooCheckDb(opts.domains, function(err, agree, email) { | ||||
| 		if (err) { | ||||
| 			cb(err); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
|   fooCheckDb(opts.domains, function (err, agree, email) { | ||||
|     if (err) { cb(err); return; } | ||||
| 		// Services SHOULD automatically accept the ToS and use YOUR email
 | ||||
| 		// Clients MUST NOT accept the ToS without asking the user
 | ||||
| 		opts.agreeTos = agree; | ||||
| 		opts.email = email; | ||||
| 
 | ||||
|     // Services SHOULD automatically accept the ToS and use YOUR email
 | ||||
|     // Clients MUST NOT accept the ToS without asking the user
 | ||||
|     opts.agreeTos = agree; | ||||
|     opts.email = email; | ||||
| 		// NOTE: you can also change other options such as `challengeType` and `challenge`
 | ||||
| 		// (this would be helpful if you decided you wanted wildcard support as a domain altname)
 | ||||
| 		// opts.challengeType = 'http-01';
 | ||||
| 		// opts.challenge = require('le-challenge-fs').create({});
 | ||||
| 
 | ||||
|     // NOTE: you can also change other options such as `challengeType` and `challenge`
 | ||||
|     // (this would be helpful if you decided you wanted wildcard support as a domain altname)
 | ||||
|     // opts.challengeType = 'http-01';
 | ||||
|     // opts.challenge = require('le-challenge-fs').create({});
 | ||||
| 
 | ||||
|     cb(null, { options: opts, certs: certs }); | ||||
|   }); | ||||
| 		cb(null, { options: opts, certs: certs }); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| //
 | ||||
| // My User / Domain Database
 | ||||
| //
 | ||||
| function fooCheckDb(domains, cb) { | ||||
|   // This is an oversimplified example of how we might implement a check in
 | ||||
|   // our database if we have different rules for different users and domains
 | ||||
|   var domains = [ 'example.com', 'www.example.com' ]; | ||||
|   var userEmail = 'john.doe@example.com'; | ||||
|   var userAgrees = true; | ||||
|   var passCheck = opts.domains.every(function (domain) { | ||||
|     return -1 !== domains.indexOf(domain); | ||||
|   }); | ||||
| 	// This is an oversimplified example of how we might implement a check in
 | ||||
| 	// our database if we have different rules for different users and domains
 | ||||
| 	var domains = ["example.com", "www.example.com"]; | ||||
| 	var userEmail = "john.doe@example.com"; | ||||
| 	var userAgrees = true; | ||||
| 	var passCheck = opts.domains.every(function(domain) { | ||||
| 		return -1 !== domains.indexOf(domain); | ||||
| 	}); | ||||
| 
 | ||||
|   if (!passCheck) { | ||||
|     cb(new Error('domain not allowed')); | ||||
|   } else { | ||||
|     cb(null, userAgrees, userEmail); | ||||
|   } | ||||
| 	if (!passCheck) { | ||||
| 		cb(new Error("domain not allowed")); | ||||
| 	} else { | ||||
| 		cb(null, userAgrees, userEmail); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -1,37 +1,38 @@ | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| //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
 | ||||
|   version: 'draft-11' | ||||
| 		server: "https://acme-v02.api.letsencrypt.org/directory", | ||||
| 		// Note: If at first you don't succeed, stop and switch to staging
 | ||||
| 		// https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 
 | ||||
| , server: 'https://acme-v02.api.letsencrypt.org/directory' | ||||
|   // Note: If at first you don't succeed, stop and switch to staging
 | ||||
|   // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 		// You MUST change this to a valid email address
 | ||||
| 		email: "john.doe@example.com", | ||||
| 
 | ||||
|   // You MUST change this to a valid email address
 | ||||
| , email: 'john.doe@example.com' | ||||
| 		// You MUST NOT build clients that accept the ToS without asking the user
 | ||||
| 		agreeTos: true, | ||||
| 
 | ||||
|   // You MUST NOT build clients that accept the ToS without asking the user
 | ||||
| , agreeTos: true | ||||
| 		// You MUST change these to valid domains
 | ||||
| 		// NOTE: all domains will validated and listed on the certificate
 | ||||
| 		approvedDomains: ["example.com", "www.example.com"], | ||||
| 
 | ||||
|   // You MUST change these to valid domains
 | ||||
|   // NOTE: all domains will validated and listed on the certificate
 | ||||
| , approvedDomains: [ 'example.com', 'www.example.com' ] | ||||
| 		// You MUST have access to write to directory where certs are saved
 | ||||
| 		// ex: /home/foouser/acme/etc
 | ||||
| 		configDir: "~/.config/acme/", | ||||
| 		store: require("greenlock-store-fs"), | ||||
| 
 | ||||
|   // You MUST have access to write to directory where certs are saved
 | ||||
|   // ex: /home/foouser/acme/etc
 | ||||
| , configDir: '~/.config/acme/' | ||||
| 		app: require("express")().use("/", function(req, res) { | ||||
| 			res.setHeader("Content-Type", "text/html; charset=utf-8"); | ||||
| 			res.end("Hello, World!\n\n💚 🔒.js"); | ||||
| 		}), | ||||
| 
 | ||||
| , app: require('express')().use('/', function (req, res) { | ||||
|     res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||
|     res.end('Hello, World!\n\n💚 🔒.js'); | ||||
|   }) | ||||
| 		// Get notified of important updates and help me make greenlock better
 | ||||
| 		communityMember: true | ||||
| 
 | ||||
|   // Get notified of important updates and help me make greenlock better
 | ||||
| , communityMember: true | ||||
| 
 | ||||
| //, debug: true
 | ||||
| 
 | ||||
| }).listen(80, 443); | ||||
| 		//, debug: true
 | ||||
| 	}) | ||||
| 	.listen(80, 443); | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| //
 | ||||
| // WARNING: Not for noobs
 | ||||
| @ -9,87 +9,96 @@ | ||||
| // This demo is used with tunnel-server.js and tunnel-client.js
 | ||||
| //
 | ||||
| 
 | ||||
| var email = 'john.doe@gmail.com'; | ||||
| var domains = [ 'example.com' ]; | ||||
| var email = "john.doe@gmail.com"; | ||||
| var domains = ["example.com"]; | ||||
| var agreeLeTos = true; | ||||
| //var secret = "My Little Brony";
 | ||||
| var secret = require('crypto').randomBytes(16).toString('hex'); | ||||
| var secret = require("crypto") | ||||
| 	.randomBytes(16) | ||||
| 	.toString("hex"); | ||||
| 
 | ||||
| require('../').create({ | ||||
|   version: 'draft-11' | ||||
| require("../") | ||||
| 	.create({ | ||||
| 		version: "draft-11", | ||||
| 
 | ||||
| , server: 'https://acme-v02.api.letsencrypt.org/directory' | ||||
|   // Note: If at first you don't succeed, stop and switch to staging
 | ||||
|   // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 
 | ||||
| , email: email | ||||
| , agreeTos: agreeLeTos | ||||
| , approveDomains: domains | ||||
| , configDir: '~/.config/acme/' | ||||
| , app: remoteAccess(secret) | ||||
|   // Get notified of important updates and help me make greenlock better
 | ||||
| , communityMember: true | ||||
| //, debug: true
 | ||||
| }).listen(3000, 8443); | ||||
| 		server: "https://acme-v02.api.letsencrypt.org/directory", | ||||
| 		// Note: If at first you don't succeed, stop and switch to staging
 | ||||
| 		// https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 
 | ||||
| 		email: email, | ||||
| 		agreeTos: agreeLeTos, | ||||
| 		approveDomains: domains, | ||||
| 		configDir: "~/.config/acme/", | ||||
| 		app: remoteAccess(secret), | ||||
| 		// Get notified of important updates and help me make greenlock better
 | ||||
| 		communityMember: true | ||||
| 		//, debug: true
 | ||||
| 	}) | ||||
| 	.listen(3000, 8443); | ||||
| 
 | ||||
| function remoteAccess(secret) { | ||||
|   var express = require('express'); | ||||
|   var basicAuth = require('express-basic-auth'); | ||||
|   var serveIndex = require('serve-index'); | ||||
| 	var express = require("express"); | ||||
| 	var basicAuth = require("express-basic-auth"); | ||||
| 	var serveIndex = require("serve-index"); | ||||
| 
 | ||||
|   var rootIndex = serveIndex('/', { hidden: true, icons: true, view: 'details' }); | ||||
|   var rootFs = express.static('/', { dotfiles: 'allow', redirect: true, index: false }); | ||||
| 	var rootIndex = serveIndex("/", { hidden: true, icons: true, view: "details" }); | ||||
| 	var rootFs = express.static("/", { dotfiles: "allow", redirect: true, index: false }); | ||||
| 
 | ||||
|   var userIndex = serveIndex(require('os').homedir(), { hidden: true, icons: true, view: 'details' }); | ||||
|   var userFs = express.static(require('os').homedir(), { dotfiles: 'allow', redirect: true, index: false }); | ||||
| 	var userIndex = serveIndex(require("os").homedir(), { hidden: true, icons: true, view: "details" }); | ||||
| 	var userFs = express.static(require("os").homedir(), { dotfiles: "allow", redirect: true, index: false }); | ||||
| 
 | ||||
|   var app = express(); | ||||
|   var realm = 'Login Required'; | ||||
| 	var app = express(); | ||||
| 	var realm = "Login Required"; | ||||
| 
 | ||||
|   var myAuth = basicAuth({ | ||||
|     users: { 'root': secret, 'user': secret } | ||||
|   , challenge: true | ||||
|   , realm: realm | ||||
|   , unauthorizedResponse: function (/*req*/) { | ||||
|       return 'Unauthorized <a href="/">Home</a>'; | ||||
|     } | ||||
|   }); | ||||
| 	var myAuth = basicAuth({ | ||||
| 		users: { root: secret, user: secret }, | ||||
| 		challenge: true, | ||||
| 		realm: realm, | ||||
| 		unauthorizedResponse: function(/*req*/) { | ||||
| 			return 'Unauthorized <a href="/">Home</a>'; | ||||
| 		} | ||||
| 	}); | ||||
| 
 | ||||
|   app.get('/', function (req, res) { | ||||
|     res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||
|     res.end( | ||||
|       '<a href="/browse/">View Files</a>' | ||||
|     + '  |  ' | ||||
|     + '<a href="/logout/">Logout</a>' | ||||
|     ); | ||||
|   }); | ||||
|   app.use('/logout', function (req, res) { | ||||
|     res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||
|     res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"'); | ||||
|     res.statusCode = 401; | ||||
|     //res.setHeader('Location', '/');
 | ||||
|     res.end('Logged out   |   <a href="/">Home</a>'); | ||||
|   }); | ||||
|   app.use('/browse', myAuth); | ||||
|   app.use('/browse', function (req, res, next) { | ||||
|     if ('root' === req.auth.user) { rootFs(req, res, function () { rootIndex(req, res, next); }); return; } | ||||
|     if ('user' === req.auth.user) { userFs(req, res, function () { userIndex(req, res, next); }); return; } | ||||
|     res.end('Sad Panda'); | ||||
|   }); | ||||
| 	app.get("/", function(req, res) { | ||||
| 		res.setHeader("Content-Type", "text/html; charset=utf-8"); | ||||
| 		res.end('<a href="/browse/">View Files</a>' + "  |  " + '<a href="/logout/">Logout</a>'); | ||||
| 	}); | ||||
| 	app.use("/logout", function(req, res) { | ||||
| 		res.setHeader("Content-Type", "text/html; charset=utf-8"); | ||||
| 		res.setHeader("WWW-Authenticate", 'Basic realm="' + realm + '"'); | ||||
| 		res.statusCode = 401; | ||||
| 		//res.setHeader('Location', '/');
 | ||||
| 		res.end('Logged out   |   <a href="/">Home</a>'); | ||||
| 	}); | ||||
| 	app.use("/browse", myAuth); | ||||
| 	app.use("/browse", function(req, res, next) { | ||||
| 		if ("root" === req.auth.user) { | ||||
| 			rootFs(req, res, function() { | ||||
| 				rootIndex(req, res, next); | ||||
| 			}); | ||||
| 			return; | ||||
| 		} | ||||
| 		if ("user" === req.auth.user) { | ||||
| 			userFs(req, res, function() { | ||||
| 				userIndex(req, res, next); | ||||
| 			}); | ||||
| 			return; | ||||
| 		} | ||||
| 		res.end("Sad Panda"); | ||||
| 	}); | ||||
| 
 | ||||
|   console.log(''); | ||||
|   console.log(''); | ||||
|   console.log('Usernames are\n'); | ||||
|   console.log('\troot'); | ||||
|   console.log('\tuser'); | ||||
|   console.log(''); | ||||
|   console.log('Password (for both) is\n'); | ||||
|   console.log('\t' + secret); | ||||
|   console.log(''); | ||||
|   console.log("Shhhh... It's a secret to everybody!"); | ||||
|   console.log(''); | ||||
|   console.log(''); | ||||
| 	console.log(""); | ||||
| 	console.log(""); | ||||
| 	console.log("Usernames are\n"); | ||||
| 	console.log("\troot"); | ||||
| 	console.log("\tuser"); | ||||
| 	console.log(""); | ||||
| 	console.log("Password (for both) is\n"); | ||||
| 	console.log("\t" + secret); | ||||
| 	console.log(""); | ||||
| 	console.log("Shhhh... It's a secret to everybody!"); | ||||
| 	console.log(""); | ||||
| 	console.log(""); | ||||
| 
 | ||||
|   return app; | ||||
| 	return app; | ||||
| } | ||||
|  | ||||
| @ -2,19 +2,19 @@ | ||||
| // I'm not a fan of `socket.io` because it's huge and complex.
 | ||||
| // I much prefer `ws` because it's very simple and easy.
 | ||||
| // That said, it's popular.......
 | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| //var greenlock = require('greenlock-express');
 | ||||
| var greenlock = require('../'); | ||||
| var options = require('./greenlock-options.js'); | ||||
| var socketio = require('socket.io'); | ||||
| var greenlock = require("../"); | ||||
| var options = require("./greenlock-options.js"); | ||||
| var socketio = require("socket.io"); | ||||
| var server; | ||||
| var io; | ||||
| 
 | ||||
| // Any node http app will do - whether express, raw http or whatever
 | ||||
| options.app = require('express')().use('/', function (req, res) { | ||||
|   res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||
|   res.end('Hello, World!\n\n💚 🔒.js'); | ||||
| options.app = require("express")().use("/", function(req, res) { | ||||
| 	res.setHeader("Content-Type", "text/html; charset=utf-8"); | ||||
| 	res.end("Hello, World!\n\n💚 🔒.js"); | ||||
| }); | ||||
| 
 | ||||
| // The server that's handed back from `listen` is a raw https server
 | ||||
| @ -22,11 +22,11 @@ server = greenlock.create(options).listen(80, 443); | ||||
| io = socketio(server); | ||||
| 
 | ||||
| // Then you do your socket.io stuff
 | ||||
| io.on('connection', function (socket) { | ||||
|   console.log('a user connected'); | ||||
|   socket.emit('Welcome'); | ||||
| io.on("connection", function(socket) { | ||||
| 	console.log("a user connected"); | ||||
| 	socket.emit("Welcome"); | ||||
| 
 | ||||
|   socket.on('chat message', function (msg) { | ||||
|     socket.broadcast.emit('chat message', msg); | ||||
|   }); | ||||
| 	socket.on("chat message", function(msg) { | ||||
| 		socket.broadcast.emit("chat message", msg); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
| @ -1,54 +1,50 @@ | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| // npm install spdy@3.x
 | ||||
| 
 | ||||
| //var Greenlock = require('greenlock-express')
 | ||||
| var Greenlock = require('../'); | ||||
| var Greenlock = require("../"); | ||||
| 
 | ||||
| var greenlock = Greenlock.create({ | ||||
| 	// Let's Encrypt v2 is ACME draft 11
 | ||||
| 	version: "draft-11", | ||||
| 
 | ||||
|   // Let's Encrypt v2 is ACME draft 11
 | ||||
|   version: 'draft-11' | ||||
| 	server: "https://acme-v02.api.letsencrypt.org/directory", | ||||
| 	// Note: If at first you don't succeed, stop and switch to staging
 | ||||
| 	// https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 
 | ||||
| , server: 'https://acme-v02.api.letsencrypt.org/directory' | ||||
|   // Note: If at first you don't succeed, stop and switch to staging
 | ||||
|   // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 	// You MUST change this to a valid email address
 | ||||
| 	email: "jon@example.com", | ||||
| 
 | ||||
|   // You MUST change this to a valid email address
 | ||||
| , email: 'jon@example.com' | ||||
| 	// You MUST NOT build clients that accept the ToS without asking the user
 | ||||
| 	agreeTos: true, | ||||
| 
 | ||||
|   // You MUST NOT build clients that accept the ToS without asking the user
 | ||||
| , agreeTos: true | ||||
| 	// You MUST change these to valid domains
 | ||||
| 	// NOTE: all domains will validated and listed on the certificate
 | ||||
| 	approvedDomains: ["example.com", "www.example.com"], | ||||
| 
 | ||||
|   // You MUST change these to valid domains
 | ||||
|   // NOTE: all domains will validated and listed on the certificate
 | ||||
| , approvedDomains: [ 'example.com', 'www.example.com' ] | ||||
| 	// You MUST have access to write to directory where certs are saved
 | ||||
| 	// ex: /home/foouser/acme/etc
 | ||||
| 	configDir: "~/.config/acme/", // MUST have write access
 | ||||
| 
 | ||||
|   // You MUST have access to write to directory where certs are saved
 | ||||
|   // ex: /home/foouser/acme/etc
 | ||||
| , configDir: '~/.config/acme/'      // MUST have write access
 | ||||
| 
 | ||||
|   // Get notified of important updates and help me make greenlock better
 | ||||
| , communityMember: true | ||||
| 
 | ||||
| //, debug: true
 | ||||
| 	// Get notified of important updates and help me make greenlock better
 | ||||
| 	communityMember: true | ||||
| 
 | ||||
| 	//, debug: true
 | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ////////////////////////
 | ||||
| // http-01 Challenges //
 | ||||
| ////////////////////////
 | ||||
| 
 | ||||
| // http-01 challenge happens over http/1.1, not http2
 | ||||
| var redirectHttps = require('redirect-https')(); | ||||
| var redirectHttps = require("redirect-https")(); | ||||
| var acmeChallengeHandler = greenlock.middleware(redirectHttps); | ||||
| require('http').createServer(acmeChallengeHandler).listen(80, function () { | ||||
|   console.log("Listening for ACME http-01 challenges on", this.address()); | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| require("http") | ||||
| 	.createServer(acmeChallengeHandler) | ||||
| 	.listen(80, function() { | ||||
| 		console.log("Listening for ACME http-01 challenges on", this.address()); | ||||
| 	}); | ||||
| 
 | ||||
| ////////////////////////
 | ||||
| // 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
 | ||||
| var spdyOptions = Object.assign({}, greenlock.tlsOptions); | ||||
| spdyOptions.spdy = { protocols: [ 'h2', 'http/1.1' ], plain: false }; | ||||
| var myApp = require('./my-express-app.js'); | ||||
| var server = require('spdy').createServer(spdyOptions, myApp); | ||||
| server.on('error', function (err) { | ||||
|   console.error(err); | ||||
| spdyOptions.spdy = { protocols: ["h2", "http/1.1"], plain: false }; | ||||
| var myApp = require("./my-express-app.js"); | ||||
| var server = require("spdy").createServer(spdyOptions, myApp); | ||||
| server.on("error", function(err) { | ||||
| 	console.error(err); | ||||
| }); | ||||
| server.on('listening', function () { | ||||
|   console.log("Listening for SPDY/http2/https requests on", this.address()); | ||||
| server.on("listening", function() { | ||||
| 	console.log("Listening for SPDY/http2/https requests on", this.address()); | ||||
| }); | ||||
| server.listen(443); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| #!/usr/bin/env node
 | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| ///////////////////
 | ||||
| // vhost example //
 | ||||
| @ -11,118 +11,124 @@ | ||||
| 
 | ||||
| // The prefix where sites go by name.
 | ||||
| // For example: whatever.com may live in /srv/www/whatever.com, thus /srv/www is our path
 | ||||
| var srv = process.argv[3] || '/srv/www/'; | ||||
| var srv = process.argv[3] || "/srv/www/"; | ||||
| 
 | ||||
| var path = require('path'); | ||||
| var fs = require('fs').promises; | ||||
| var finalhandler = require('finalhandler'); | ||||
| var serveStatic = require('serve-static'); | ||||
| var path = require("path"); | ||||
| var fs = require("fs").promises; | ||||
| var finalhandler = require("finalhandler"); | ||||
| var serveStatic = require("serve-static"); | ||||
| 
 | ||||
| //var glx = require('greenlock-express')
 | ||||
| var glx = require('./').create({ | ||||
| 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
 | ||||
|                                                             // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 	configDir: process.argv[4] || "~/.config/acme/", // You MUST have access to write to directory where certs
 | ||||
| 	// are saved. ex: /home/foouser/.config/acme
 | ||||
| 
 | ||||
| , configDir: process.argv[4] || '~/.config/acme/'           // You MUST have access to write to directory where certs
 | ||||
|                                                             // are saved. ex: /home/foouser/.config/acme
 | ||||
| 	approveDomains: myApproveDomains, // Greenlock's wraps around tls.SNICallback. Check the
 | ||||
| 	// domain name here and reject invalid ones
 | ||||
| 
 | ||||
| , approveDomains: myApproveDomains                          // Greenlock's wraps around tls.SNICallback. Check the
 | ||||
|                                                             // domain name here and reject invalid ones
 | ||||
| 	app: myVhostApp, // Any node-style http app (i.e. express, koa, hapi, rill)
 | ||||
| 
 | ||||
| , app: myVhostApp                                           // Any node-style http app (i.e. express, koa, hapi, rill)
 | ||||
| 
 | ||||
|   /* CHANGE TO A VALID EMAIL */ | ||||
| , email: process.argv[2] || 'jon.doe@example.com'           // Email for Let's Encrypt account and Greenlock Security
 | ||||
| , agreeTos: true                                            // Accept Let's Encrypt ToS
 | ||||
| //, communityMember: true                                   // Join Greenlock to get important updates, no spam
 | ||||
| 
 | ||||
| //, debug: true
 | ||||
| 	/* CHANGE TO A VALID EMAIL */ | ||||
| 	email: process.argv[2] || "jon.doe@example.com", // Email for Let's Encrypt account and Greenlock Security
 | ||||
| 	agreeTos: true // Accept Let's Encrypt ToS
 | ||||
| 	//, communityMember: true                                   // Join Greenlock to get important updates, no spam
 | ||||
| 
 | ||||
| 	//, debug: true
 | ||||
| }); | ||||
| 
 | ||||
| var server = glx.listen(80, 443); | ||||
| server.on('listening', function () { | ||||
|   console.info(server.type + " listening on", server.address()); | ||||
| server.on("listening", function() { | ||||
| 	console.info(server.type + " listening on", server.address()); | ||||
| }); | ||||
| 
 | ||||
| function myApproveDomains(opts, certs, cb) { | ||||
|   console.log('sni:', opts.domain); | ||||
|   // In this example the filesystem is our "database".
 | ||||
|   // We check in /srv/www for whatever.com and if it exists, it's allowed
 | ||||
| 	console.log("sni:", opts.domain); | ||||
| 	// In this example the filesystem is our "database".
 | ||||
| 	// We check in /srv/www for whatever.com and if it exists, it's allowed
 | ||||
| 
 | ||||
|   // SECURITY Greenlock validates opts.domains ahead-of-time so you don't have to
 | ||||
|   return checkWwws(opts.domains[0]).then(function () { | ||||
|     //opts.email = email;
 | ||||
|     opts.agreeTos = true; | ||||
|     cb(null, { options: opts, certs: certs }); | ||||
|   }).catch(cb); | ||||
| 	// SECURITY Greenlock validates opts.domains ahead-of-time so you don't have to
 | ||||
| 	return checkWwws(opts.domains[0]) | ||||
| 		.then(function() { | ||||
| 			//opts.email = email;
 | ||||
| 			opts.agreeTos = true; | ||||
| 			cb(null, { options: opts, certs: certs }); | ||||
| 		}) | ||||
| 		.catch(cb); | ||||
| } | ||||
| 
 | ||||
| function checkWwws(_hostname) { | ||||
|   if (!_hostname) { | ||||
|     // SECURITY, don't allow access to the 'srv' root
 | ||||
|     // (greenlock-express uses middleware to check '..', etc)
 | ||||
|     return ''; | ||||
|   } | ||||
|   var hostname = _hostname; | ||||
|   var _hostdir = path.join(srv, hostname); | ||||
|   var hostdir = _hostdir; | ||||
|   // TODO could test for www/no-www both in directory
 | ||||
|   return fs.readdir(hostdir).then(function () { | ||||
|     // TODO check for some sort of htaccess.json and use email in that
 | ||||
|     // NOTE: you can also change other options such as `challengeType` and `challenge`
 | ||||
|     // opts.challengeType = 'http-01';
 | ||||
|     // opts.challenge = require('le-challenge-fs').create({});
 | ||||
|     return hostname; | ||||
|   }).catch(function () { | ||||
|     if ('www.' === hostname.slice(0, 4)) { | ||||
|       // Assume we'll redirect to non-www if it's available.
 | ||||
|       hostname = hostname.slice(4); | ||||
|       hostdir = path.join(srv, hostname); | ||||
|       return fs.readdir(hostdir).then(function () { | ||||
|         // TODO list both domains?
 | ||||
|         return hostname; | ||||
|       }); | ||||
|     } else { | ||||
|       // Or check and see if perhaps we should redirect non-www to www
 | ||||
|       hostname = 'www.' + hostname; | ||||
|       hostdir = path.join(srv, hostname); | ||||
|       return fs.readdir(hostdir).then(function () { | ||||
|         // TODO list both domains?
 | ||||
|         return hostname; | ||||
|       }); | ||||
|     } | ||||
|   }).catch(function () { | ||||
|     throw new Error("rejecting '" + _hostname + "' because '" + _hostdir + "' could not be read"); | ||||
|   }); | ||||
| 	if (!_hostname) { | ||||
| 		// SECURITY, don't allow access to the 'srv' root
 | ||||
| 		// (greenlock-express uses middleware to check '..', etc)
 | ||||
| 		return ""; | ||||
| 	} | ||||
| 	var hostname = _hostname; | ||||
| 	var _hostdir = path.join(srv, hostname); | ||||
| 	var hostdir = _hostdir; | ||||
| 	// TODO could test for www/no-www both in directory
 | ||||
| 	return fs | ||||
| 		.readdir(hostdir) | ||||
| 		.then(function() { | ||||
| 			// TODO check for some sort of htaccess.json and use email in that
 | ||||
| 			// NOTE: you can also change other options such as `challengeType` and `challenge`
 | ||||
| 			// opts.challengeType = 'http-01';
 | ||||
| 			// opts.challenge = require('le-challenge-fs').create({});
 | ||||
| 			return hostname; | ||||
| 		}) | ||||
| 		.catch(function() { | ||||
| 			if ("www." === hostname.slice(0, 4)) { | ||||
| 				// Assume we'll redirect to non-www if it's available.
 | ||||
| 				hostname = hostname.slice(4); | ||||
| 				hostdir = path.join(srv, hostname); | ||||
| 				return fs.readdir(hostdir).then(function() { | ||||
| 					// TODO list both domains?
 | ||||
| 					return hostname; | ||||
| 				}); | ||||
| 			} else { | ||||
| 				// Or check and see if perhaps we should redirect non-www to www
 | ||||
| 				hostname = "www." + hostname; | ||||
| 				hostdir = path.join(srv, hostname); | ||||
| 				return fs.readdir(hostdir).then(function() { | ||||
| 					// TODO list both domains?
 | ||||
| 					return hostname; | ||||
| 				}); | ||||
| 			} | ||||
| 		}) | ||||
| 		.catch(function() { | ||||
| 			throw new Error("rejecting '" + _hostname + "' because '" + _hostdir + "' could not be read"); | ||||
| 		}); | ||||
| } | ||||
| 
 | ||||
| function myVhostApp(req, res) { | ||||
|   // SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to
 | ||||
|   // (also: only domains approved above will get here)
 | ||||
|   console.log('vhost:', req.headers.host); | ||||
|   if (!req.headers.host) { | ||||
|     // SECURITY, don't allow access to the 'srv' root
 | ||||
|     // (greenlock-express uses middleware to check '..', etc)
 | ||||
|     return res.end(); | ||||
|   } | ||||
| 	// SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to
 | ||||
| 	// (also: only domains approved above will get here)
 | ||||
| 	console.log("vhost:", req.headers.host); | ||||
| 	if (!req.headers.host) { | ||||
| 		// SECURITY, don't allow access to the 'srv' root
 | ||||
| 		// (greenlock-express uses middleware to check '..', etc)
 | ||||
| 		return res.end(); | ||||
| 	} | ||||
| 
 | ||||
|   // We could cache wether or not a host exists for some amount of time
 | ||||
|   var fin = finalhandler(req, res); | ||||
|   return checkWwws(req.headers.host).then(function (hostname) { | ||||
|     if (hostname !== req.headers.host) { | ||||
|       res.statusCode = 302; | ||||
|       res.setHeader('Location', 'https://' + hostname); | ||||
|       // SECURITY this is safe only because greenlock disallows invalid hostnames
 | ||||
|       res.end("<!-- redirecting to https://" + hostname + "-->"); | ||||
|       return; | ||||
|     } | ||||
|     var serve = serveStatic(path.join(srv, hostname), { redirect: true }); | ||||
|     serve(req, res, fin); | ||||
|   }).catch(function () { | ||||
|    fin(); | ||||
|   }); | ||||
| 	// We could cache wether or not a host exists for some amount of time
 | ||||
| 	var fin = finalhandler(req, res); | ||||
| 	return checkWwws(req.headers.host) | ||||
| 		.then(function(hostname) { | ||||
| 			if (hostname !== req.headers.host) { | ||||
| 				res.statusCode = 302; | ||||
| 				res.setHeader("Location", "https://" + hostname); | ||||
| 				// SECURITY this is safe only because greenlock disallows invalid hostnames
 | ||||
| 				res.end("<!-- redirecting to https://" + hostname + "-->"); | ||||
| 				return; | ||||
| 			} | ||||
| 			var serve = serveStatic(path.join(srv, hostname), { redirect: true }); | ||||
| 			serve(req, res, fin); | ||||
| 		}) | ||||
| 		.catch(function() { | ||||
| 			fin(); | ||||
| 		}); | ||||
| } | ||||
|  | ||||
| @ -1,40 +1,46 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| "use strict"; | ||||
| 
 | ||||
| ////////////////////////
 | ||||
| // Greenlock Setup    //
 | ||||
| ////////////////////////
 | ||||
| 
 | ||||
| //var Greenlock = require('greenlock-express');
 | ||||
| var Greenlock = require('../'); | ||||
| var Greenlock = require("../"); | ||||
| var greenlock = Greenlock.create({ | ||||
| 	// Let's Encrypt v2 is ACME draft 11
 | ||||
| 	// Note: If at first you don't succeed, stop and switch to staging
 | ||||
| 	// https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 	server: "https://acme-v02.api.letsencrypt.org/directory", | ||||
| 	version: "draft-11", | ||||
| 	configDir: "~/.config/acme/", | ||||
| 	app: require("./my-express-app.js"), | ||||
| 
 | ||||
|   // Let's Encrypt v2 is ACME draft 11
 | ||||
|   // Note: If at first you don't succeed, stop and switch to staging
 | ||||
|   // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
|   server: 'https://acme-v02.api.letsencrypt.org/directory' | ||||
| , version: 'draft-11' | ||||
| , configDir: '~/.config/acme/' | ||||
| , app: require('./my-express-app.js') | ||||
| 	// You MUST change these to a valid email and domains
 | ||||
| 	email: "john.doe@example.com", | ||||
| 	approvedDomains: ["example.com", "www.example.com"], | ||||
| 	agreeTos: true, | ||||
| 
 | ||||
|   // You MUST change these to a valid email and domains
 | ||||
| , email: 'john.doe@example.com' | ||||
| , approvedDomains: [ 'example.com', 'www.example.com' ] | ||||
| , agreeTos: true | ||||
| 
 | ||||
|   // Get notified of important updates and help me make greenlock better
 | ||||
| , communityMember: true | ||||
| , telemetry: true | ||||
| //, debug: true
 | ||||
| 	// Get notified of important updates and help me make greenlock better
 | ||||
| 	communityMember: true, | ||||
| 	telemetry: true | ||||
| 	//, debug: true
 | ||||
| }); | ||||
| 
 | ||||
| var server = greenlock.listen(80, 443); | ||||
| 
 | ||||
| var WebSocket = require('ws'); | ||||
| var WebSocket = require("ws"); | ||||
| var ws = new WebSocket.Server({ server: server }); | ||||
| ws.on('connection', function (ws, req) { | ||||
|   // inspect req.headers.authorization (or cookies) for session info
 | ||||
|   ws.send("[Secure Echo Server] Hello!\nAuth: '" + (req.headers.authorization || 'none') + "'\n" | ||||
|     + "Cookie: '" + (req.headers.cookie || 'none') + "'\n"); | ||||
|   ws.on('message', function (data) { ws.send(data); }); | ||||
| ws.on("connection", function(ws, req) { | ||||
| 	// inspect req.headers.authorization (or cookies) for session info
 | ||||
| 	ws.send( | ||||
| 		"[Secure Echo Server] Hello!\nAuth: '" + | ||||
| 			(req.headers.authorization || "none") + | ||||
| 			"'\n" + | ||||
| 			"Cookie: '" + | ||||
| 			(req.headers.cookie || "none") + | ||||
| 			"'\n" | ||||
| 	); | ||||
| 	ws.on("message", function(data) { | ||||
| 		ws.send(data); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| #!/usr/bin/env node
 | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| /*global Promise*/ | ||||
| 
 | ||||
| ///////////////////////
 | ||||
| @ -11,60 +11,67 @@ | ||||
| //
 | ||||
| 
 | ||||
| //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' | ||||
| //, server: 'https://acme-v02.api.letsencrypt.org/directory'  // If at first you don't succeed, stop and switch to staging
 | ||||
|                                                             // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 	configDir: "~/acme/", // You MUST have access to write to directory where certs
 | ||||
| 	// are saved. ex: /home/foouser/.config/acme
 | ||||
| 
 | ||||
| , configDir: '~/acme/'                                      // You MUST have access to write to directory where certs
 | ||||
|                                                             // are saved. ex: /home/foouser/.config/acme
 | ||||
| 	approveDomains: myApproveDomains, // Greenlock's wraps around tls.SNICallback. Check the
 | ||||
| 	// domain name here and reject invalid ones
 | ||||
| 
 | ||||
| , approveDomains: myApproveDomains                          // Greenlock's wraps around tls.SNICallback. Check the
 | ||||
|                                                             // domain name here and reject invalid ones
 | ||||
| 	app: require("./my-express-app.js"), // Any node-style http app (i.e. express, koa, hapi, rill)
 | ||||
| 
 | ||||
| , 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 */ | ||||
| , email: 'jon.doe@example.com'                              // Email for Let's Encrypt account and Greenlock Security
 | ||||
| , agreeTos: true                                            // Accept Let's Encrypt ToS
 | ||||
| , communityMember: true                                     // Join Greenlock to (very rarely) get important updates
 | ||||
| 
 | ||||
| //, debug: true
 | ||||
| , store: require('le-store-fs') | ||||
| 	//, debug: true
 | ||||
| 	store: require("le-store-fs") | ||||
| }); | ||||
| 
 | ||||
| var server = glx.listen(80, 443); | ||||
| server.on('listening', function () { | ||||
|   console.info(server.type + " listening on", server.address()); | ||||
| server.on("listening", function() { | ||||
| 	console.info(server.type + " listening on", server.address()); | ||||
| }); | ||||
| 
 | ||||
| function myApproveDomains(opts) { | ||||
|   console.log('sni:', opts.domain); | ||||
| 	console.log("sni:", opts.domain); | ||||
| 
 | ||||
|   // must be 'example.com' or start with 'example.com'
 | ||||
|   if ('example.com' !== opts.domain | ||||
|     && 'example.com' !== opts.domain.split('.').slice(1).join('.')) { | ||||
|     return Promise.reject(new Error("we don't serve your kind here: " + opts.domain)); | ||||
|   } | ||||
| 	// must be 'example.com' or start with 'example.com'
 | ||||
| 	if ( | ||||
| 		"example.com" !== opts.domain && | ||||
| 		"example.com" !== | ||||
| 			opts.domain | ||||
| 				.split(".") | ||||
| 				.slice(1) | ||||
| 				.join(".") | ||||
| 	) { | ||||
| 		return Promise.reject(new Error("we don't serve your kind here: " + opts.domain)); | ||||
| 	} | ||||
| 
 | ||||
|   // the primary domain for the cert
 | ||||
|   opts.subject = 'example.com'; | ||||
|   // the altnames (including the primary)
 | ||||
|   opts.domains = [ opts.subject, '*.example.com' ]; | ||||
| 	// the primary domain for the cert
 | ||||
| 	opts.subject = "example.com"; | ||||
| 	// the altnames (including the primary)
 | ||||
| 	opts.domains = [opts.subject, "*.example.com"]; | ||||
| 
 | ||||
|   if (!opts.challenges) { opts.challenges = {}; } | ||||
|   opts.challenges['http-01'] = require('le-challenge-fs').create({}); | ||||
|   // Note: When implementing a dns-01 plugin you should make it check in a loop
 | ||||
|   // until it can positively confirm that the DNS changes have propagated.
 | ||||
|   // That could take several seconds to a few minutes.
 | ||||
|   opts.challenges['dns-01'] = require('le-challenge-dns').create({}); | ||||
| 	if (!opts.challenges) { | ||||
| 		opts.challenges = {}; | ||||
| 	} | ||||
| 	opts.challenges["http-01"] = require("le-challenge-fs").create({}); | ||||
| 	// Note: When implementing a dns-01 plugin you should make it check in a loop
 | ||||
| 	// until it can positively confirm that the DNS changes have propagated.
 | ||||
| 	// That could take several seconds to a few minutes.
 | ||||
| 	opts.challenges["dns-01"] = require("le-challenge-dns").create({}); | ||||
| 
 | ||||
|   // explicitly set account id and certificate.id
 | ||||
|   opts.account = { id: opts.email }; | ||||
|   opts.certificate = { id: opts.subject }; | ||||
| 	// explicitly set account id and certificate.id
 | ||||
| 	opts.account = { id: opts.email }; | ||||
| 	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; | ||||
| try { | ||||
|   PromiseA = require('bluebird'); | ||||
| } catch(e) { | ||||
|   PromiseA = global.Promise; | ||||
| 	PromiseA = require("bluebird"); | ||||
| } catch (e) { | ||||
| 	PromiseA = global.Promise; | ||||
| } | ||||
| 
 | ||||
| // opts.approveDomains(options, certs, cb)
 | ||||
| module.exports.create = function (opts) { | ||||
|   // accept all defaults for greenlock.challenges, greenlock.store, greenlock.middleware
 | ||||
|   if (!opts._communityPackage) { | ||||
|     opts._communityPackage = 'greenlock-express.js'; | ||||
|     opts._communityPackageVersion = require('./package.json').version; | ||||
|   } | ||||
| module.exports.create = function(opts) { | ||||
| 	// accept all defaults for greenlock.challenges, greenlock.store, greenlock.middleware
 | ||||
| 	if (!opts._communityPackage) { | ||||
| 		opts._communityPackage = "greenlock-express.js"; | ||||
| 		opts._communityPackageVersion = require("./package.json").version; | ||||
| 	} | ||||
| 
 | ||||
|   function explainError(e) { | ||||
|     console.error('Error:' + e.message); | ||||
|     if ('EACCES' === e.errno) { | ||||
|       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)\""); | ||||
|       return; | ||||
|     } | ||||
|     if ('EADDRINUSE' === e.errno) { | ||||
|       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."); | ||||
|       return; | ||||
|     } | ||||
|     console.error(e.code + ": '" + e.address + ":" + e.port + "'"); | ||||
|   } | ||||
| 	function explainError(e) { | ||||
| 		console.error("Error:" + e.message); | ||||
| 		if ("EACCES" === e.errno) { | ||||
| 			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)"'); | ||||
| 			return; | ||||
| 		} | ||||
| 		if ("EADDRINUSE" === e.errno) { | ||||
| 			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."); | ||||
| 			return; | ||||
| 		} | ||||
| 		console.error(e.code + ": '" + e.address + ":" + e.port + "'"); | ||||
| 	} | ||||
| 
 | ||||
|   function _createPlain(plainPort) { | ||||
|     if (!plainPort) { plainPort = 80; } | ||||
| 	function _createPlain(plainPort) { | ||||
| 		if (!plainPort) { | ||||
| 			plainPort = 80; | ||||
| 		} | ||||
| 
 | ||||
|     var parts = String(plainPort).split(':'); | ||||
|     var p = parts.pop(); | ||||
|     var addr = parts.join(':').replace(/^\[/, '').replace(/\]$/, ''); | ||||
|     var args = []; | ||||
|     var httpType; | ||||
|     var server; | ||||
|     var validHttpPort = (parseInt(p, 10) >= 0); | ||||
| 		var parts = String(plainPort).split(":"); | ||||
| 		var p = parts.pop(); | ||||
| 		var addr = parts | ||||
| 			.join(":") | ||||
| 			.replace(/^\[/, "") | ||||
| 			.replace(/\]$/, ""); | ||||
| 		var args = []; | ||||
| 		var httpType; | ||||
| 		var server; | ||||
| 		var validHttpPort = parseInt(p, 10) >= 0; | ||||
| 
 | ||||
|     if (addr) { args[1] = addr; } | ||||
|     if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) { | ||||
|       console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe"); | ||||
|     } | ||||
| 		if (addr) { | ||||
| 			args[1] = addr; | ||||
| 		} | ||||
| 		if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) { | ||||
| 			console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe"); | ||||
| 		} | ||||
| 
 | ||||
|     server = require('http').createServer( | ||||
|       greenlock.middleware.sanitizeHost(greenlock.middleware(require('redirect-https')())) | ||||
|     ); | ||||
|     httpType = 'http'; | ||||
| 		server = require("http").createServer( | ||||
| 			greenlock.middleware.sanitizeHost(greenlock.middleware(require("redirect-https")())) | ||||
| 		); | ||||
| 		httpType = "http"; | ||||
| 
 | ||||
|     return { server: server, listen: function () { return new PromiseA(function (resolve, reject) { | ||||
|       args[0] = p; | ||||
|       args.push(function () { | ||||
|         if (!greenlock.servername) { | ||||
|           if (Array.isArray(greenlock.approvedDomains) && greenlock.approvedDomains.length) { | ||||
|             greenlock.servername = greenlock.approvedDomains[0]; | ||||
|           } | ||||
|           if (Array.isArray(greenlock.approveDomains) && greenlock.approvedDomains.length) { | ||||
|             greenlock.servername = greenlock.approvedDomains[0]; | ||||
|           } | ||||
|         } | ||||
| 		return { | ||||
| 			server: server, | ||||
| 			listen: function() { | ||||
| 				return new PromiseA(function(resolve, reject) { | ||||
| 					args[0] = p; | ||||
| 					args.push(function() { | ||||
| 						if (!greenlock.servername) { | ||||
| 							if (Array.isArray(greenlock.approvedDomains) && greenlock.approvedDomains.length) { | ||||
| 								greenlock.servername = greenlock.approvedDomains[0]; | ||||
| 							} | ||||
| 							if (Array.isArray(greenlock.approveDomains) && greenlock.approvedDomains.length) { | ||||
| 								greenlock.servername = greenlock.approvedDomains[0]; | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
|         if (!greenlock.servername) { | ||||
|           resolve(null); | ||||
|           return; | ||||
|         } | ||||
| 						if (!greenlock.servername) { | ||||
| 							resolve(null); | ||||
| 							return; | ||||
| 						} | ||||
| 
 | ||||
|         return greenlock.check({ domains: [ greenlock.servername ] }).then(function (certs) { | ||||
|           if (certs) { | ||||
|             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
 | ||||
|             greenlock.tlsOptions.SNICallback(greenlock.servername, function (err/*, secureContext*/) { | ||||
|               if (err) { reject(err); return; } | ||||
|               return greenlock.check({ domains: [ greenlock.servername ] }).then(function (certs) { | ||||
|                 resolve({ | ||||
|                   key: Buffer.from(certs.privkey, 'ascii') | ||||
|                 , cert: Buffer.from(certs.cert + '\r\n' + certs.chain, 'ascii') | ||||
|                 }); | ||||
|               }).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); | ||||
|         } | ||||
|       }); | ||||
|     }); } }; | ||||
|   } | ||||
| 						return greenlock | ||||
| 							.check({ domains: [greenlock.servername] }) | ||||
| 							.then(function(certs) { | ||||
| 								if (certs) { | ||||
| 									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
 | ||||
| 									greenlock.tlsOptions.SNICallback(greenlock.servername, function(err /*, secureContext*/) { | ||||
| 										if (err) { | ||||
| 											reject(err); | ||||
| 											return; | ||||
| 										} | ||||
| 										return greenlock | ||||
| 											.check({ domains: [greenlock.servername] }) | ||||
| 											.then(function(certs) { | ||||
| 												resolve({ | ||||
| 													key: Buffer.from(certs.privkey, "ascii"), | ||||
| 													cert: Buffer.from(certs.cert + "\r\n" + certs.chain, "ascii") | ||||
| 												}); | ||||
| 											}) | ||||
| 											.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) { | ||||
|     if (!port) { port = 443; } | ||||
| 	function _create(port) { | ||||
| 		if (!port) { | ||||
| 			port = 443; | ||||
| 		} | ||||
| 
 | ||||
|     var parts = String(port).split(':'); | ||||
|     var p = parts.pop(); | ||||
|     var addr = parts.join(':').replace(/^\[/, '').replace(/\]$/, ''); | ||||
|     var args = []; | ||||
|     var httpType; | ||||
|     var server; | ||||
|     var validHttpPort = (parseInt(p, 10) >= 0); | ||||
| 		var parts = String(port).split(":"); | ||||
| 		var p = parts.pop(); | ||||
| 		var addr = parts | ||||
| 			.join(":") | ||||
| 			.replace(/^\[/, "") | ||||
| 			.replace(/\]$/, ""); | ||||
| 		var args = []; | ||||
| 		var httpType; | ||||
| 		var server; | ||||
| 		var validHttpPort = parseInt(p, 10) >= 0; | ||||
| 
 | ||||
|     if (addr) { args[1] = addr; } | ||||
|     if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) { | ||||
|       console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe"); | ||||
|     } | ||||
| 		if (addr) { | ||||
| 			args[1] = addr; | ||||
| 		} | ||||
| 		if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) { | ||||
| 			console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe"); | ||||
| 		} | ||||
| 
 | ||||
|     var https; | ||||
|     try { | ||||
|       https = require('spdy'); | ||||
|       greenlock.tlsOptions.spdy = { protocols: [ 'h2', 'http/1.1' ], plain: false }; | ||||
|       httpType = 'http2 (spdy/h2)'; | ||||
|     } catch(e) { | ||||
|       https = require('https'); | ||||
|       httpType = 'https'; | ||||
|     } | ||||
|     var sniCallback = greenlock.tlsOptions.SNICallback; | ||||
|     greenlock.tlsOptions.SNICallback = function (domain, cb) { | ||||
|       sniCallback(domain, function (err, context) { | ||||
|         cb(err, context); | ||||
| 		var https; | ||||
| 		try { | ||||
| 			https = require("spdy"); | ||||
| 			greenlock.tlsOptions.spdy = { protocols: ["h2", "http/1.1"], plain: false }; | ||||
| 			httpType = "http2 (spdy/h2)"; | ||||
| 		} catch (e) { | ||||
| 			https = require("https"); | ||||
| 			httpType = "https"; | ||||
| 		} | ||||
| 		var sniCallback = greenlock.tlsOptions.SNICallback; | ||||
| 		greenlock.tlsOptions.SNICallback = function(domain, cb) { | ||||
| 			sniCallback(domain, function(err, context) { | ||||
| 				cb(err, context); | ||||
| 
 | ||||
|         if (!context || server._hasDefaultSecureContext) { return; } | ||||
|         if (!domain) { domain = greenlock.servername; } | ||||
|         if (!domain) { return; } | ||||
| 				if (!context || server._hasDefaultSecureContext) { | ||||
| 					return; | ||||
| 				} | ||||
| 				if (!domain) { | ||||
| 					domain = greenlock.servername; | ||||
| 				} | ||||
| 				if (!domain) { | ||||
| 					return; | ||||
| 				} | ||||
| 
 | ||||
|         return greenlock.check({ domains: [ domain ] }).then(function (certs) { | ||||
|           // ignore the case that check doesn't have all the right args here
 | ||||
|           // to get the same certs that it just got (eventually the right ones will come in)
 | ||||
|           if (!certs) { return; } | ||||
|           if (server.setSecureContext) { | ||||
|             // only available in node v11.0+
 | ||||
|             server.setSecureContext({ | ||||
|               key: Buffer.from(certs.privkey, 'ascii') | ||||
|             , cert: Buffer.from(certs.cert + '\r\n' + certs.chain, 'ascii') | ||||
|             }); | ||||
|             console.info("Using '%s' as default certificate", domain); | ||||
|           } else { | ||||
|             console.info("Setting default certificates dynamically requires node v11.0+. Skipping."); | ||||
|           } | ||||
|           server._hasDefaultSecureContext = true; | ||||
|         }).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
 | ||||
|           //console.warn("Unusual error: couldn't get newly authorized certificate:");
 | ||||
|           //console.warn(e.message);
 | ||||
|         }); | ||||
|       }); | ||||
|     }; | ||||
|     if (greenlock.tlsOptions.cert) { | ||||
|       server._hasDefaultSecureContext = true; | ||||
|       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)"); | ||||
|       } | ||||
|     } | ||||
|     server = https.createServer( | ||||
|       greenlock.tlsOptions | ||||
|     , greenlock.middleware.sanitizeHost(function (req, res) { | ||||
|         try { | ||||
|           greenlock.app(req, res); | ||||
|         } catch(e) { | ||||
|           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."); | ||||
|           } catch(e) { | ||||
|             // ignore
 | ||||
|             // (headers may have already been sent, etc)
 | ||||
|           } | ||||
|         } | ||||
|       }) | ||||
|     ); | ||||
|     server.type = httpType; | ||||
| 				return greenlock | ||||
| 					.check({ domains: [domain] }) | ||||
| 					.then(function(certs) { | ||||
| 						// ignore the case that check doesn't have all the right args here
 | ||||
| 						// to get the same certs that it just got (eventually the right ones will come in)
 | ||||
| 						if (!certs) { | ||||
| 							return; | ||||
| 						} | ||||
| 						if (server.setSecureContext) { | ||||
| 							// only available in node v11.0+
 | ||||
| 							server.setSecureContext({ | ||||
| 								key: Buffer.from(certs.privkey, "ascii"), | ||||
| 								cert: Buffer.from(certs.cert + "\r\n" + certs.chain, "ascii") | ||||
| 							}); | ||||
| 							console.info("Using '%s' as default certificate", domain); | ||||
| 						} else { | ||||
| 							console.info("Setting default certificates dynamically requires node v11.0+. Skipping."); | ||||
| 						} | ||||
| 						server._hasDefaultSecureContext = true; | ||||
| 					}) | ||||
| 					.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
 | ||||
| 						//console.warn("Unusual error: couldn't get newly authorized certificate:");
 | ||||
| 						//console.warn(e.message);
 | ||||
| 					}); | ||||
| 			}); | ||||
| 		}; | ||||
| 		if (greenlock.tlsOptions.cert) { | ||||
| 			server._hasDefaultSecureContext = true; | ||||
| 			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)" | ||||
| 				); | ||||
| 			} | ||||
| 		} | ||||
| 		server = https.createServer( | ||||
| 			greenlock.tlsOptions, | ||||
| 			greenlock.middleware.sanitizeHost(function(req, res) { | ||||
| 				try { | ||||
| 					greenlock.app(req, res); | ||||
| 				} catch (e) { | ||||
| 					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."); | ||||
| 					} catch (e) { | ||||
| 						// ignore
 | ||||
| 						// (headers may have already been sent, etc)
 | ||||
| 					} | ||||
| 				} | ||||
| 			}) | ||||
| 		); | ||||
| 		server.type = httpType; | ||||
| 
 | ||||
|     return { server: server, listen: function () { return new PromiseA(function (resolve) { | ||||
|       args[0] = p; | ||||
|       args.push(function () { resolve(/*server*/); }); | ||||
|       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); | ||||
|         } | ||||
|       }); | ||||
|     }); } }; | ||||
|   } | ||||
| 		return { | ||||
| 			server: server, | ||||
| 			listen: function() { | ||||
| 				return new PromiseA(function(resolve) { | ||||
| 					args[0] = p; | ||||
| 					args.push(function() { | ||||
| 						resolve(/*server*/); | ||||
| 					}); | ||||
| 					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
 | ||||
|   var greenlock = require('greenlock').create(opts); | ||||
| 	// NOTE: 'greenlock' is just 'opts' renamed
 | ||||
| 	var greenlock = require("greenlock").create(opts); | ||||
| 
 | ||||
|   if (!opts.app) { | ||||
|     opts.app = function (req, res) { | ||||
|       res.end("Hello, World!\nWith Love,\nGreenlock for Express.js"); | ||||
|     }; | ||||
|   } | ||||
| 	if (!opts.app) { | ||||
| 		opts.app = function(req, res) { | ||||
| 			res.end("Hello, World!\nWith Love,\nGreenlock for Express.js"); | ||||
| 		}; | ||||
| 	} | ||||
| 
 | ||||
|   opts.listen = function (plainPort, port, fnPlain, fn) { | ||||
|     var server; | ||||
|     var plainServer; | ||||
| 	opts.listen = function(plainPort, port, fnPlain, fn) { | ||||
| 		var server; | ||||
| 		var plainServer; | ||||
| 
 | ||||
|     // If there is only one handler for the `listening` (i.e. TCP bound) event
 | ||||
|     // then we want to use it as HTTPS (backwards compat)
 | ||||
|     if (!fn) { | ||||
|       fn = fnPlain; | ||||
|       fnPlain = null; | ||||
|     } | ||||
| 		// If there is only one handler for the `listening` (i.e. TCP bound) event
 | ||||
| 		// then we want to use it as HTTPS (backwards compat)
 | ||||
| 		if (!fn) { | ||||
| 			fn = fnPlain; | ||||
| 			fnPlain = null; | ||||
| 		} | ||||
| 
 | ||||
|     var obj1 = _createPlain(plainPort, true); | ||||
|     var obj2 = _create(port, false); | ||||
| 		var obj1 = _createPlain(plainPort, true); | ||||
| 		var obj2 = _create(port, false); | ||||
| 
 | ||||
|     plainServer = obj1.server; | ||||
|     server = obj2.server; | ||||
| 		plainServer = obj1.server; | ||||
| 		server = obj2.server; | ||||
| 
 | ||||
|     server.then = obj1.listen().then(function (tlsOptions) { | ||||
|       if (tlsOptions) { | ||||
|         if (server.setSecureContext) { | ||||
|           // only available in node v11.0+
 | ||||
|           server.setSecureContext(tlsOptions); | ||||
|           console.info("Using '%s' as default certificate", greenlock.servername); | ||||
|         } else { | ||||
|           console.info("Setting default certificates dynamically requires node v11.0+. Skipping."); | ||||
|         } | ||||
|         server._hasDefaultSecureContext = true; | ||||
|       } | ||||
|       return obj2.listen().then(function () { | ||||
|         // Report plain http status
 | ||||
|         if ('function' === typeof fnPlain) { | ||||
|           fnPlain.apply(plainServer); | ||||
|         } else if (!fn && !plainServer.listenerCount('listening') && !server.listenerCount('listening')) { | ||||
|           console.info('[:' + (plainServer.address().port || plainServer.address()) | ||||
|             + "] Handling ACME challenges and redirecting to " + server.type); | ||||
|         } | ||||
| 		server.then = obj1.listen().then(function(tlsOptions) { | ||||
| 			if (tlsOptions) { | ||||
| 				if (server.setSecureContext) { | ||||
| 					// only available in node v11.0+
 | ||||
| 					server.setSecureContext(tlsOptions); | ||||
| 					console.info("Using '%s' as default certificate", greenlock.servername); | ||||
| 				} else { | ||||
| 					console.info("Setting default certificates dynamically requires node v11.0+. Skipping."); | ||||
| 				} | ||||
| 				server._hasDefaultSecureContext = true; | ||||
| 			} | ||||
| 			return obj2.listen().then(function() { | ||||
| 				// Report plain http status
 | ||||
| 				if ("function" === typeof fnPlain) { | ||||
| 					fnPlain.apply(plainServer); | ||||
| 				} else if (!fn && !plainServer.listenerCount("listening") && !server.listenerCount("listening")) { | ||||
| 					console.info( | ||||
| 						"[:" + | ||||
| 							(plainServer.address().port || plainServer.address()) + | ||||
| 							"] Handling ACME challenges and redirecting to " + | ||||
| 							server.type | ||||
| 					); | ||||
| 				} | ||||
| 
 | ||||
|         // Report h2/https status
 | ||||
|         if ('function' === typeof fn) { | ||||
|           fn.apply(server); | ||||
|         } else if (!server.listenerCount('listening')) { | ||||
|           console.info('[:' + (server.address().port || server.address()) + "] Serving " + server.type); | ||||
|         } | ||||
|       }); | ||||
|     }).then; | ||||
| 				// Report h2/https status
 | ||||
| 				if ("function" === typeof fn) { | ||||
| 					fn.apply(server); | ||||
| 				} else if (!server.listenerCount("listening")) { | ||||
| 					console.info("[:" + (server.address().port || server.address()) + "] Serving " + server.type); | ||||
| 				} | ||||
| 			}); | ||||
| 		}).then; | ||||
| 
 | ||||
|     server.unencrypted = plainServer; | ||||
|     return server; | ||||
|   }; | ||||
|   opts.middleware.acme = function (opts) { | ||||
|     return greenlock.middleware.sanitizeHost(greenlock.middleware(require('redirect-https')(opts))); | ||||
|   }; | ||||
|   opts.middleware.secure = function (app) { | ||||
|     return greenlock.middleware.sanitizeHost(app); | ||||
|   }; | ||||
| 		server.unencrypted = plainServer; | ||||
| 		return server; | ||||
| 	}; | ||||
| 	opts.middleware.acme = function(opts) { | ||||
| 		return greenlock.middleware.sanitizeHost(greenlock.middleware(require("redirect-https")(opts))); | ||||
| 	}; | ||||
| 	opts.middleware.secure = function(app) { | ||||
| 		return greenlock.middleware.sanitizeHost(app); | ||||
| 	}; | ||||
| 
 | ||||
|   return greenlock; | ||||
| 	return greenlock; | ||||
| }; | ||||
|  | ||||
| @ -1,37 +1,37 @@ | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| 
 | ||||
| function requireBluebird() { | ||||
|   try { | ||||
|     return require('bluebird'); | ||||
|   } catch(e) { | ||||
|     console.error(""); | ||||
|     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(""); | ||||
|     throw e; | ||||
|   } | ||||
| 	try { | ||||
| 		return require("bluebird"); | ||||
| 	} catch (e) { | ||||
| 		console.error(""); | ||||
| 		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(""); | ||||
| 		throw e; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| if ('undefined' === typeof Promise) { | ||||
|   global.Promise = requireBluebird(); | ||||
| if ("undefined" === typeof Promise) { | ||||
| 	global.Promise = requireBluebird(); | ||||
| } | ||||
| 
 | ||||
| if ('function' !== typeof require('util').promisify) { | ||||
|   require('util').promisify = requireBluebird().promisify; | ||||
| if ("function" !== typeof require("util").promisify) { | ||||
| 	require("util").promisify = requireBluebird().promisify; | ||||
| } | ||||
| 
 | ||||
| if (!console.debug) { | ||||
|   console.debug = console.log; | ||||
| 	console.debug = console.log; | ||||
| } | ||||
| 
 | ||||
| var fs = require('fs'); | ||||
| var fs = require("fs"); | ||||
| var fsAsync = {}; | ||||
| Object.keys(fs).forEach(function (key) { | ||||
|   var fn = fs[key]; | ||||
|   if ('function' !== typeof fn || !/[a-z]/.test(key[0])) { | ||||
|     return; | ||||
|   } | ||||
|   fsAsync[key] = require('util').promisify(fn); | ||||
| Object.keys(fs).forEach(function(key) { | ||||
| 	var fn = fs[key]; | ||||
| 	if ("function" !== typeof fn || !/[a-z]/.test(key[0])) { | ||||
| 		return; | ||||
| 	} | ||||
| 	fsAsync[key] = require("util").promisify(fn); | ||||
| }); | ||||
| 
 | ||||
| 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", | ||||
|   "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.", | ||||
|   "main": "index.js", | ||||
|   "homepage": "https://greenlock.domains", | ||||
|   "directories": { | ||||
|     "example": "examples" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "greenlock": "^2.7.24", | ||||
|     "redirect-https": "^1.1.5" | ||||
|   }, | ||||
|   "files": [ | ||||
|     "lib" | ||||
|   ], | ||||
|   "trulyOptionalDependencies": { | ||||
|     "spdy": "^3.4.7" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "express": "^4.16.3", | ||||
|     "express-basic-auth": "^1.1.5", | ||||
|     "finalhandler": "^1.1.1", | ||||
|     "serve-index": "^1.9.1", | ||||
|     "serve-static": "^1.13.2", | ||||
|     "ws": "^5.2.1" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "start": "node server.js ./config.js", | ||||
|     "test": "node test/greenlock.js" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "https://git.rootprojects.org/root/greenlock-express.js.git" | ||||
|   }, | ||||
|   "keywords": [ | ||||
|     "Let's Encrypt", | ||||
|     "ACME", | ||||
|     "greenlock", | ||||
|     "Free SSL", | ||||
|     "Automated HTTPS", | ||||
|     "https", | ||||
|     "tls" | ||||
|   ], | ||||
|   "author": "AJ ONeal <solderjs@gmail.com> (https://solderjs.com/)", | ||||
|   "license": "MPL-2.0", | ||||
|   "bugs": { | ||||
|     "url": "https://git.rootprojects.org/root/greenlock-express.js/issues" | ||||
|   } | ||||
| 	"name": "greenlock-express", | ||||
| 	"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.", | ||||
| 	"main": "index.js", | ||||
| 	"homepage": "https://greenlock.domains", | ||||
| 	"directories": { | ||||
| 		"example": "examples" | ||||
| 	}, | ||||
| 	"dependencies": { | ||||
| 		"greenlock": "^2.7.24", | ||||
| 		"redirect-https": "^1.1.5" | ||||
| 	}, | ||||
| 	"files": [ | ||||
| 		"lib" | ||||
| 	], | ||||
| 	"trulyOptionalDependencies": { | ||||
| 		"spdy": "^3.4.7" | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
| 		"express": "^4.16.3", | ||||
| 		"express-basic-auth": "^1.1.5", | ||||
| 		"finalhandler": "^1.1.1", | ||||
| 		"serve-index": "^1.9.1", | ||||
| 		"serve-static": "^1.13.2", | ||||
| 		"ws": "^5.2.1" | ||||
| 	}, | ||||
| 	"scripts": { | ||||
| 		"start": "node server.js ./config.js", | ||||
| 		"test": "node test/greenlock.js" | ||||
| 	}, | ||||
| 	"repository": { | ||||
| 		"type": "git", | ||||
| 		"url": "https://git.rootprojects.org/root/greenlock-express.js.git" | ||||
| 	}, | ||||
| 	"keywords": [ | ||||
| 		"Let's Encrypt", | ||||
| 		"ACME", | ||||
| 		"greenlock", | ||||
| 		"Free SSL", | ||||
| 		"Automated HTTPS", | ||||
| 		"https", | ||||
| 		"tls" | ||||
| 	], | ||||
| 	"author": "AJ ONeal <solderjs@gmail.com> (https://solderjs.com/)", | ||||
| 	"license": "MPL-2.0", | ||||
| 	"bugs": { | ||||
| 		"url": "https://git.rootprojects.org/root/greenlock-express.js/issues" | ||||
| 	} | ||||
| } | ||||
|  | ||||
							
								
								
									
										336
									
								
								server.js
									
									
									
									
									
								
							
							
						
						
									
										336
									
								
								server.js
									
									
									
									
									
								
							| @ -1,5 +1,5 @@ | ||||
| #!/usr/bin/env node
 | ||||
| 'use strict'; | ||||
| "use strict"; | ||||
| /*global Promise*/ | ||||
| 
 | ||||
| /////////////////////////////////
 | ||||
| @ -15,189 +15,219 @@ | ||||
| // ex: /srv/api/api.example.com
 | ||||
| //
 | ||||
| 
 | ||||
| var configpath = process.argv[2] || './config.js'; | ||||
| var configpath = process.argv[2] || "./config.js"; | ||||
| var config = require(configpath); | ||||
| // The prefix where sites go by name.
 | ||||
| // For example: whatever.com may live in /srv/www/whatever.com, thus /srv/www is our path
 | ||||
| 
 | ||||
| var path = require('path'); | ||||
| var fs = require('./lib/compat.js').fsAsync; | ||||
| var finalhandler = require('finalhandler'); | ||||
| var serveStatic = require('serve-static'); | ||||
| var path = require("path"); | ||||
| var fs = require("./lib/compat.js").fsAsync; | ||||
| var finalhandler = require("finalhandler"); | ||||
| var serveStatic = require("serve-static"); | ||||
| 
 | ||||
| //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'
 | ||||
| , server: 'https://acme-v02.api.letsencrypt.org/directory'  // If at first you don't succeed, stop and switch to staging
 | ||||
|                                                             // https://acme-staging-v02.api.letsencrypt.org/directory
 | ||||
| 	configDir: config.configDir, // You MUST have access to write to directory where certs
 | ||||
| 	// are saved. ex: /home/foouser/.config/acme
 | ||||
| 
 | ||||
| , configDir: config.configDir                               // You MUST have access to write to directory where certs
 | ||||
|                                                             // are saved. ex: /home/foouser/.config/acme
 | ||||
| 	approveDomains: myApproveDomains, // Greenlock's wraps around tls.SNICallback. Check the
 | ||||
| 	// domain name here and reject invalid ones
 | ||||
| 
 | ||||
| , approveDomains: myApproveDomains                          // Greenlock's wraps around tls.SNICallback. Check the
 | ||||
|                                                             // domain name here and reject invalid ones
 | ||||
| 	app: myVhostApp, // Any node-style http app (i.e. express, koa, hapi, rill)
 | ||||
| 
 | ||||
| , 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
 | ||||
| , agreeTos: true                                            // Accept Let's Encrypt ToS
 | ||||
| //, communityMember: true                                   // Join Greenlock to get important updates, no spam
 | ||||
| 
 | ||||
| //, debug: true
 | ||||
| , store: require('greenlock-store-fs') | ||||
| 	/* CHANGE TO A VALID EMAIL */ | ||||
| 	email: config.email, // Email for Let's Encrypt account and Greenlock Security
 | ||||
| 	agreeTos: true, // Accept Let's Encrypt ToS
 | ||||
| 	//, communityMember: true                                   // Join Greenlock to get important updates, no spam
 | ||||
| 
 | ||||
| 	//, debug: true
 | ||||
| 	store: require("greenlock-store-fs") | ||||
| }); | ||||
| 
 | ||||
| var server = glx.listen(80, 443); | ||||
| server.on('listening', function () { | ||||
|   console.info(server.type + " listening on", server.address()); | ||||
| server.on("listening", function() { | ||||
| 	console.info(server.type + " listening on", server.address()); | ||||
| }); | ||||
| 
 | ||||
| function myApproveDomains(opts) { | ||||
|   console.info("SNI:", opts.domain); | ||||
|   // In this example the filesystem is our "database".
 | ||||
|   // We check in /srv/www for whatever.com and if it exists, it's allowed
 | ||||
|   // SECURITY Greenlock validates opts.domains ahead-of-time so you don't have to
 | ||||
| 	console.info("SNI:", opts.domain); | ||||
| 	// In this example the filesystem is our "database".
 | ||||
| 	// We check in /srv/www for whatever.com and if it exists, it's allowed
 | ||||
| 	// SECURITY Greenlock validates opts.domains ahead-of-time so you don't have to
 | ||||
| 
 | ||||
|   var domains = []; | ||||
|   var domain = opts.domain.replace(/^(www|api)\./, ''); | ||||
|   return checkWwws(domain).then(function (hostname) { | ||||
|     // this is either example.com or www.example.com
 | ||||
|     domains.push(hostname); | ||||
|     if ('api.' + domain !== opts.domain) { | ||||
|       if (!domains.includes(opts.domain)) { | ||||
|         domains.push(opts.domain) | ||||
|       } | ||||
|     } | ||||
|   }).catch(function () { | ||||
|     // ignore error
 | ||||
|     return null; | ||||
|   }).then(function () { | ||||
|     // check for api prefix
 | ||||
|     var apiname = domain; | ||||
|     if (domains.length) { | ||||
|       apiname = 'api.' + domain; | ||||
|     } | ||||
|     return checkApi(apiname).then(function (app) { | ||||
|       if (!app) { return null; } | ||||
|       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 + "'")); | ||||
|     } | ||||
| 	var domains = []; | ||||
| 	var domain = opts.domain.replace(/^(www|api)\./, ""); | ||||
| 	return checkWwws(domain) | ||||
| 		.then(function(hostname) { | ||||
| 			// this is either example.com or www.example.com
 | ||||
| 			domains.push(hostname); | ||||
| 			if ("api." + domain !== opts.domain) { | ||||
| 				if (!domains.includes(opts.domain)) { | ||||
| 					domains.push(opts.domain); | ||||
| 				} | ||||
| 			} | ||||
| 		}) | ||||
| 		.catch(function() { | ||||
| 			// ignore error
 | ||||
| 			return null; | ||||
| 		}) | ||||
| 		.then(function() { | ||||
| 			// check for api prefix
 | ||||
| 			var apiname = domain; | ||||
| 			if (domains.length) { | ||||
| 				apiname = "api." + domain; | ||||
| 			} | ||||
| 			return checkApi(apiname) | ||||
| 				.then(function(app) { | ||||
| 					if (!app) { | ||||
| 						return null; | ||||
| 					} | ||||
| 					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); | ||||
|     opts.domains = domains; | ||||
|     //opts.email = email;
 | ||||
|     opts.agreeTos = true; | ||||
|     // pick the shortest (bare) or latest (www. instead of api.) to be the subject
 | ||||
|     opts.subject = opts.domains.sort(function (a, b) { | ||||
|       var len = a.length - b.length; | ||||
|       if (0 !== len) { return len; } | ||||
|       if (a < b) { return 1; } else { return -1; } | ||||
|     })[0]; | ||||
| 			console.info("Approved domains:", domains); | ||||
| 			opts.domains = domains; | ||||
| 			//opts.email = email;
 | ||||
| 			opts.agreeTos = true; | ||||
| 			// pick the shortest (bare) or latest (www. instead of api.) to be the subject
 | ||||
| 			opts.subject = opts.domains.sort(function(a, b) { | ||||
| 				var len = a.length - b.length; | ||||
| 				if (0 !== len) { | ||||
| 					return len; | ||||
| 				} | ||||
| 				if (a < b) { | ||||
| 					return 1; | ||||
| 				} else { | ||||
| 					return -1; | ||||
| 				} | ||||
| 			})[0]; | ||||
| 
 | ||||
|     if (!opts.challenges) { opts.challenges = {}; } | ||||
|     opts.challenges['http-01'] = require('le-challenge-fs'); | ||||
|     //opts.challenges['dns-01'] = require('le-challenge-dns');
 | ||||
| 			if (!opts.challenges) { | ||||
| 				opts.challenges = {}; | ||||
| 			} | ||||
| 			opts.challenges["http-01"] = require("le-challenge-fs"); | ||||
| 			//opts.challenges['dns-01'] = require('le-challenge-dns');
 | ||||
| 
 | ||||
|     // explicitly set account id and certificate.id
 | ||||
|     opts.account = { id: opts.email }; | ||||
|     opts.certificate = { id: opts.subject }; | ||||
| 			// explicitly set account id and certificate.id
 | ||||
| 			opts.account = { id: opts.email }; | ||||
| 			opts.certificate = { id: opts.subject }; | ||||
| 
 | ||||
|     return Promise.resolve(opts); | ||||
|   }); | ||||
| 			return Promise.resolve(opts); | ||||
| 		}); | ||||
| } | ||||
| 
 | ||||
| function checkApi(hostname) { | ||||
|   var apipath = path.join(config.api, hostname); | ||||
|   var link = ''; | ||||
|   return fs.stat(apipath).then(function (stats) { | ||||
|     if (stats.isDirectory()) { | ||||
|       return require(apipath); | ||||
|     } | ||||
|     return fs.readFile(apipath, 'utf8').then(function (txt) { | ||||
|       var linkpath = txt.split('\n')[0]; | ||||
|       link = (' => ' + linkpath + ' '); | ||||
|       return require(linkpath); | ||||
|     }); | ||||
|   }).catch(function (e) { | ||||
|     if ('ENOENT' === e.code) { return null; } | ||||
|     console.error(e); | ||||
|     throw new Error("rejecting '" + hostname + "' because '" + apipath + link + "' failed at require()"); | ||||
|   }); | ||||
| 	var apipath = path.join(config.api, hostname); | ||||
| 	var link = ""; | ||||
| 	return fs | ||||
| 		.stat(apipath) | ||||
| 		.then(function(stats) { | ||||
| 			if (stats.isDirectory()) { | ||||
| 				return require(apipath); | ||||
| 			} | ||||
| 			return fs.readFile(apipath, "utf8").then(function(txt) { | ||||
| 				var linkpath = txt.split("\n")[0]; | ||||
| 				link = " => " + linkpath + " "; | ||||
| 				return require(linkpath); | ||||
| 			}); | ||||
| 		}) | ||||
| 		.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) { | ||||
|   if (!_hostname) { | ||||
|     // SECURITY don't serve the whole config.srv
 | ||||
|     return Promise.reject(new Error("missing hostname")); | ||||
|   } | ||||
|   var hostname = _hostname; | ||||
|   var hostdir = path.join(config.srv, hostname); | ||||
|   // TODO could test for www/no-www both in directory
 | ||||
|   return fs.readdir(hostdir).then(function () { | ||||
|     // TODO check for some sort of htaccess.json and use email in that
 | ||||
|     // NOTE: you can also change other options such as `challengeType` and `challenge`
 | ||||
|     // opts.challengeType = 'http-01';
 | ||||
|     // opts.challenge = require('le-challenge-fs').create({});
 | ||||
|     return hostname; | ||||
|   }).catch(function () { | ||||
|     if ('www.' === hostname.slice(0, 4)) { | ||||
|       // Assume we'll redirect to non-www if it's available.
 | ||||
|       hostname = hostname.slice(4); | ||||
|       hostdir = path.join(config.srv, hostname); | ||||
|       return fs.readdir(hostdir).then(function () { | ||||
|         return hostname; | ||||
|       }); | ||||
|     } else { | ||||
|       // Or check and see if perhaps we should redirect non-www to www
 | ||||
|       hostname = 'www.' + hostname; | ||||
|       hostdir = path.join(config.srv, hostname); | ||||
|       return fs.readdir(hostdir).then(function () { | ||||
|         return hostname; | ||||
|       }); | ||||
|     } | ||||
|   }).catch(function () { | ||||
|     throw new Error("rejecting '" + _hostname + "' because '" + hostdir + "' could not be read"); | ||||
|   }); | ||||
| 	if (!_hostname) { | ||||
| 		// SECURITY don't serve the whole config.srv
 | ||||
| 		return Promise.reject(new Error("missing hostname")); | ||||
| 	} | ||||
| 	var hostname = _hostname; | ||||
| 	var hostdir = path.join(config.srv, hostname); | ||||
| 	// TODO could test for www/no-www both in directory
 | ||||
| 	return fs | ||||
| 		.readdir(hostdir) | ||||
| 		.then(function() { | ||||
| 			// TODO check for some sort of htaccess.json and use email in that
 | ||||
| 			// NOTE: you can also change other options such as `challengeType` and `challenge`
 | ||||
| 			// opts.challengeType = 'http-01';
 | ||||
| 			// opts.challenge = require('le-challenge-fs').create({});
 | ||||
| 			return hostname; | ||||
| 		}) | ||||
| 		.catch(function() { | ||||
| 			if ("www." === hostname.slice(0, 4)) { | ||||
| 				// Assume we'll redirect to non-www if it's available.
 | ||||
| 				hostname = hostname.slice(4); | ||||
| 				hostdir = path.join(config.srv, hostname); | ||||
| 				return fs.readdir(hostdir).then(function() { | ||||
| 					return hostname; | ||||
| 				}); | ||||
| 			} else { | ||||
| 				// Or check and see if perhaps we should redirect non-www to www
 | ||||
| 				hostname = "www." + hostname; | ||||
| 				hostdir = path.join(config.srv, hostname); | ||||
| 				return fs.readdir(hostdir).then(function() { | ||||
| 					return hostname; | ||||
| 				}); | ||||
| 			} | ||||
| 		}) | ||||
| 		.catch(function() { | ||||
| 			throw new Error("rejecting '" + _hostname + "' because '" + hostdir + "' could not be read"); | ||||
| 		}); | ||||
| } | ||||
| 
 | ||||
| function myVhostApp(req, res) { | ||||
|   // SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to
 | ||||
|   // (also: only domains approved above will get here)
 | ||||
|   console.info(req.method, (req.headers.host||'') + req.url); | ||||
|   Object.keys(req.headers).forEach(function (key) { | ||||
|     console.info(key, req.headers[key]) | ||||
|   }); | ||||
| 	// SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to
 | ||||
| 	// (also: only domains approved above will get here)
 | ||||
| 	console.info(req.method, (req.headers.host || "") + req.url); | ||||
| 	Object.keys(req.headers).forEach(function(key) { | ||||
| 		console.info(key, req.headers[key]); | ||||
| 	}); | ||||
| 
 | ||||
|   // We could cache wether or not a host exists for some amount of time
 | ||||
|   var fin = finalhandler(req, res); | ||||
|   return checkWwws(req.headers.host).then(function (hostname) { | ||||
|     if (hostname !== req.headers.host) { | ||||
|       res.statusCode = 302; | ||||
|       res.setHeader('Location', 'https://' + hostname); | ||||
|       // SECURITY this is safe only because greenlock disallows invalid hostnames
 | ||||
|       res.end("<!-- redirecting to https://" + hostname + "-->"); | ||||
|       return; | ||||
|     } | ||||
|     var serve = serveStatic(path.join(config.srv, hostname), { redirect: true }); | ||||
|     serve(req, res, fin); | ||||
|   }).catch(function (err) { | ||||
|     return checkApi(req.headers.host).then(function (app) { | ||||
|       if (app) { app(req, res); return; } | ||||
|       console.error("none found", err); | ||||
|       fin(); | ||||
|     }).catch(function (err) { | ||||
|       console.error("api crashed error", err); | ||||
|       fin(err); | ||||
|     }); | ||||
|   }); | ||||
| 	// We could cache wether or not a host exists for some amount of time
 | ||||
| 	var fin = finalhandler(req, res); | ||||
| 	return checkWwws(req.headers.host) | ||||
| 		.then(function(hostname) { | ||||
| 			if (hostname !== req.headers.host) { | ||||
| 				res.statusCode = 302; | ||||
| 				res.setHeader("Location", "https://" + hostname); | ||||
| 				// SECURITY this is safe only because greenlock disallows invalid hostnames
 | ||||
| 				res.end("<!-- redirecting to https://" + hostname + "-->"); | ||||
| 				return; | ||||
| 			} | ||||
| 			var serve = serveStatic(path.join(config.srv, hostname), { redirect: true }); | ||||
| 			serve(req, res, fin); | ||||
| 		}) | ||||
| 		.catch(function(err) { | ||||
| 			return checkApi(req.headers.host) | ||||
| 				.then(function(app) { | ||||
| 					if (app) { | ||||
| 						app(req, res); | ||||
| 						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
 | ||||
| var Greenlock = require('../'); | ||||
| var Greenlock = require("../"); | ||||
| var greenlock = Greenlock.create({ | ||||
|   version: 'draft-11' | ||||
| , server: 'https://acme-staging-v02.api.letsencrypt.org/directory' | ||||
| , agreeTos: true | ||||
| , approvedDomains: [ 'example.com', 'www.example.com' ] | ||||
| , configDir: require('path').join(require('os').tmpdir(), 'acme') | ||||
| 	version: "draft-11", | ||||
| 	server: "https://acme-staging-v02.api.letsencrypt.org/directory", | ||||
| 	agreeTos: true, | ||||
| 	approvedDomains: ["example.com", "www.example.com"], | ||||
| 	configDir: require("path").join(require("os").tmpdir(), "acme"), | ||||
| 
 | ||||
| , app: require('express')().use('/', function (req, res) { | ||||
|     res.setHeader('Content-Type', 'text/html; charset=utf-8'); | ||||
|     res.end('Hello, World!\n\n💚 🔒.js'); | ||||
|   }) | ||||
| 	app: require("express")().use("/", function(req, res) { | ||||
| 		res.setHeader("Content-Type", "text/html; charset=utf-8"); | ||||
| 		res.end("Hello, World!\n\n💚 🔒.js"); | ||||
| 	}) | ||||
| }); | ||||
| 
 | ||||
| var server1 = greenlock.listen(5080, 5443); | ||||
| server1.on('listening', function () { | ||||
|   console.log("### THREE 3333 - All is well server1", this.address()); | ||||
|   setTimeout(function () { | ||||
|     // so that the address() object doesn't disappear
 | ||||
|     server1.close(); | ||||
|     server1.unencrypted.close(); | ||||
|   }, 10); | ||||
| server1.on("listening", function() { | ||||
| 	console.log("### THREE 3333 - All is well server1", this.address()); | ||||
| 	setTimeout(function() { | ||||
| 		// so that the address() object doesn't disappear
 | ||||
| 		server1.close(); | ||||
| 		server1.unencrypted.close(); | ||||
| 	}, 10); | ||||
| }); | ||||
| setTimeout(function () { | ||||
|   var server2 = greenlock.listen(6080, 6443, function () { | ||||
|     console.log("### FIVE 55555 - Started server 2!"); | ||||
|     setTimeout(function () { | ||||
|       server2.close(); | ||||
|       server2.unencrypted.close(); | ||||
|       server6.close(); | ||||
|       server6.unencrypted.close(); | ||||
|       server7.close(); | ||||
|       server7.unencrypted.close(); | ||||
|       setTimeout(function () { | ||||
|         // TODO greenlock needs a close event (and to listen to its server's close event)
 | ||||
|         process.exit(0); | ||||
|       }, 1000); | ||||
|     }, 1000); | ||||
|   }); | ||||
|   server2.on('listening', function () { | ||||
|     console.log("### FOUR 44444 - All is well server2", server2.address()); | ||||
|   }); | ||||
| setTimeout(function() { | ||||
| 	var server2 = greenlock.listen(6080, 6443, function() { | ||||
| 		console.log("### FIVE 55555 - Started server 2!"); | ||||
| 		setTimeout(function() { | ||||
| 			server2.close(); | ||||
| 			server2.unencrypted.close(); | ||||
| 			server6.close(); | ||||
| 			server6.unencrypted.close(); | ||||
| 			server7.close(); | ||||
| 			server7.unencrypted.close(); | ||||
| 			setTimeout(function() { | ||||
| 				// TODO greenlock needs a close event (and to listen to its server's close event)
 | ||||
| 				process.exit(0); | ||||
| 			}, 1000); | ||||
| 		}, 1000); | ||||
| 	}); | ||||
| 	server2.on("listening", function() { | ||||
| 		console.log("### FOUR 44444 - All is well server2", server2.address()); | ||||
| 	}); | ||||
| }, 1000); | ||||
| 
 | ||||
| var server3 = greenlock.listen(22, 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"); | ||||
| var server3 = greenlock.listen( | ||||
| 	22, | ||||
| 	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 () { | ||||
|   console.log("Success: caught expected (plain) error"); | ||||
| }); | ||||
| server3.on('error', function () { | ||||
|   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 () { | ||||
|   console.log('Success: server4: plain'); | ||||
|   server4.unencrypted.close(); | ||||
| }, function () { | ||||
|   console.log('Success: server4: ' + server4.type); | ||||
|   server4.close(); | ||||
| var server4 = greenlock.listen( | ||||
| 	7080, | ||||
| 	7443, | ||||
| 	function() { | ||||
| 		console.log("Success: server4: plain"); | ||||
| 		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 () { | ||||
|   console.log("Server 5 with one fn", this.address()); | ||||
|   server5.close(); | ||||
|   server5.unencrypted.close(); | ||||
| }); | ||||
| var server6 = greenlock.listen("[::]:11080", "[::1]:11443"); | ||||
| 
 | ||||
| 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