mirror of
				https://github.com/therootcompany/greenlock-express.js.git
				synced 2024-11-16 17:28:59 +00:00 
			
		
		
		
	for unscoped npm package
This commit is contained in:
		
							parent
							
								
									ce5e31bbf7
								
							
						
					
					
						commit
						3e97142bf4
					
				
							
								
								
									
										20
									
								
								config.js
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								config.js
									
									
									
									
									
								
							| @ -1,20 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| var path = require("path"); |  | ||||||
| module.exports = { |  | ||||||
| 	email: "jon.doe@example.com", |  | ||||||
| 	configDir: path.join(__dirname, "acme"), |  | ||||||
| 	srv: "/srv/www/", |  | ||||||
| 	api: "/srv/api/", |  | ||||||
| 	proxy: { |  | ||||||
| 		"example.com": "http://localhost:4080", |  | ||||||
| 		"*.example.com": "http://localhost:4080" |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	// DNS-01 challenges only
 |  | ||||||
| 	challenges: { |  | ||||||
| 		"*.example.com": require("acme-dns-01-YOUR_DNS_HOST").create({ |  | ||||||
| 			token: "xxxx" |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
							
								
								
									
										35
									
								
								demo.js
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								demo.js
									
									
									
									
									
								
							| @ -1,35 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| require("./") |  | ||||||
| 	.init(initialize) |  | ||||||
| 	.serve(worker) |  | ||||||
| 	.master(function() { |  | ||||||
| 		console.log("Hello from master"); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| function initialize() { |  | ||||||
| 	var pkg = require("./package.json"); |  | ||||||
| 	var config = { |  | ||||||
| 		package: { |  | ||||||
| 			name: "Greenlock_Express_Demo", |  | ||||||
| 			version: pkg.version, |  | ||||||
| 			author: pkg.author |  | ||||||
| 		}, |  | ||||||
| 		staging: true, |  | ||||||
| 		cluster: true, |  | ||||||
| 
 |  | ||||||
| 		notify: function(ev, params) { |  | ||||||
| 			console.info(ev, params); |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| 	return config; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function worker(glx) { |  | ||||||
| 	console.info(); |  | ||||||
| 	console.info("Hello from worker #" + glx.id()); |  | ||||||
| 
 |  | ||||||
| 	glx.serveApp(function(req, res) { |  | ||||||
| 		res.end("Hello, Encrypted World!"); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
| @ -1,53 +0,0 @@ | |||||||
| # sudo systemctl daemon-reload |  | ||||||
| # sudo systemctl restart greenlock-express |  | ||||||
| # sudo journalctl -xefu greenlock-express |  | ||||||
| [Unit] |  | ||||||
| Description=Greenlock Static Server |  | ||||||
| Documentation=https://git.coolaj86.com/coolaj86/greenlock-express.js/ |  | ||||||
| After=network.target |  | ||||||
| Wants=network.target systemd-networkd-wait-online.service |  | ||||||
| 
 |  | ||||||
| [Service] |  | ||||||
| # Restart on crash (bad signal), 'clean' failure (error exit code), everything |  | ||||||
| # Allow up to 3 restarts within 10 seconds |  | ||||||
| # (it's unlikely that a user or properly-running script will do this) |  | ||||||
| Restart=always |  | ||||||
| StartLimitInterval=10 |  | ||||||
| StartLimitBurst=3 |  | ||||||
| 
 |  | ||||||
| # User and group the process will run as |  | ||||||
| # (git is the de facto standard on most systems) |  | ||||||
| User=ubuntu |  | ||||||
| Group=ubuntu |  | ||||||
| 
 |  | ||||||
| WorkingDirectory=/srv/www |  | ||||||
| # custom directory cannot be set and will be the place where gitea exists, not the working directory |  | ||||||
| ExecStart=/opt/node/bin/node /opt/greenlock-express.js/server.js /opt/greenlock-express.js/config.js |  | ||||||
| 
 |  | ||||||
| # Limit the number of file descriptors and processes; see `man systemd.exec` for more limit settings. |  | ||||||
| # greenlock is not expected to use more than this. |  | ||||||
| LimitNOFILE=1048576 |  | ||||||
| LimitNPROC=64 |  | ||||||
| 
 |  | ||||||
| # Use private /tmp and /var/tmp, which are discarded after gitea stops. |  | ||||||
| PrivateTmp=true |  | ||||||
| # Use a minimal /dev |  | ||||||
| PrivateDevices=true |  | ||||||
| # Hide /home, /root, and /run/user. Nobody will steal your SSH-keys. |  | ||||||
| ProtectHome=true |  | ||||||
| # Make /usr, /boot, /etc and possibly some more folders read-only. |  | ||||||
| ProtectSystem=full |  | ||||||
| # ... except /opt/greenlock-express.js/acme because we want a place for the database |  | ||||||
| # and /opt/greenlock-express.js/var because we want a place where logs can go. |  | ||||||
| # This merely retains r/w access rights, it does not add any new. |  | ||||||
| # Must still be writable on the host! |  | ||||||
| ReadWriteDirectories=/srv/www /opt/greenlock-express.js |  | ||||||
| 
 |  | ||||||
| # Note: in v231 and above ReadWritePaths has been renamed to ReadWriteDirectories |  | ||||||
| ; ReadWritePaths=/opt/gitea /var/log/gitea |  | ||||||
| 
 |  | ||||||
| # The following additional security directives only work with systemd v229 or later. |  | ||||||
| # They further retrict privileges that can be gained by gitea. |  | ||||||
| # Note that you may have to add capabilities required by any plugins in use. |  | ||||||
| CapabilityBoundingSet=CAP_NET_BIND_SERVICE |  | ||||||
| AmbientCapabilities=CAP_NET_BIND_SERVICE |  | ||||||
| @ -1,39 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| var pkg = require("../../package.json"); |  | ||||||
| //require("greenlock-express")
 |  | ||||||
| require("../../") |  | ||||||
| 	.init(function getConfig() { |  | ||||||
| 		// Greenlock Config
 |  | ||||||
| 
 |  | ||||||
| 		return { |  | ||||||
| 			package: { name: "websocket-example", version: pkg.version }, |  | ||||||
| 			maintainerEmail: "jon@example.com", |  | ||||||
| 
 |  | ||||||
| 			// When you're ready to go full cloud scale, you just change this to true:
 |  | ||||||
| 			// Note: in cluster you CANNOT use in-memory state (see below)
 |  | ||||||
| 			cluster: true, |  | ||||||
| 
 |  | ||||||
|       // This will default to the number of workers being equal to
 |  | ||||||
|       // n-1 cpus, with a minimum of 2
 |  | ||||||
|       workers: 4 |  | ||||||
| 		}; |  | ||||||
| 	}) |  | ||||||
| 	.serve(httpsWorker); |  | ||||||
| 
 |  | ||||||
| function httpsWorker(glx) { |  | ||||||
| 	// WRONG
 |  | ||||||
| 	// This won't work like you
 |  | ||||||
| 	// think because EACH worker
 |  | ||||||
| 	// has ITS OWN `count`.
 |  | ||||||
| 	var count = 0; |  | ||||||
| 
 |  | ||||||
| 	var app = function(req, res) { |  | ||||||
| 		res.end("Hello... how many times now? Oh, " + count + " times"); |  | ||||||
| 		count += 1; |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	// Serves on 80 and 443... for each worker
 |  | ||||||
| 	// Get's SSL certificates magically!
 |  | ||||||
| 	glx.serveApp(app); |  | ||||||
| } |  | ||||||
| @ -1,17 +0,0 @@ | |||||||
| "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"); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| // DO NOT DO app.listen() unless we're testing this directly
 |  | ||||||
| if (require.main === module) { |  | ||||||
| 	app.listen(3000); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Instead do export the app:
 |  | ||||||
| module.exports = app; |  | ||||||
| @ -1,27 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| function httpsWorker(glx) { |  | ||||||
| 	var app = require("./my-express-app.js"); |  | ||||||
| 
 |  | ||||||
| 	app.get("/hello", function(req, res) { |  | ||||||
| 		res.end("Hello, Encrypted World!"); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	// Serves on 80 and 443
 |  | ||||||
| 	// Get's SSL certificates magically!
 |  | ||||||
| 	glx.serveApp(app); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var pkg = require("../../package.json"); |  | ||||||
| //require("greenlock-express")
 |  | ||||||
| require("../../") |  | ||||||
| 	.init(function getConfig() { |  | ||||||
| 		// Greenlock Config
 |  | ||||||
| 
 |  | ||||||
| 		return { |  | ||||||
| 			package: { name: "http2-example", version: pkg.version }, |  | ||||||
| 			maintainerEmail: "jon@example.com", |  | ||||||
| 			cluster: false |  | ||||||
| 		}; |  | ||||||
| 	}) |  | ||||||
| 	.serve(httpsWorker); |  | ||||||
| @ -1,44 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| function httpsWorker(glx) { |  | ||||||
| 	// we need the raw https server
 |  | ||||||
| 	var server = glx.httpsServer(); |  | ||||||
| 	var proxy = require("http-proxy").createProxyServer({ xfwd: true }); |  | ||||||
| 
 |  | ||||||
| 	// catches error events during proxying
 |  | ||||||
| 	proxy.on("error", function(err, req, res) { |  | ||||||
| 		console.error(err); |  | ||||||
| 		res.statusCode = 500; |  | ||||||
| 		res.end(); |  | ||||||
| 		return; |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	// We'll proxy websockets too
 |  | ||||||
| 	server.on("upgrade", function(req, socket, head) { |  | ||||||
| 		proxy.ws(req, socket, head, { |  | ||||||
| 			ws: true, |  | ||||||
| 			target: "ws://localhost:3000" |  | ||||||
| 		}); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	// servers a node app that proxies requests to a localhost
 |  | ||||||
| 	glx.serveApp(function(req, res) { |  | ||||||
| 		proxy.web(req, res, { |  | ||||||
| 			target: "http://localhost:3000" |  | ||||||
| 		}); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var pkg = require("../../package.json"); |  | ||||||
| //require("greenlock-express")
 |  | ||||||
| require("../../") |  | ||||||
| 	.init(function getConfig() { |  | ||||||
| 		// Greenlock Config
 |  | ||||||
| 
 |  | ||||||
| 		return { |  | ||||||
| 			package: { name: "http-proxy-example", version: pkg.version }, |  | ||||||
| 			maintainerEmail: "jon@example.com", |  | ||||||
| 			cluster: false |  | ||||||
| 		}; |  | ||||||
| 	}) |  | ||||||
| 	.serve(httpsWorker); |  | ||||||
| @ -1,42 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| var pkg = require("../../package.json"); |  | ||||||
| 
 |  | ||||||
| // The WRONG way:
 |  | ||||||
| //var http = require('http');
 |  | ||||||
| //var httpServer = https.createSecureServer(redirectToHttps);
 |  | ||||||
| //
 |  | ||||||
| // Why is that wrong?
 |  | ||||||
| // Greenlock needs to change some low-level http and https options.
 |  | ||||||
| // Use glx.httpServer(redirectToHttps) instead.
 |  | ||||||
| 
 |  | ||||||
| function httpsWorker(glx) { |  | ||||||
| 	//
 |  | ||||||
| 	// HTTP can only be used for ACME HTTP-01 Challenges
 |  | ||||||
| 	// (and it is not required for DNS-01 challenges)
 |  | ||||||
| 	//
 |  | ||||||
| 
 |  | ||||||
| 	// Get the raw http server:
 |  | ||||||
| 	var httpServer = glx.httpServer(function(req, res) { |  | ||||||
| 		res.statusCode = 301; |  | ||||||
| 		res.setHeader("Location", "https://" + req.headers.host + req.path); |  | ||||||
| 		res.end("Insecure connections are not allowed. Redirecting..."); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	httpServer.listen(80, "0.0.0.0", function() { |  | ||||||
| 		console.info("Listening on ", httpServer.address()); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| //require("greenlock-express")
 |  | ||||||
| require("../../") |  | ||||||
| 	.init(function getConfig() { |  | ||||||
| 		// Greenlock Config
 |  | ||||||
| 
 |  | ||||||
| 		return { |  | ||||||
| 			package: { name: "plain-http-example", version: pkg.version }, |  | ||||||
| 			maintainerEmail: "jon@example.com", |  | ||||||
| 			cluster: false |  | ||||||
| 		}; |  | ||||||
| 	}) |  | ||||||
| 	.serve(httpsWorker); |  | ||||||
| @ -1,48 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| var pkg = require("../../package.json"); |  | ||||||
| 
 |  | ||||||
| // The WRONG way:
 |  | ||||||
| //var http2 = require('http2');
 |  | ||||||
| //var http2Server = https.createSecureServer(tlsOptions, app);
 |  | ||||||
| //
 |  | ||||||
| // Why is that wrong?
 |  | ||||||
| // Greenlock needs to change some low-level http and https options.
 |  | ||||||
| // Use glx.httpsServer(tlsOptions, app) instead.
 |  | ||||||
| 
 |  | ||||||
| function httpsWorker(glx) { |  | ||||||
| 	//
 |  | ||||||
| 	// HTTP2 is the default httpsServer for node v12+
 |  | ||||||
| 	// (HTTPS/1.1 is used for node <= v11)
 |  | ||||||
| 	//
 |  | ||||||
| 
 |  | ||||||
| 	// Get the raw http2 server:
 |  | ||||||
| 	var http2Server = glx.httpsServer(function(req, res) { |  | ||||||
| 		res.end("Hello, Encrypted World!"); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	http2Server.listen(443, "0.0.0.0", function() { |  | ||||||
| 		console.info("Listening on ", http2Server.address()); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	// Note:
 |  | ||||||
| 	// You must ALSO listen on port 80 for ACME HTTP-01 Challenges
 |  | ||||||
| 	// (the ACME and http->https middleware are loaded by glx.httpServer)
 |  | ||||||
| 	var httpServer = glx.httpServer(); |  | ||||||
| 	httpServer.listen(80, "0.0.0.0", function() { |  | ||||||
| 		console.info("Listening on ", httpServer.address()); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| //require("greenlock-express")
 |  | ||||||
| require("../../") |  | ||||||
| 	.init(function getConfig() { |  | ||||||
| 		// Greenlock Config
 |  | ||||||
| 
 |  | ||||||
| 		return { |  | ||||||
| 			package: { name: "http2-example", version: pkg.version }, |  | ||||||
| 			maintainerEmail: "jon@example.com", |  | ||||||
| 			cluster: false |  | ||||||
| 		}; |  | ||||||
| 	}) |  | ||||||
| 	.serve(httpsWorker); |  | ||||||
| @ -1,49 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| var pkg = require("../../package.json"); |  | ||||||
| 
 |  | ||||||
| // The WRONG way:
 |  | ||||||
| //var https = require('https');
 |  | ||||||
| //var httpsServer = https.createServer(tlsOptions, app);
 |  | ||||||
| //
 |  | ||||||
| // Why is that wrong?
 |  | ||||||
| // Greenlock needs to change some low-level http and https options.
 |  | ||||||
| // Use glx.httpsServer(tlsOptions, app) instead.
 |  | ||||||
| 
 |  | ||||||
| function httpsWorker(glx) { |  | ||||||
| 	//
 |  | ||||||
| 	// HTTPS/1.1 is only used for node v11 or lower
 |  | ||||||
| 	// (HTTP2 is used for node v12+)
 |  | ||||||
| 	//
 |  | ||||||
| 	// Why not just require('https')?
 |  | ||||||
| 
 |  | ||||||
| 	// Get the raw https server:
 |  | ||||||
| 	var httpsServer = glx.httpsServer(null, function(req, res) { |  | ||||||
| 		res.end("Hello, Encrypted World!"); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	httpsServer.listen(443, "0.0.0.0", function() { |  | ||||||
| 		console.info("Listening on ", httpsServer.address()); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	// Note:
 |  | ||||||
| 	// You must ALSO listen on port 80 for ACME HTTP-01 Challenges
 |  | ||||||
| 	// (the ACME and http->https middleware are loaded by glx.httpServer)
 |  | ||||||
| 	var httpServer = glx.httpServer(); |  | ||||||
| 	httpServer.listen(80, "0.0.0.0", function() { |  | ||||||
| 		console.info("Listening on ", httpServer.address()); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| //require("greenlock-express")
 |  | ||||||
| require("../../") |  | ||||||
| 	.init(function getConfig() { |  | ||||||
| 		// Greenlock Config
 |  | ||||||
| 
 |  | ||||||
| 		return { |  | ||||||
| 			package: { name: "https1-example", version: pkg.version }, |  | ||||||
| 			maintainerEmail: "jon@example.com", |  | ||||||
| 			cluster: false |  | ||||||
| 		}; |  | ||||||
| 	}) |  | ||||||
| 	.serve(httpsWorker); |  | ||||||
| @ -1,22 +0,0 @@ | |||||||
| # Quick Start for Let's Encrypt with Node.js |  | ||||||
| 
 |  | ||||||
| ```js |  | ||||||
| npm install --save greenlock-express |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Manage via API or the config file: |  | ||||||
| 
 |  | ||||||
| `~/.config/greenlock/manage.json`: (default filesystem config) |  | ||||||
| 
 |  | ||||||
| ```json |  | ||||||
| { |  | ||||||
| 	"subscriberEmail": "letsencrypt-test@therootcompany.com", |  | ||||||
| 	"agreeToTerms": true, |  | ||||||
| 	"sites": { |  | ||||||
| 		"example.com": { |  | ||||||
| 			"subject": "example.com", |  | ||||||
| 			"altnames": ["example.com", "www.example.com"] |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| @ -1,32 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| function httpsWorker(glx) { |  | ||||||
| 	// This can be a node http app (shown),
 |  | ||||||
| 	// an Express app, or Hapi, Koa, Rill, etc
 |  | ||||||
| 	var app = function(req, res) { |  | ||||||
| 		res.end("Hello, Encrypted World!"); |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	// Serves on 80 and 443
 |  | ||||||
| 	// Get's SSL certificates magically!
 |  | ||||||
| 	glx.serveApp(app); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var pkg = require("../../package.json"); |  | ||||||
| //require("greenlock-express")
 |  | ||||||
| require("../../") |  | ||||||
| 	.init(function getConfig() { |  | ||||||
| 		// Greenlock Config
 |  | ||||||
| 
 |  | ||||||
| 		return { |  | ||||||
| 			// Package name+version is used for ACME client user agent
 |  | ||||||
| 			package: { name: "websocket-example", version: pkg.version }, |  | ||||||
| 
 |  | ||||||
| 			// Maintainer email is the contact for critical bug and security notices
 |  | ||||||
| 			maintainerEmail: "jon@example.com", |  | ||||||
| 
 |  | ||||||
| 			// Change to true when you're ready to make your app cloud-scale
 |  | ||||||
| 			cluster: false |  | ||||||
| 		}; |  | ||||||
| 	}) |  | ||||||
| 	.serve(httpsWorker); |  | ||||||
| @ -1,49 +0,0 @@ | |||||||
| // First and foremost:
 |  | ||||||
| // I'm not a fan of `socket.io` because it's huge and complex.
 |  | ||||||
| // I much prefer `ws` because it's very simple and easy.
 |  | ||||||
| // That said, it's popular.......
 |  | ||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| // Note: You DO NOT NEED socket.io
 |  | ||||||
| //       You can just use WebSockets
 |  | ||||||
| //       (see the websocket example)
 |  | ||||||
| 
 |  | ||||||
| function httpsWorker(glx) { |  | ||||||
| 	var socketio = require("socket.io"); |  | ||||||
| 	var io; |  | ||||||
| 
 |  | ||||||
| 	// we need the raw https server
 |  | ||||||
| 	var server = glx.httpsServer(); |  | ||||||
| 
 |  | ||||||
| 	io = socketio(server); |  | ||||||
| 
 |  | ||||||
| 	// Then you do your socket.io stuff
 |  | ||||||
| 	io.on("connection", function(socket) { |  | ||||||
| 		console.log("a user connected"); |  | ||||||
| 		socket.emit("Welcome"); |  | ||||||
| 
 |  | ||||||
| 		socket.on("chat message", function(msg) { |  | ||||||
| 			socket.broadcast.emit("chat message", msg); |  | ||||||
| 		}); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	// servers a node app that proxies requests to a localhost
 |  | ||||||
| 	glx.serveApp(function(req, res) { |  | ||||||
| 		res.setHeader("Content-Type", "text/html; charset=utf-8"); |  | ||||||
| 		res.end("Hello, World!\n\n💚 🔒.js"); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var pkg = require("../../package.json"); |  | ||||||
| //require("greenlock-express")
 |  | ||||||
| require("../../") |  | ||||||
| 	.init(function getConfig() { |  | ||||||
| 		// Greenlock Config
 |  | ||||||
| 
 |  | ||||||
| 		return { |  | ||||||
| 			package: { name: "socket-io-example", version: pkg.version }, |  | ||||||
| 			maintainerEmail: "jon@example.com", |  | ||||||
| 			cluster: false |  | ||||||
| 		}; |  | ||||||
| 	}) |  | ||||||
| 	.serve(httpsWorker); |  | ||||||
| @ -1,3 +0,0 @@ | |||||||
| // SPDY is dead. It was replaced by HTTP2, which is a native node module
 |  | ||||||
| //
 |  | ||||||
| // Greenlock uses HTTP2 as the default https server in node v12+
 |  | ||||||
| @ -1,42 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| function httpsWorker(glx) { |  | ||||||
| 	// we need the raw https server
 |  | ||||||
| 	var server = glx.httpsServer(); |  | ||||||
| 	var WebSocket = require("ws"); |  | ||||||
| 	var ws = new WebSocket.Server({ server: server }); |  | ||||||
| 	ws.on("connection", function(ws, req) { |  | ||||||
| 		// inspect req.headers.authorization (or cookies) for session info
 |  | ||||||
| 		ws.send( |  | ||||||
| 			"[Secure Echo Server] Hello!\nAuth: '" + |  | ||||||
| 				(req.headers.authorization || "none") + |  | ||||||
| 				"'\n" + |  | ||||||
| 				"Cookie: '" + |  | ||||||
| 				(req.headers.cookie || "none") + |  | ||||||
| 				"'\n" |  | ||||||
| 		); |  | ||||||
| 		ws.on("message", function(data) { |  | ||||||
| 			ws.send(data); |  | ||||||
| 		}); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	// servers a node app that proxies requests to a localhost
 |  | ||||||
| 	glx.serveApp(function(req, res) { |  | ||||||
| 		res.setHeader("Content-Type", "text/html; charset=utf-8"); |  | ||||||
| 		res.end("Hello, World!\n\n💚 🔒.js"); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| var pkg = require("../../package.json"); |  | ||||||
| //require("greenlock-express")
 |  | ||||||
| require("../../") |  | ||||||
| 	.init(function getConfig() { |  | ||||||
| 		// Greenlock Config
 |  | ||||||
| 
 |  | ||||||
| 		return { |  | ||||||
| 			package: { name: "websocket-example", version: pkg.version }, |  | ||||||
| 			maintainerEmail: "jon@example.com", |  | ||||||
| 			cluster: false |  | ||||||
| 		}; |  | ||||||
| 	}) |  | ||||||
| 	.serve(httpsWorker); |  | ||||||
| @ -1,44 +1,3 @@ | |||||||
| "use strict"; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| require("./lib/compat"); | module.exports = require('@root/greenlock-express'); | ||||||
| var cluster = require("cluster"); |  | ||||||
| 
 |  | ||||||
| // Greenlock Express
 |  | ||||||
| var GLE = module.exports; |  | ||||||
| 
 |  | ||||||
| // Node's cluster is awesome, because it encourages writing scalable services.
 |  | ||||||
| //
 |  | ||||||
| // The point of this provide an API that is consistent between single-process
 |  | ||||||
| // and multi-process services so that beginners can more easily take advantage
 |  | ||||||
| // of what cluster has to offer.
 |  | ||||||
| //
 |  | ||||||
| // This API provides just enough abstraction to make it easy, but leaves just
 |  | ||||||
| // enough hoopla so that there's not a large gap in understanding what happens
 |  | ||||||
| // under the hood. That's the hope, anyway.
 |  | ||||||
| 
 |  | ||||||
| GLE.init = function(fn) { |  | ||||||
| 	if (cluster.isWorker) { |  | ||||||
| 		// ignore the init function and launch the worker
 |  | ||||||
| 		return require("./worker.js").create(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var opts = fn(); |  | ||||||
| 	if (!opts || "object" !== typeof opts) { |  | ||||||
| 		throw new Error( |  | ||||||
| 			"the `Greenlock.init(fn)` function should return an object `{ maintainerEmail, packageAgent, notify }`" |  | ||||||
| 		); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// just for ironic humor
 |  | ||||||
| 	["cloudnative", "cloudscale", "webscale", "distributed", "blockchain"].forEach(function(k) { |  | ||||||
| 		if (opts[k]) { |  | ||||||
| 			opts.cluster = true; |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	if (opts.cluster) { |  | ||||||
| 		return require("./master.js").create(opts); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return require("./single.js").create(opts); |  | ||||||
| }; |  | ||||||
|  | |||||||
							
								
								
									
										77
									
								
								greenlock.js
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								greenlock.js
									
									
									
									
									
								
							| @ -1,77 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| module.exports.create = function(opts) { |  | ||||||
| 	opts = parsePackage(opts); |  | ||||||
| 	opts.packageAgent = addGreenlockAgent(opts); |  | ||||||
| 
 |  | ||||||
| 	var Greenlock = require("@root/greenlock"); |  | ||||||
| 	var greenlock = Greenlock.create(opts); |  | ||||||
| 
 |  | ||||||
| 	// re-export as top-level function to simplify rpc with workers
 |  | ||||||
| 	greenlock.getAcmeHttp01ChallengeResponse = function(opts) { |  | ||||||
| 		return greenlock.challenges.get(opts); |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	return greenlock; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| function addGreenlockAgent(opts) { |  | ||||||
| 	// Add greenlock as part of Agent, unless this is greenlock
 |  | ||||||
| 	var packageAgent = opts.packageAgent || ""; |  | ||||||
| 	if (!/greenlock(-express|-pro)?/i.test(packageAgent)) { |  | ||||||
| 		var pkg = require("./package.json"); |  | ||||||
| 		packageAgent += " Greenlock_Express/" + pkg.version; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return packageAgent.trim(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ex: "John Doe <john@example.com> (https://john.doe)"
 |  | ||||||
| // ex: "John Doe <john@example.com>"
 |  | ||||||
| // ex: "<john@example.com>"
 |  | ||||||
| // ex: "john@example.com"
 |  | ||||||
| var looseEmailRe = /(^|[\s<])([^'" <>:;`]+@[^'" <>:;`]+\.[^'" <>:;`]+)/; |  | ||||||
| function parsePackage(opts) { |  | ||||||
| 	// 'package' is sometimes a reserved word
 |  | ||||||
| 	var pkg = opts.package || opts.pkg; |  | ||||||
| 	if (!pkg) { |  | ||||||
| 		opts.maintainerEmail = parseMaintainer(opts.maintainerEmail); |  | ||||||
| 		return opts; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (!opts.packageAgent) { |  | ||||||
| 		var err = "missing `package.THING`, which is used for the ACME client user agent string"; |  | ||||||
| 		if (!pkg.name) { |  | ||||||
| 			throw new Error(err.replace("THING", "name")); |  | ||||||
| 		} |  | ||||||
| 		if (!pkg.version) { |  | ||||||
| 			throw new Error(err.replace("THING", "version")); |  | ||||||
| 		} |  | ||||||
| 		opts.packageAgent = pkg.name + "/" + pkg.version; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (!opts.maintainerEmail) { |  | ||||||
| 		try { |  | ||||||
| 			opts.maintainerEmail = pkg.author.email || pkg.author.match(looseEmailRe)[2]; |  | ||||||
| 		} catch (e) {} |  | ||||||
| 	} |  | ||||||
| 	if (!opts.maintainerEmail) { |  | ||||||
| 		throw new Error("missing or malformed `package.author`, which is used as the contact for support notices"); |  | ||||||
| 	} |  | ||||||
| 	opts.package = undefined; |  | ||||||
| 	opts.maintainerEmail = parseMaintainer(opts.maintainerEmail); |  | ||||||
| 
 |  | ||||||
| 	return opts; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function parseMaintainer(maintainerEmail) { |  | ||||||
| 	try { |  | ||||||
| 		maintainerEmail = maintainerEmail.match(looseEmailRe)[2]; |  | ||||||
| 	} catch (e) { |  | ||||||
| 		maintainerEmail = null; |  | ||||||
| 	} |  | ||||||
| 	if (!maintainerEmail) { |  | ||||||
| 		throw new Error("missing or malformed `maintainerEmail`, which is used as the contact for support notices"); |  | ||||||
| 	} |  | ||||||
| 	return maintainerEmail; |  | ||||||
| } |  | ||||||
| @ -1,106 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| var HttpMiddleware = module.exports; |  | ||||||
| var servernameRe = /^[a-z0-9\.\-]+$/i; |  | ||||||
| var challengePrefix = "/.well-known/acme-challenge/"; |  | ||||||
| 
 |  | ||||||
| HttpMiddleware.create = function(gl, defaultApp) { |  | ||||||
| 	if (defaultApp && "function" !== typeof defaultApp) { |  | ||||||
| 		throw new Error("use greenlock.httpMiddleware() or greenlock.httpMiddleware(function (req, res) {})"); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return function(req, res, next) { |  | ||||||
| 		var hostname = HttpMiddleware.sanitizeHostname(req); |  | ||||||
| 
 |  | ||||||
| 		req.on("error", function(err) { |  | ||||||
| 			explainError(gl, err, "http_01_middleware_socket", hostname); |  | ||||||
| 		}); |  | ||||||
| 
 |  | ||||||
| 		if (skipIfNeedBe(req, res, next, defaultApp, hostname)) { |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		var token = req.url.slice(challengePrefix.length); |  | ||||||
| 
 |  | ||||||
| 		gl.getAcmeHttp01ChallengeResponse({ type: "http-01", servername: hostname, token: token }) |  | ||||||
| 			.catch(function(err) { |  | ||||||
| 				respondToError(gl, res, err, "http_01_middleware_challenge_response", hostname); |  | ||||||
| 				return { __done: true }; |  | ||||||
| 			}) |  | ||||||
| 			.then(function(result) { |  | ||||||
| 				if (result && result.__done) { |  | ||||||
| 					return; |  | ||||||
| 				} |  | ||||||
| 				return respondWithGrace(res, result, hostname, token); |  | ||||||
| 			}); |  | ||||||
| 	}; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| function skipIfNeedBe(req, res, next, defaultApp, hostname) { |  | ||||||
| 	if (!hostname || 0 !== req.url.indexOf(challengePrefix)) { |  | ||||||
| 		if ("function" === typeof defaultApp) { |  | ||||||
| 			defaultApp(req, res, next); |  | ||||||
| 		} else if ("function" === typeof next) { |  | ||||||
| 			next(); |  | ||||||
| 		} else { |  | ||||||
| 			res.statusCode = 500; |  | ||||||
| 			res.end("[500] Developer Error: app.use('/', greenlock.httpMiddleware()) or greenlock.httpMiddleware(app)"); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function respondWithGrace(res, result, hostname, token) { |  | ||||||
| 	var keyAuth = result && result.keyAuthorization; |  | ||||||
| 	if (keyAuth && "string" === typeof keyAuth) { |  | ||||||
| 		res.setHeader("Content-Type", "text/plain; charset=utf-8"); |  | ||||||
| 		res.end(keyAuth); |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	res.statusCode = 404; |  | ||||||
| 	res.setHeader("Content-Type", "application/json; charset=utf-8"); |  | ||||||
| 	res.end(JSON.stringify({ error: { message: "domain '" + hostname + "' has no token '" + token + "'." } })); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function explainError(gl, err, ctx, hostname) { |  | ||||||
| 	if (!err.servername) { |  | ||||||
| 		err.servername = hostname; |  | ||||||
| 	} |  | ||||||
| 	if (!err.context) { |  | ||||||
| 		err.context = ctx; |  | ||||||
| 	} |  | ||||||
| 	(gl.notify || gl._notify)("error", err); |  | ||||||
| 	return err; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function respondToError(gl, res, err, ctx, hostname) { |  | ||||||
| 	err = explainError(gl, err, ctx, hostname); |  | ||||||
| 	res.statusCode = 500; |  | ||||||
| 	res.end("Internal Server Error: See logs for details."); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| HttpMiddleware.getHostname = function(req) { |  | ||||||
| 	return req.hostname || req.headers["x-forwarded-host"] || (req.headers.host || ""); |  | ||||||
| }; |  | ||||||
| HttpMiddleware.sanitizeHostname = function(req) { |  | ||||||
| 	// we can trust XFH because spoofing causes no ham in this limited use-case scenario
 |  | ||||||
| 	// (and only telebit would be legitimately setting XFH)
 |  | ||||||
| 	var servername = HttpMiddleware.getHostname(req) |  | ||||||
| 		.toLowerCase() |  | ||||||
| 		.replace(/:.*/, ""); |  | ||||||
| 	try { |  | ||||||
| 		req.hostname = servername; |  | ||||||
| 	} catch (e) { |  | ||||||
| 		// read-only express property
 |  | ||||||
| 	} |  | ||||||
| 	if (req.headers["x-forwarded-host"]) { |  | ||||||
| 		req.headers["x-forwarded-host"] = servername; |  | ||||||
| 	} |  | ||||||
| 	try { |  | ||||||
| 		req.headers.host = servername; |  | ||||||
| 	} catch (e) { |  | ||||||
| 		// TODO is this a possible error?
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return (servernameRe.test(servername) && -1 === servername.indexOf("..") && servername) || ""; |  | ||||||
| }; |  | ||||||
| @ -1,139 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| var SanitizeHost = module.exports; |  | ||||||
| var HttpMiddleware = require("./http-middleware.js"); |  | ||||||
| 
 |  | ||||||
| SanitizeHost.create = function(gl, app) { |  | ||||||
| 	return function(req, res, next) { |  | ||||||
| 		function realNext() { |  | ||||||
| 			if ("function" === typeof app) { |  | ||||||
| 				app(req, res); |  | ||||||
| 			} else if ("function" === typeof next) { |  | ||||||
| 				next(); |  | ||||||
| 			} else { |  | ||||||
| 				res.statusCode = 500; |  | ||||||
| 				res.end("Error: no middleware assigned"); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		var hostname = HttpMiddleware.getHostname(req); |  | ||||||
| 		// Replace the hostname, and get the safe version
 |  | ||||||
| 		var safehost = HttpMiddleware.sanitizeHostname(req); |  | ||||||
| 
 |  | ||||||
| 		// if no hostname, move along
 |  | ||||||
| 		if (!hostname) { |  | ||||||
| 			realNext(); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// if there were unallowed characters, complain
 |  | ||||||
| 		if (safehost.length !== hostname.length) { |  | ||||||
| 			res.statusCode = 400; |  | ||||||
| 			res.end("Malformed HTTP Header: 'Host: " + hostname + "'"); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Note: This sanitize function is also called on plain sockets, which don't need Domain Fronting checks
 |  | ||||||
| 		if (req.socket.encrypted) { |  | ||||||
| 			if (req.socket && "string" === typeof req.socket.servername) { |  | ||||||
| 				// Workaround for https://github.com/nodejs/node/issues/22389
 |  | ||||||
| 				if (!SanitizeHost._checkServername(safehost, req.socket)) { |  | ||||||
| 					res.statusCode = 400; |  | ||||||
| 					res.setHeader("Content-Type", "text/html; charset=utf-8"); |  | ||||||
| 					res.end( |  | ||||||
| 						"<h1>Domain Fronting Error</h1>" + |  | ||||||
| 							"<p>This connection was secured using TLS/SSL for '" + |  | ||||||
| 							(req.socket.servername || "").toLowerCase() + |  | ||||||
| 							"'</p>" + |  | ||||||
| 							"<p>The HTTP request specified 'Host: " + |  | ||||||
| 							safehost + |  | ||||||
| 							"', which is (obviously) different.</p>" + |  | ||||||
| 							"<p>Because this looks like a domain fronting attack, the connection has been terminated.</p>" |  | ||||||
| 					); |  | ||||||
| 					return; |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			/* |  | ||||||
|       else if (safehost && !gl._skip_fronting_check) { |  | ||||||
| 
 |  | ||||||
| 				// We used to print a log message here, but it turns out that it's
 |  | ||||||
| 				// really common for IoT devices to not use SNI (as well as many bots
 |  | ||||||
| 				// and such).
 |  | ||||||
| 				// It was common for the log message to pop up as the first request
 |  | ||||||
| 				// to the server, and that was confusing. So instead now we do nothing.
 |  | ||||||
| 
 |  | ||||||
| 				//console.warn("no string for req.socket.servername," + " skipping fronting check for '" + safehost + "'");
 |  | ||||||
| 				//gl._skip_fronting_check = true;
 |  | ||||||
| 			} |  | ||||||
|       */ |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// carry on
 |  | ||||||
| 		realNext(); |  | ||||||
| 	}; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| var warnDomainFronting = true; |  | ||||||
| var warnUnexpectedError = true; |  | ||||||
| SanitizeHost._checkServername = function(safeHost, tlsSocket) { |  | ||||||
| 	var servername = (tlsSocket.servername || "").toLowerCase(); |  | ||||||
| 
 |  | ||||||
| 	// acceptable: older IoT devices may lack SNI support
 |  | ||||||
| 	if (!servername) { |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 	// acceptable: odd... but acceptable
 |  | ||||||
| 	if (!safeHost) { |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 	if (safeHost === servername) { |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if ("function" !== typeof tlsSocket.getCertificate) { |  | ||||||
| 		// domain fronting attacks allowed
 |  | ||||||
| 		if (warnDomainFronting) { |  | ||||||
| 			// https://github.com/nodejs/node/issues/24095
 |  | ||||||
| 			console.warn( |  | ||||||
| 				"Warning: node " + |  | ||||||
| 					process.version + |  | ||||||
| 					" is vulnerable to domain fronting attacks. Please use node v11.2.0 or greater." |  | ||||||
| 			); |  | ||||||
| 			warnDomainFronting = false; |  | ||||||
| 		} |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// connection established with servername and session is re-used for allowed name
 |  | ||||||
| 	// See https://github.com/nodejs/node/issues/24095
 |  | ||||||
| 	var cert = tlsSocket.getCertificate(); |  | ||||||
| 	try { |  | ||||||
| 		// TODO optimize / cache?
 |  | ||||||
| 		// *should* always have a string, right?
 |  | ||||||
| 		// *should* always be lowercase already, right?
 |  | ||||||
| 		//console.log(safeHost, cert.subject.CN, cert.subjectaltname);
 |  | ||||||
| 		var isSubject = (cert.subject.CN || "").toLowerCase() === safeHost; |  | ||||||
| 		if (isSubject) { |  | ||||||
| 			return true; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		var dnsnames = (cert.subjectaltname || "").split(/,\s+/); |  | ||||||
| 		var inSanList = dnsnames.some(function(name) { |  | ||||||
| 			// always prefixed with "DNS:"
 |  | ||||||
| 			return safeHost === name.slice(4).toLowerCase(); |  | ||||||
| 		}); |  | ||||||
| 
 |  | ||||||
| 		if (inSanList) { |  | ||||||
| 			return true; |  | ||||||
| 		} |  | ||||||
| 	} catch (e) { |  | ||||||
| 		// not sure what else to do in this situation...
 |  | ||||||
| 		if (warnUnexpectedError) { |  | ||||||
| 			console.warn("Warning: encoutered error while performing domain fronting check: " + e.message); |  | ||||||
| 			warnUnexpectedError = false; |  | ||||||
| 		} |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return false; |  | ||||||
| }; |  | ||||||
							
								
								
									
										14
									
								
								install.sh
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								install.sh
									
									
									
									
									
								
							| @ -1,14 +0,0 @@ | |||||||
| # This is just an example (but it works) |  | ||||||
| export NODE_PATH=$NPM_CONFIG_PREFIX/lib/node_modules |  | ||||||
| export NPM_CONFIG_PREFIX=/opt/node |  | ||||||
| curl -fsSL https://bit.ly/node-installer | bash |  | ||||||
| 
 |  | ||||||
| /opt/node/bin/node /opt/node/bin/npm config set scripts-prepend-node-path true |  | ||||||
| /opt/node/bin/node /opt/node/bin/npm ci |  | ||||||
| sudo setcap 'cap_net_bind_service=+ep' /opt/node/bin/node |  | ||||||
| /opt/node/bin/node /opt/node/bin/npm start |  | ||||||
| 
 |  | ||||||
| sudo rsync -av dist/etc/systemd/system/greenlock-express.service /etc/systemd/system/ |  | ||||||
| sudo systemctl daemon-reload |  | ||||||
| 
 |  | ||||||
| sudo systemctl restart greenlock-express |  | ||||||
| @ -1,37 +0,0 @@ | |||||||
| "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; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| if ("undefined" === typeof Promise) { |  | ||||||
| 	global.Promise = requireBluebird(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| if ("function" !== typeof require("util").promisify) { |  | ||||||
| 	require("util").promisify = requireBluebird().promisify; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| if (!console.debug) { |  | ||||||
| 	console.debug = console.log; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| exports.fsAsync = fsAsync; |  | ||||||
							
								
								
									
										36
									
								
								main.js
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								main.js
									
									
									
									
									
								
							| @ -1,36 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| // this is the stuff that should run in the main foreground process,
 |  | ||||||
| // whether it's single or master
 |  | ||||||
| 
 |  | ||||||
| var major = process.versions.node.split(".")[0]; |  | ||||||
| var minor = process.versions.node.split(".")[1]; |  | ||||||
| var _hasSetSecureContext = false; |  | ||||||
| var shouldUpgrade = false; |  | ||||||
| 
 |  | ||||||
| // TODO can we trust earlier versions as well?
 |  | ||||||
| if (major >= 12) { |  | ||||||
| 	_hasSetSecureContext = !!require("http2").createSecureServer({}, function() {}).setSecureContext; |  | ||||||
| } else { |  | ||||||
| 	_hasSetSecureContext = !!require("https").createServer({}, function() {}).setSecureContext; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // TODO document in issues
 |  | ||||||
| if (!_hasSetSecureContext) { |  | ||||||
| 	// TODO this isn't necessary if greenlock options are set with options.cert
 |  | ||||||
| 	console.warn("Warning: node " + process.version + " is missing tlsSocket.setSecureContext()."); |  | ||||||
| 	console.warn("         The default certificate may not be set."); |  | ||||||
| 	shouldUpgrade = true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| if (major < 11 || (11 === major && minor < 2)) { |  | ||||||
| 	// https://github.com/nodejs/node/issues/24095
 |  | ||||||
| 	console.warn("Warning: node " + process.version + " is missing tlsSocket.getCertificate()."); |  | ||||||
| 	console.warn("         This is necessary to guard against domain fronting attacks."); |  | ||||||
| 	shouldUpgrade = true; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| if (shouldUpgrade) { |  | ||||||
| 	console.warn("Warning: Please upgrade to node v11.2.0 or greater."); |  | ||||||
| 	console.warn(); |  | ||||||
| } |  | ||||||
							
								
								
									
										160
									
								
								master.js
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								master.js
									
									
									
									
									
								
							| @ -1,160 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| require("./main.js"); |  | ||||||
| 
 |  | ||||||
| var Master = module.exports; |  | ||||||
| 
 |  | ||||||
| var cluster = require("cluster"); |  | ||||||
| var os = require("os"); |  | ||||||
| var msgPrefix = "greenlock:"; |  | ||||||
| 
 |  | ||||||
| Master.create = function(opts) { |  | ||||||
| 	var resolveCb; |  | ||||||
| 	var _readyCb; |  | ||||||
| 	var _kicked = false; |  | ||||||
| 
 |  | ||||||
| 	var greenlock = require("./greenlock.js").create(opts); |  | ||||||
| 
 |  | ||||||
| 	var ready = new Promise(function(resolve) { |  | ||||||
| 		resolveCb = resolve; |  | ||||||
| 	}).then(function(fn) { |  | ||||||
| 		_readyCb = fn; |  | ||||||
| 		return fn; |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	function kickoff() { |  | ||||||
| 		if (_kicked) { |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 		_kicked = true; |  | ||||||
| 
 |  | ||||||
| 		Master._spawnWorkers(opts, greenlock); |  | ||||||
| 
 |  | ||||||
| 		ready.then(function(fn) { |  | ||||||
| 			// not sure what this API should be yet
 |  | ||||||
| 			fn(); |  | ||||||
| 		}); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var master = { |  | ||||||
| 		serve: function() { |  | ||||||
| 			kickoff(); |  | ||||||
| 			return master; |  | ||||||
| 		}, |  | ||||||
| 		master: function(fn) { |  | ||||||
| 			if (_readyCb) { |  | ||||||
| 				throw new Error("can't call master twice"); |  | ||||||
| 			} |  | ||||||
| 			kickoff(); |  | ||||||
| 			resolveCb(fn); |  | ||||||
| 			return master; |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| 	return master; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| function range(n) { |  | ||||||
| 	n = parseInt(n, 10); |  | ||||||
| 	if (!n) { |  | ||||||
| 		return []; |  | ||||||
| 	} |  | ||||||
| 	return new Array(n).join(",").split(","); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| Master._spawnWorkers = function(opts, greenlock) { |  | ||||||
| 	var numCpus = parseInt(process.env.NUMBER_OF_PROCESSORS, 10) || os.cpus().length; |  | ||||||
| 
 |  | ||||||
| 	// process rpc messages
 |  | ||||||
| 	// start when dead
 |  | ||||||
| 	var numWorkers = parseInt(opts.workers || opts.numWorkers, 10); |  | ||||||
| 	if (!numWorkers) { |  | ||||||
| 		if (numCpus <= 2) { |  | ||||||
| 			numWorkers = 2; |  | ||||||
| 		} else { |  | ||||||
| 			numWorkers = numCpus - 1; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	cluster.once("exit", function() { |  | ||||||
| 		setTimeout(function() { |  | ||||||
| 			process.exit(3); |  | ||||||
| 		}, 100); |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	var workers = range(numWorkers); |  | ||||||
| 	function next() { |  | ||||||
| 		if (!workers.length) { |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 		workers.pop(); |  | ||||||
| 
 |  | ||||||
| 		// for a nice aesthetic
 |  | ||||||
| 		setTimeout(function() { |  | ||||||
| 			Master._spawnWorker(opts, greenlock); |  | ||||||
| 			next(); |  | ||||||
| 		}, 250); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	next(); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| Master._spawnWorker = function(opts, greenlock) { |  | ||||||
| 	var w = cluster.fork(); |  | ||||||
| 	// automatically added to master's `cluster.workers`
 |  | ||||||
| 	w.once("exit", function(code, signal) { |  | ||||||
| 		// TODO handle failures
 |  | ||||||
| 		// Should test if the first starts successfully
 |  | ||||||
| 		// Should exit if failures happen too quickly
 |  | ||||||
| 
 |  | ||||||
| 		// For now just kill all when any die
 |  | ||||||
| 		if (signal) { |  | ||||||
| 			console.error("worker was killed by signal:", signal); |  | ||||||
| 		} else if (code !== 0) { |  | ||||||
| 			console.error("worker exited with error code:", code); |  | ||||||
| 		} else { |  | ||||||
| 			console.error("worker unexpectedly quit without exit code or signal"); |  | ||||||
| 		} |  | ||||||
| 		process.exit(2); |  | ||||||
| 
 |  | ||||||
| 		//addWorker();
 |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	function handleMessage(msg) { |  | ||||||
| 		if (0 !== (msg._id || "").indexOf(msgPrefix)) { |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 		if ("string" !== typeof msg._funcname) { |  | ||||||
| 			// TODO developer error
 |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		function rpc() { |  | ||||||
| 			return greenlock[msg._funcname](msg._input) |  | ||||||
| 				.then(function(result) { |  | ||||||
| 					w.send({ |  | ||||||
| 						_id: msg._id, |  | ||||||
| 						_result: result |  | ||||||
| 					}); |  | ||||||
| 				}) |  | ||||||
| 				.catch(function(e) { |  | ||||||
| 					var error = new Error(e.message); |  | ||||||
| 					Object.getOwnPropertyNames(e).forEach(function(k) { |  | ||||||
| 						error[k] = e[k]; |  | ||||||
| 					}); |  | ||||||
| 					w.send({ |  | ||||||
| 						_id: msg._id, |  | ||||||
| 						_error: error |  | ||||||
| 					}); |  | ||||||
| 				}); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		try { |  | ||||||
| 			rpc(); |  | ||||||
| 		} catch (e) { |  | ||||||
| 			console.error("Unexpected and uncaught greenlock." + msg._funcname + " error:"); |  | ||||||
| 			console.error(e); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	w.on("message", handleMessage); |  | ||||||
| }; |  | ||||||
							
								
								
									
										14
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
| 	"name": "@root/greenlock-express", | 	"name": "@root/greenlock-express", | ||||||
| 	"version": "3.0.7", | 	"version": "3.0.10", | ||||||
| 	"lockfileVersion": 1, | 	"lockfileVersion": 1, | ||||||
| 	"requires": true, | 	"requires": true, | ||||||
| 	"dependencies": { | 	"dependencies": { | ||||||
| @ -40,9 +40,9 @@ | |||||||
| 			"integrity": "sha512-OaEub02ufoU038gy6bsNHQOjIn8nUjGiLcaRmJ40IUykneJkIW5fxDqKxQx48cszuNflYldsJLPPXCrGfHs8yQ==" | 			"integrity": "sha512-OaEub02ufoU038gy6bsNHQOjIn8nUjGiLcaRmJ40IUykneJkIW5fxDqKxQx48cszuNflYldsJLPPXCrGfHs8yQ==" | ||||||
| 		}, | 		}, | ||||||
| 		"@root/greenlock": { | 		"@root/greenlock": { | ||||||
| 			"version": "3.0.17", | 			"version": "3.0.24", | ||||||
| 			"resolved": "https://registry.npmjs.org/@root/greenlock/-/greenlock-3.0.17.tgz", | 			"resolved": "https://registry.npmjs.org/@root/greenlock/-/greenlock-3.0.24.tgz", | ||||||
| 			"integrity": "sha512-1XKhcLFEx1WFdn1Bc2rkAE/SL1ZUJYYMZdbnehTrfhCr5Y+9U1gdkNZnR/jInhoUvcicF/PXuZkGVucU50RNUg==", | 			"integrity": "sha512-uJgHIdWEzZ1QeFN+Ydc2vKs91RDlZQTUF2R2WcklayWivXvBnr7QiyLDVtI5VZuJN6y5RQeWXmDQub/On+8wbg==", | ||||||
| 			"requires": { | 			"requires": { | ||||||
| 				"@root/acme": "^3.0.8", | 				"@root/acme": "^3.0.8", | ||||||
| 				"@root/csr": "^0.8.1", | 				"@root/csr": "^0.8.1", | ||||||
| @ -77,9 +77,9 @@ | |||||||
| 			"integrity": "sha512-rEUDiUsHtild8GfIjFE9wXtcVxeS+ehCJQBwbQQ3IVfORKHK93CFnRtkr69R75lZFjcmKYVc+AXDB+AeRFOULA==" | 			"integrity": "sha512-rEUDiUsHtild8GfIjFE9wXtcVxeS+ehCJQBwbQQ3IVfORKHK93CFnRtkr69R75lZFjcmKYVc+AXDB+AeRFOULA==" | ||||||
| 		}, | 		}, | ||||||
| 		"@root/request": { | 		"@root/request": { | ||||||
| 			"version": "1.4.1", | 			"version": "1.4.2", | ||||||
| 			"resolved": "https://registry.npmjs.org/@root/request/-/request-1.4.1.tgz", | 			"resolved": "https://registry.npmjs.org/@root/request/-/request-1.4.2.tgz", | ||||||
| 			"integrity": "sha512-2zSP1v9VhJ3gvm4oph0C4BYCoM3Sj84/Wx4iKdt0IbqbJzfON04EodBq5dsV65UxO/aHZciUBwY2GCZcHqaTYg==" | 			"integrity": "sha512-J8FM4+SJuc7WRC+Jz17m+VT2lgI7HtatHhxN1F2ck5aIKUAxJEaR4u/gLBsgT60mVHevKCjKN0O8115UtJjwLw==" | ||||||
| 		}, | 		}, | ||||||
| 		"@root/x509": { | 		"@root/x509": { | ||||||
| 			"version": "0.7.2", | 			"version": "0.7.2", | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								publish.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								publish.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | 
 | ||||||
|  | set -e | ||||||
|  | set -u | ||||||
|  | 
 | ||||||
|  | git fetch --all | ||||||
|  | git checkout master | ||||||
|  | git pull origin master | ||||||
|  | 
 | ||||||
|  | git checkout npm | ||||||
|  | git checkout master -- package.json | ||||||
|  | git checkout master -- README.md | ||||||
|  | sed -i '' -e 's|"name": ".root.greenlock"|"name": "greenlock"|' package.json | ||||||
|  | npm install --save @root/greenlock-express@latest | ||||||
|  | git add package* README.md || true | ||||||
|  | git commit -m "bump" || true | ||||||
|  | npm publish ./ | ||||||
|  | git reset --hard | ||||||
| @ -1,77 +0,0 @@ | |||||||
| #!/usr/bin/env node |  | ||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| // BG WH \u001b[47m |  | ||||||
| // BOLD  \u001b[1m |  | ||||||
| // RED   \u001b[31m |  | ||||||
| // GREEN \u001b[32m |  | ||||||
| // RESET \u001b[0m |  | ||||||
| 
 |  | ||||||
| var grabbers = [ |  | ||||||
| 	[ |  | ||||||
| 		"", |  | ||||||
| 		"================================================================================", |  | ||||||
| 		"", |  | ||||||
| 		" 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥", |  | ||||||
| 		"🔥                            🔥", |  | ||||||
| 		"🔥  Do you rely on Greenlock? 🔥", |  | ||||||
| 		"🔥                            🔥", |  | ||||||
| 		" 🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥" |  | ||||||
| 	], |  | ||||||
| 
 |  | ||||||
| 	[ |  | ||||||
| 		"", |  | ||||||
| 		"================================================================================", |  | ||||||
| 		"", |  | ||||||
| 		" 🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒", |  | ||||||
| 		"🍒                              🍒", |  | ||||||
| 		"🍒  Do you rely on Greenlock?   🍒", |  | ||||||
| 		"🍒                              🍒", |  | ||||||
| 		" 🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒" |  | ||||||
| 	], |  | ||||||
| 
 |  | ||||||
| 	[ |  | ||||||
| 		"", |  | ||||||
| 		"================================================================================", |  | ||||||
| 		"", |  | ||||||
| 		" 👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇", |  | ||||||
| 		"👉                             👈", |  | ||||||
| 		"👉  Do you rely on Greenlock?  👈", |  | ||||||
| 		"👉                             👈", |  | ||||||
| 		" 👆👆👆👆👆👆👆👆👆👆👆👆👆👆👆 " |  | ||||||
| 	], |  | ||||||
| 
 |  | ||||||
| 	[ |  | ||||||
| 		"", |  | ||||||
| 		"================================================================================", |  | ||||||
| 		"", |  | ||||||
| 		" 👀 👀 👀 👀 👀 👀 👀 👀 👀 👀 👀 ", |  | ||||||
| 		"👀                              👀", |  | ||||||
| 		"👀  Do you rely on Greenlock?   👀", |  | ||||||
| 		"👀                              👀", |  | ||||||
| 		" 👀 👀 👀 👀 👀 👀 👀 👀 👀 👀 👀 ", |  | ||||||
| 	] |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| setTimeout(function() { |  | ||||||
| 	grabbers[Math.floor(Math.random() * grabbers.length)].concat([ |  | ||||||
| 		"", |  | ||||||
| 		"Hey! Let's Encrypt will \u001b[31mSTOP WORKING\u001b[0m with Greenlock v2 at the end of October,", |  | ||||||
| 		"and \u001b[31mWITHOUT YOUR HELP\u001b[0m we won't get the next release out in time.", |  | ||||||
| 		"", |  | ||||||
| 		"If Greenlock has saved you time and money, and taken stress out of your life,", |  | ||||||
| 		"or you just love it, please reach out to return the favor today:", |  | ||||||
| 		"", |  | ||||||
| 		"\u001b[31mSAVE GREENLOCK:\u001b[0m", |  | ||||||
| 		"https://indiegogo.com/at/greenlock", |  | ||||||
| 		"", |  | ||||||
| 		"================================================================================", |  | ||||||
| 		"" |  | ||||||
| 	]).forEach(function(line) { |  | ||||||
| 		console.info(line); |  | ||||||
| 	}); |  | ||||||
| }, 300); |  | ||||||
| 
 |  | ||||||
| setTimeout(function() { |  | ||||||
| 	// give time to read |  | ||||||
| }, 1500); |  | ||||||
							
								
								
									
										157
									
								
								servers.js
									
									
									
									
									
								
							
							
						
						
									
										157
									
								
								servers.js
									
									
									
									
									
								
							| @ -1,157 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| var Servers = module.exports; |  | ||||||
| 
 |  | ||||||
| var http = require("http"); |  | ||||||
| var HttpMiddleware = require("./http-middleware.js"); |  | ||||||
| var HttpsMiddleware = require("./https-middleware.js"); |  | ||||||
| var sni = require("./sni.js"); |  | ||||||
| var cluster = require("cluster"); |  | ||||||
| 
 |  | ||||||
| Servers.create = function(greenlock) { |  | ||||||
| 	var servers = {}; |  | ||||||
| 	var _httpServer; |  | ||||||
| 	var _httpsServer; |  | ||||||
| 
 |  | ||||||
| 	function startError(e) { |  | ||||||
| 		explainError(e); |  | ||||||
| 		process.exit(1); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	servers.httpServer = function(defaultApp) { |  | ||||||
| 		if (_httpServer) { |  | ||||||
| 			return _httpServer; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		_httpServer = http.createServer(HttpMiddleware.create(greenlock, defaultApp)); |  | ||||||
| 		_httpServer.once("error", startError); |  | ||||||
| 
 |  | ||||||
| 		return _httpServer; |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	var _middlewareApp; |  | ||||||
| 
 |  | ||||||
| 	servers.httpsServer = function(secureOpts, defaultApp) { |  | ||||||
| 		if (defaultApp) { |  | ||||||
| 			// TODO guard against being set twice?
 |  | ||||||
| 			_middlewareApp = defaultApp; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (_httpsServer) { |  | ||||||
| 			if (secureOpts && Object.keys(secureOpts).length) { |  | ||||||
| 				throw new Error("Call glx.httpsServer(tlsOptions) before calling glx.serveApp(app)"); |  | ||||||
| 			} |  | ||||||
| 			return _httpsServer; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (!secureOpts) { |  | ||||||
| 			secureOpts = {}; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		_httpsServer = createSecureServer( |  | ||||||
| 			wrapDefaultSniCallback(greenlock, secureOpts), |  | ||||||
| 			HttpsMiddleware.create(greenlock, function(req, res) { |  | ||||||
| 				if (!_middlewareApp) { |  | ||||||
| 					throw new Error("Set app with `glx.serveApp(app)` or `glx.httpsServer(tlsOptions, app)`"); |  | ||||||
| 				} |  | ||||||
| 				_middlewareApp(req, res); |  | ||||||
| 			}) |  | ||||||
| 		); |  | ||||||
| 		_httpsServer.once("error", startError); |  | ||||||
| 
 |  | ||||||
| 		return _httpsServer; |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	servers.id = function() { |  | ||||||
| 		return (cluster.isWorker && cluster.worker.id) || "0"; |  | ||||||
| 	}; |  | ||||||
| 	servers.serveApp = function(app) { |  | ||||||
| 		return new Promise(function(resolve, reject) { |  | ||||||
| 			if ("function" !== typeof app) { |  | ||||||
| 				reject(new Error("glx.serveApp(app) expects a node/express app in the format `function (req, res) { ... }`")); |  | ||||||
| 				return; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			var id = cluster.isWorker && cluster.worker.id; |  | ||||||
| 			var idstr = (id && "#" + id + " ") || ""; |  | ||||||
| 			var plainServer = servers.httpServer(require("redirect-https")()); |  | ||||||
| 			var plainAddr = "0.0.0.0"; |  | ||||||
| 			var plainPort = 80; |  | ||||||
| 			plainServer.listen(plainPort, plainAddr, function() { |  | ||||||
| 				console.info( |  | ||||||
| 					idstr + "Listening on", |  | ||||||
| 					plainAddr + ":" + plainPort, |  | ||||||
| 					"for ACME challenges, and redirecting to HTTPS" |  | ||||||
| 				); |  | ||||||
| 
 |  | ||||||
| 				// TODO fetch greenlock.servername
 |  | ||||||
| 				_middlewareApp = app || _middlewareApp; |  | ||||||
| 				var secureServer = servers.httpsServer(null, app); |  | ||||||
| 				var secureAddr = "0.0.0.0"; |  | ||||||
| 				var securePort = 443; |  | ||||||
| 				secureServer.listen(securePort, secureAddr, function() { |  | ||||||
| 					console.info(idstr + "Listening on", secureAddr + ":" + securePort, "for secure traffic"); |  | ||||||
| 
 |  | ||||||
| 					plainServer.removeListener("error", startError); |  | ||||||
| 					secureServer.removeListener("error", startError); |  | ||||||
| 					resolve(); |  | ||||||
| 				}); |  | ||||||
| 			}); |  | ||||||
| 		}); |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	return servers; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| function explainError(e) { |  | ||||||
| 	console.error(); |  | ||||||
| 	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)"'); |  | ||||||
| 	} else 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."); |  | ||||||
| 	} else { |  | ||||||
| 		console.error(e.code + ": '" + e.address + ":" + e.port + "'"); |  | ||||||
| 	} |  | ||||||
| 	console.error(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function wrapDefaultSniCallback(greenlock, secureOpts) { |  | ||||||
| 	// I'm not sure yet if the original SNICallback
 |  | ||||||
| 	// should be called before or after, so I'm just
 |  | ||||||
| 	// going to delay making that choice until I have the use case
 |  | ||||||
| 	/* |  | ||||||
| 		if (!secureOpts.SNICallback) { |  | ||||||
| 			secureOpts.SNICallback = function(servername, cb) { |  | ||||||
| 				cb(null, null); |  | ||||||
| 			}; |  | ||||||
| 		} |  | ||||||
|   */ |  | ||||||
| 	if (secureOpts.SNICallback) { |  | ||||||
| 		console.warn(); |  | ||||||
| 		console.warn("[warning] Ignoring the given tlsOptions.SNICallback function."); |  | ||||||
| 		console.warn(); |  | ||||||
| 		console.warn("          We're very open to implementing support for this,"); |  | ||||||
| 		console.warn("          we just don't understand the use case yet."); |  | ||||||
| 		console.warn("          Please open an issue to discuss. We'd love to help."); |  | ||||||
| 		console.warn(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// TODO greenlock.servername for workers
 |  | ||||||
| 	secureOpts.SNICallback = sni.create(greenlock, secureOpts); |  | ||||||
| 	return secureOpts; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function createSecureServer(secureOpts, fn) { |  | ||||||
| 	var major = process.versions.node.split(".")[0]; |  | ||||||
| 
 |  | ||||||
| 	// TODO can we trust earlier versions as well?
 |  | ||||||
| 	if (major >= 12) { |  | ||||||
| 		secureOpts.allowHTTP1 = true; |  | ||||||
| 		return require("http2").createSecureServer(secureOpts, fn); |  | ||||||
| 	} else { |  | ||||||
| 		return require("https").createServer(secureOpts, fn); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										25
									
								
								single.js
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								single.js
									
									
									
									
									
								
							| @ -1,25 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| require("./main.js"); |  | ||||||
| 
 |  | ||||||
| var Single = module.exports; |  | ||||||
| var Servers = require("./servers.js"); |  | ||||||
| 
 |  | ||||||
| Single.create = function(opts) { |  | ||||||
| 	var greenlock = require("./greenlock.js").create(opts); |  | ||||||
| 
 |  | ||||||
| 	var servers = Servers.create(greenlock); |  | ||||||
| 
 |  | ||||||
| 	var single = { |  | ||||||
| 		serve: function(fn) { |  | ||||||
| 			fn(servers); |  | ||||||
| 			return single; |  | ||||||
| 		}, |  | ||||||
| 		master: function(/*fn*/) { |  | ||||||
| 			// ignore
 |  | ||||||
| 			//fn(master);
 |  | ||||||
| 			return single; |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| 	return single; |  | ||||||
| }; |  | ||||||
							
								
								
									
										194
									
								
								sni.js
									
									
									
									
									
								
							
							
						
						
									
										194
									
								
								sni.js
									
									
									
									
									
								
							| @ -1,194 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| var sni = module.exports; |  | ||||||
| var tls = require("tls"); |  | ||||||
| var servernameRe = /^[a-z0-9\.\-]+$/i; |  | ||||||
| 
 |  | ||||||
| // a nice, round, irrational number - about every 6¼ hours
 |  | ||||||
| var refreshOffset = Math.round(Math.PI * 2 * (60 * 60 * 1000)); |  | ||||||
| // and another, about 15 minutes
 |  | ||||||
| var refreshStagger = Math.round(Math.PI * 5 * (60 * 1000)); |  | ||||||
| // and another, about 30 seconds
 |  | ||||||
| var smallStagger = Math.round(Math.PI * (30 * 1000)); |  | ||||||
| 
 |  | ||||||
| //secureOpts.SNICallback = sni.create(greenlock, secureOpts);
 |  | ||||||
| sni.create = function(greenlock, secureOpts) { |  | ||||||
| 	var _cache = {}; |  | ||||||
| 	var defaultServername = greenlock.servername || ""; |  | ||||||
| 
 |  | ||||||
| 	if (secureOpts.cert) { |  | ||||||
| 		// Note: it's fine if greenlock.servername is undefined,
 |  | ||||||
| 		// but if the caller wants this to auto-renew, they should define it
 |  | ||||||
| 		_cache[defaultServername] = { |  | ||||||
| 			refreshAt: 0, |  | ||||||
| 			secureContext: tls.createSecureContext(secureOpts) |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return getSecureContext; |  | ||||||
| 
 |  | ||||||
| 	function notify(ev, args) { |  | ||||||
| 		try { |  | ||||||
| 			// TODO _notify() or notify()?
 |  | ||||||
| 			(greenlock.notify || greenlock._notify)(ev, args); |  | ||||||
| 		} catch (e) { |  | ||||||
| 			console.error(e); |  | ||||||
| 			console.error(ev, args); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	function getSecureContext(servername, cb) { |  | ||||||
| 		//console.log("debug sni", servername);
 |  | ||||||
| 		if ("string" !== typeof servername) { |  | ||||||
| 			// this will never happen... right? but stranger things have...
 |  | ||||||
| 			console.error("[sanity fail] non-string servername:", servername); |  | ||||||
| 			cb(new Error("invalid servername"), null); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		var secureContext = getCachedContext(servername); |  | ||||||
| 		if (secureContext) { |  | ||||||
| 			//console.log("debug sni got cached context", servername, getCachedMeta(servername));
 |  | ||||||
| 			cb(null, secureContext); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		getFreshContext(servername) |  | ||||||
| 			.then(function(secureContext) { |  | ||||||
| 				if (secureContext) { |  | ||||||
| 					//console.log("debug sni got fresh context", servername, getCachedMeta(servername));
 |  | ||||||
| 					cb(null, secureContext); |  | ||||||
| 					return; |  | ||||||
| 				} |  | ||||||
| 				// Note: this does not replace tlsSocket.setSecureContext()
 |  | ||||||
| 				// as it only works when SNI has been sent
 |  | ||||||
| 				//console.log("debug sni got default context", servername, getCachedMeta(servername));
 |  | ||||||
| 				cb(null, getDefaultContext()); |  | ||||||
| 			}) |  | ||||||
| 			.catch(function(err) { |  | ||||||
| 				if (!err.context) { |  | ||||||
| 					err.context = "sni_callback"; |  | ||||||
| 				} |  | ||||||
| 				notify("error", err); |  | ||||||
| 				//console.log("debug sni error", servername, err);
 |  | ||||||
| 				cb(err); |  | ||||||
| 			}); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	function getCachedMeta(servername) { |  | ||||||
| 		var meta = _cache[servername]; |  | ||||||
| 		if (!meta) { |  | ||||||
| 			if (!_cache[wildname(servername)]) { |  | ||||||
| 				return null; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return meta; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	function getCachedContext(servername) { |  | ||||||
| 		var meta = getCachedMeta(servername); |  | ||||||
| 		if (!meta) { |  | ||||||
| 			return null; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// always renew in background
 |  | ||||||
| 		if (!meta.refreshAt || Date.now() >= meta.refreshAt) { |  | ||||||
| 			getFreshContext(servername).catch(function(e) { |  | ||||||
| 				if (!e.context) { |  | ||||||
| 					e.context = "sni_background_refresh"; |  | ||||||
| 				} |  | ||||||
| 				notify("error", e); |  | ||||||
| 			}); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// under normal circumstances this would never be expired
 |  | ||||||
| 		// and, if it is expired, something is so wrong it's probably
 |  | ||||||
| 		// not worth wating for the renewal - it has probably failed
 |  | ||||||
| 		return meta.secureContext; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	function getFreshContext(servername) { |  | ||||||
| 		var meta = getCachedMeta(servername); |  | ||||||
| 		if (!meta && !validServername(servername)) { |  | ||||||
| 			return Promise.resolve(null); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (meta) { |  | ||||||
| 			// prevent stampedes
 |  | ||||||
| 			meta.refreshAt = Date.now() + randomRefreshOffset(); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// TODO don't get unknown certs at all, rely on auto-updates from greenlock
 |  | ||||||
| 		// Note: greenlock.get() will return an existing fresh cert or issue a new one
 |  | ||||||
| 		return greenlock.get({ servername: servername }).then(function(result) { |  | ||||||
| 			var meta = getCachedMeta(servername); |  | ||||||
| 			if (!meta) { |  | ||||||
| 				meta = _cache[servername] = { secureContext: { _valid: false } }; |  | ||||||
| 			} |  | ||||||
| 			// prevent from being punked by bot trolls
 |  | ||||||
| 			meta.refreshAt = Date.now() + smallStagger; |  | ||||||
| 
 |  | ||||||
| 			// nothing to do
 |  | ||||||
| 			if (!result) { |  | ||||||
| 				return null; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			// we only care about the first one
 |  | ||||||
| 			var pems = result.pems; |  | ||||||
| 			var site = result.site; |  | ||||||
| 			if (!pems || !pems.cert) { |  | ||||||
| 				// nothing to do
 |  | ||||||
| 				// (and the error should have been reported already)
 |  | ||||||
| 				return null; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			meta = { |  | ||||||
| 				refreshAt: Date.now() + randomRefreshOffset(), |  | ||||||
| 				secureContext: tls.createSecureContext({ |  | ||||||
| 					// TODO support passphrase-protected privkeys
 |  | ||||||
| 					key: pems.privkey, |  | ||||||
| 					cert: pems.cert + "\n" + pems.chain + "\n" |  | ||||||
| 				}) |  | ||||||
| 			}; |  | ||||||
| 			meta.secureContext._valid = true; |  | ||||||
| 
 |  | ||||||
| 			// copy this same object into every place
 |  | ||||||
| 			(result.altnames || site.altnames || [result.subject || site.subject]).forEach(function(altname) { |  | ||||||
| 				_cache[altname] = meta; |  | ||||||
| 			}); |  | ||||||
| 
 |  | ||||||
| 			return meta.secureContext; |  | ||||||
| 		}); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	function getDefaultContext() { |  | ||||||
| 		return getCachedContext(defaultServername); |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // whenever we need to know when to refresh next
 |  | ||||||
| function randomRefreshOffset() { |  | ||||||
| 	var stagger = Math.round(refreshStagger / 2) - Math.round(Math.random() * refreshStagger); |  | ||||||
| 	return refreshOffset + stagger; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function validServername(servername) { |  | ||||||
| 	// format and (lightly) sanitize sni so that users can be naive
 |  | ||||||
| 	// and not have to worry about SQL injection or fs discovery
 |  | ||||||
| 
 |  | ||||||
| 	servername = (servername || "").toLowerCase(); |  | ||||||
| 	// hostname labels allow a-z, 0-9, -, and are separated by dots
 |  | ||||||
| 	// _ is sometimes allowed, but not as a "hostname", and not by Let's Encrypt ACME
 |  | ||||||
| 	// REGEX // https://www.codeproject.com/Questions/1063023/alphanumeric-validation-javascript-without-regex
 |  | ||||||
| 	return servernameRe.test(servername) && -1 === servername.indexOf(".."); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| function wildname(servername) { |  | ||||||
| 	return ( |  | ||||||
| 		"*." + |  | ||||||
| 		servername |  | ||||||
| 			.split(".") |  | ||||||
| 			.slice(1) |  | ||||||
| 			.join(".") |  | ||||||
| 	); |  | ||||||
| } |  | ||||||
| @ -1,85 +0,0 @@ | |||||||
| #!/usr/bin/env node
 |  | ||||||
| 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"), |  | ||||||
| 
 |  | ||||||
| 	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); |  | ||||||
| }); |  | ||||||
| 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"); |  | ||||||
| 	} |  | ||||||
| ); |  | ||||||
| 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();
 |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| 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 server6 = greenlock.listen("[::]:11080", "[::1]:11443"); |  | ||||||
| 
 |  | ||||||
| var server7 = greenlock.listen("/tmp/gl.plain.sock", "/tmp/gl.sec.sock"); |  | ||||||
							
								
								
									
										62
									
								
								worker.js
									
									
									
									
									
								
							
							
						
						
									
										62
									
								
								worker.js
									
									
									
									
									
								
							| @ -1,62 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
| 
 |  | ||||||
| var Worker = module.exports; |  | ||||||
| // *very* generous, but well below the http norm of 120
 |  | ||||||
| var messageTimeout = 30 * 1000; |  | ||||||
| var msgPrefix = "greenlock:"; |  | ||||||
| 
 |  | ||||||
| Worker.create = function() { |  | ||||||
| 	var greenlock = {}; |  | ||||||
| 	["getAcmeHttp01ChallengeResponse", "get", "notify"].forEach(function(k) { |  | ||||||
| 		greenlock[k] = function(args) { |  | ||||||
| 			return rpc(k, args); |  | ||||||
| 		}; |  | ||||||
| 	}); |  | ||||||
| 
 |  | ||||||
| 	var worker = { |  | ||||||
| 		serve: function(fn) { |  | ||||||
| 			var servers = require("./servers.js").create(greenlock); |  | ||||||
| 			fn(servers); |  | ||||||
| 			return worker; |  | ||||||
| 		}, |  | ||||||
| 		master: function() { |  | ||||||
| 			// ignore
 |  | ||||||
| 			return worker; |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| 	return worker; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| function rpc(funcname, msg) { |  | ||||||
| 	return new Promise(function(resolve, reject) { |  | ||||||
| 		var rnd = Math.random() |  | ||||||
| 			.toString() |  | ||||||
| 			.slice(2) |  | ||||||
| 			.toString(16); |  | ||||||
| 		var id = msgPrefix + rnd; |  | ||||||
| 		var timeout; |  | ||||||
| 
 |  | ||||||
| 		function getResponse(msg) { |  | ||||||
| 			if (msg._id !== id) { |  | ||||||
| 				return; |  | ||||||
| 			} |  | ||||||
| 			process.removeListener("message", getResponse); |  | ||||||
| 			clearTimeout(timeout); |  | ||||||
| 			resolve(msg._result); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// TODO keep a single listener than just responds
 |  | ||||||
| 		// via a collection of callbacks? or leave as is?
 |  | ||||||
| 		process.on("message", getResponse); |  | ||||||
| 		process.send({ |  | ||||||
| 			_id: id, |  | ||||||
| 			_funcname: funcname, |  | ||||||
| 			_input: msg |  | ||||||
| 		}); |  | ||||||
| 
 |  | ||||||
| 		timeout = setTimeout(function() { |  | ||||||
| 			process.removeListener("message", getResponse); |  | ||||||
| 			reject(new Error("worker rpc request timeout")); |  | ||||||
| 		}, messageTimeout); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user