This commit is contained in:
AJ ONeal 2015-12-17 00:51:37 +00:00
parent 260f0592be
commit 419b84a1ab
3 changed files with 149 additions and 23 deletions

View File

@ -1,4 +1,5 @@
# letsencrypt-express
Free SSL and Automatic HTTPS for node.js with Express, Connect, and other middleware systems
## Coming Soon
@ -9,6 +10,78 @@ We're working on it
See [examples/express-minimal.js](https://github.com/Daplie/node-letsencrypt/blob/master/examples/express-minimal.js)
## Install
```
npm install --save letsencrypt-express
```
## Examples
**Minimal**
```javascript
'use strict';
var le = require('letsencrypt-express');
var express = require('express');
var app = express();
app.use('/', function (req, res) {
res.send({ success: true });
});
le.create('/etc/letsencrypt', app).listen([80], [443, 5001], function () {
console.log("ENCRYPT **ALL** THE DOMAINS!");
});
```
### More Options Exposed
```javascript
'use strict';
var le = require('letsencrypt-express');
var express = require('express');
var app = express();
app.use('/', function (req, res) {
res.send({ success: true });
});
var results = le.create({
configDir: '/etc/letsencrypt'
, onRequest: app
}).listen(
// you can give just the port, or expand out to the full options
[80, { port: 8080, address: 'localhost', onListening: function () { console.log('http://localhost'); } }]
// you can give just the port, or expand out to the full options
, [443, 5001, { port: 8443, address: 'localhost' }]
// this is pretty much the default onListening handler
, function onListening() {
var server = this;
var protocol = ('requestCert' in server) ? 'https': 'http';
console.log("Listening at " + protocol + '://localhost:' + this.address().port);
}
);
// In case you need access to the raw servers (i.e. using websockets)
console.log(results.plainServers);
console.log(results.tlsServers);
```
### WebSockets with Let's Encrypt
Note: you don't need to create websockets for the plain ports.
```
results.tlsServers.forEach(function (server) {
});
```
## Options
If any of these values are `undefined` or `null` the will assume use reasonable defaults.
@ -18,6 +91,9 @@ Partially defined values will be merged with the defaults.
Setting the value to `false` will, in many cases (as documented), disable the defaults.
```
configDir: string //
webrootPath: string // string a path to a folder where temporary challenge files will be stored and read
// default os.tmpdir() + path.sep + 'acme-challenge'
@ -45,4 +121,15 @@ httpsOptions: object // object will be merged with internal defau
sniCallback: func // func replace the default sniCallback handler (which manages certificates) with your own
letsencrypt: object // object configure the letsencrypt object yourself and pass it in directly
//
// default we create the letsencrypt object using parameters you specify
```
## Heroku?
This doesn't work on heroku because heroku uses a proxy with built-in https
(which is a smart thing to do) and besides, they want you to pay big bucks
for https. (hopefully not for long?...)

31
lib/challenge-handlers.js Normal file
View File

@ -0,0 +1,31 @@
'use strict';
var fs = require('fs');
var path = require('path');
// TODO handle templating :hostname in letsencrypt proper
// Note: we're explicitly doing this on the filesystem
// rather than in-memory to support node cluster
module.exports = {
set: function setChallenge(args, hostname, key, value, cb) {
var keyfile = path.join((args.webrootPath || args.webrootTpl).replace(':hostname', hostname), key);
fs.writeFile(keyfile, value, 'utf8', cb);
}
, get: function getChallenge(args, hostname, key, cb) {
var keyfile = path.join((args.webrootPath || args.webrootTpl).replace(':hostname', hostname), key);
fs.readFile(keyfile, 'utf8', cb);
}
, remove: function removeChallenge(args, hostname, key, cb) {
var keyfile = path.join((args.webrootPath || args.webrootTpl).replace(':hostname', hostname), key);
// Note: it's not actually terribly important that we wait for the unlink callback
// but it's a polite thing to do - and we're polite people!
fs.unlink(keyfile, cb);
}
};

View File

@ -1,22 +1,10 @@
'use strict';
var path = require('path');
var challengeStore = require('./lib/challange-handlers');
function getChallenge(args, hostname, key, cb) {
var fs = require('fs');
var keyfile = path.join((args.webrootPath || args.webrootTpl).replace(':hostname', hostname), key);
fs.readFile(keyfile, 'utf8', function (err, text) {
if (err) {
cb(err);
return;
}
cb(null, text);
});
}
function create(obj) {
function create(obj, app) {
var LE = require('letsencrypt');
var https = require('https');
var http = require('http');
@ -26,12 +14,23 @@ function create(obj) {
if (!obj) {
obj = {};
}
else if ('function' === typeof obj) {
if ('string' === typeof obj) {
obj = {
configDir: obj
};
}
if ('function' === typeof obj) {
obj = {
onRequest: obj
};
}
if ('function' === typeof app) {
obj.onRequest = obj.onRequest || app;
}
if (!obj.getChallenge) {
if (false !== obj.getChallenge) {
obj.getChallenge = getChallenge;
@ -41,11 +40,18 @@ function create(obj) {
}
}
if (!obj.onRequest) {
console.warn("You did not specify args.onRequest, using 'Hello, World!'");
obj.onRequest = function (req, res) {
res.end('Hello, World!');
};
if (!obj.onRequest && false !== obj.onRequest) {
console.warn("You should either do args.onRequest = app or server.on('request', app),"
+ " otherwise only acme-challenge requests will be handled (and the rest will hang)");
console.warn("You can silence this warning by setting args.onRequest = false");
}
if (!obj.letsencrypt) {
//LE.merge(obj, );
obj.letsencrypt = LE.create(obj, {
setChallenge: setChallenge
, removeChallenge: removeChallenge
});
}
function acmeResponder(req, res) {
@ -142,7 +148,7 @@ function create(obj) {
// deleting creates a "slow object", but that's okay (we only use it once)
return results;
};
}
return {
listen: listen
@ -151,4 +157,6 @@ function create(obj) {
module.exports = create;
module.exports.create = create;
module.exports.getChallenge = getChallenge;
module.exports.setChallenge = challengeStore.set;
module.exports.getChallenge = challengeStore.get;
module.exports.removeChallenge = challengeStore.remove;