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);
|
|
||||||
};
|
|
|
@ -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",
|
||||||
|
|
|
@ -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…
Reference in New Issue