use letsencrypt v2

This commit is contained in:
AJ ONeal 2016-08-09 22:39:39 -04:00
parent 688cb49c0a
commit 971a47eb76
8 changed files with 215 additions and 120 deletions

View File

@ -159,10 +159,13 @@ Options:
--debug BOOLEAN show traces and logs
--tls-sni-01-port NUMBER Port number to perform tls-sni-01 challenge.
Boulder in testing mode defaults to 5001. (default: 443 and 5001)
--tls-sni-01-port NUMBER Use TLS-SNI-01 challenge type with this port. (Default is 443)
(must be 443 with most production servers) (Boulder allows 5001 in testing mode)
--http-01-port [NUMBER] Port used in the SimpleHttp challenge. (Default is 80)
--http-01-port [NUMBER] Use HTTP-01 challenge type with this port, used for SimpleHttp challenge. (Default is 80)
(must be 80 with most production servers)
--dns-01 Use DNS-01 challenge type.
--rsa-key-size [NUMBER] Size (in bits) of the RSA key. (Default is 2048)

View File

@ -10,13 +10,15 @@ cli.parse({
, duplicate: [ false, " Allow getting a certificate that duplicates an existing one", 'boolean', false ]
, 'agree-tos': [ false, " Agree to the Let's Encrypt Subscriber Agreement", 'boolean', false ]
, debug: [ false, " show traces and logs", 'boolean', false ]
, 'tls-sni-01-port': [ false, " Port number to perform tls-sni-01 challenge. Boulder in testing mode defaults to 5001. (default: 443,5001)", 'string' ]
, 'http-01-port': [ false, " Port used in the SimpleHttp challenge. (default: 80)", 'string' ]
, 'tls-sni-01-port': [ false, " Use TLS-SNI-01 challenge type with this port (only port 443 is valid with most production servers) (default: 443,5001)", 'string' ]
, 'http-01-port': [ false, " Use HTTP-01 challenge type with this port (only port 80 is valid with most production servers) (default: 80)", 'string' ]
, 'dns-01': [ false, " Use DNS-01 challange type", 'boolean', false ]
, 'rsa-key-size': [ false, " Size (in bits) of the RSA key.", 'int', 2048 ]
, 'cert-path': [ false, " Path to where new cert.pem is saved", 'string',':config/live/:hostname/cert.pem' ]
, 'fullchain-path': [ false, " Path to where new fullchain.pem (cert + chain) is saved", 'string', ':config/live/:hostname/fullchain.pem' ]
, 'chain-path': [ false, " Path to where new chain.pem is saved", 'string', ':config/live/:hostname/chain.pem' ]
, 'cert-path': [ false, " Path to where new cert.pem is saved", 'string',':configDir/live/:hostname/cert.pem' ]
, 'fullchain-path': [ false, " Path to where new fullchain.pem (cert + chain) is saved", 'string', ':configDir/live/:hostname/fullchain.pem' ]
, 'chain-path': [ false, " Path to where new chain.pem is saved", 'string', ':configDir/live/:hostname/chain.pem' ]
, 'domain-key-path': [ false, " Path to privkey.pem to use for domain (default: generate new)", 'string' ]
, 'account-key-path': [ false, " Path to privkey.pem to use for account (default: generate new)", 'string' ]
, 'config-dir': [ false, " Configuration directory.", 'string', '~/letsencrypt/etc/' ]
, server: [ false, " ACME Directory Resource URI.", 'string', 'https://acme-v01.api.letsencrypt.org/directory)' ]
, standalone: [ false, " Obtain certs using a \"standalone\" webserver.", 'boolean', false ]
@ -49,7 +51,7 @@ cli.main(function(_, options) {
var val = args[key];
if ('string' === typeof val) {
val = val.replace(/\:config/, args.configDir);
val = val.replace(/(\:configDir)|(\:config)/, args.configDir);
}
args[key] = val;
@ -88,54 +90,6 @@ cli.main(function(_, options) {
return;
}
var LE = require('letsencrypt');
var handlers;
if (args.webrootPath) {
handlers = require('../lib/webroot').create(args);
}
else /*if (args.standalone)*/ {
handlers = require('../lib/standalone').create();
handlers.startServers(args.http01Port || [80], args.tlsSni01Port || [443, 5001]);
}
// let LE know that we're handling standalone / webroot here
LE.create({
manual: true
, debug: args.debug
, configDir: args.configDir
, privkeyPath: ':config/live/:hostname/privkey.pem' //args.privkeyPath
, fullchainPath: args.fullchainPath
, certPath: args.certPath
, chainPath: args.chainPath
}, handlers).register(args, function (err, results) {
if (err) {
console.error('[Error]: letsencrypt-cli');
console.error(err.stack || err);
return;
}
if (!results || ('object' !== typeof results)) {
console.error("Error: An unknown error occurred. My best guess is that we got an error that we're not used to from the ACME server and accidentally interpretted it as a success... or forgot to expose the error.");
console.error(results);
err = new Error("Here's a stack trace, in case it helps:");
console.error(err.stack);
return;
}
if (handlers.closeServers) {
handlers.closeServers();
}
// should get back account, path to certs, pems, etc?
console.log('\nCertificates installed at:');
console.log(Object.keys(results).filter(function (key) {
return /Path/.test(key);
}).map(function (key) {
return results[key];
}).join('\n'));
process.exit(0);
});
require('../').run(args);
});
});

4
example-standalone.bash Normal file → Executable file
View File

@ -1,7 +1,7 @@
#!/bin/bash
letsencrypt certonly \
--agree-tos --email coolaj86+le.1010@gmail.com \
node bin/letsencrypt certonly \
--agree-tos --email 'coolaj86+le.1010@gmail.com' \
--standalone \
--domains pokemap.hellabit.com,www.pokemap.hellabit.com \
--server https://acme-staging.api.letsencrypt.org/directory \

88
index.js Normal file
View File

@ -0,0 +1,88 @@
'use strict';
var LE = require('letsencrypt');
module.exports.run = function (args) {
var leChallenge;
var leStore;
var servers;
var USE_DNS = {};
var challengeType;
if (args.dns01) {
challengeType = 'dns-01';
args.webrootPath = '';
args.standalone = USE_DNS;
} else if (args.tlsSni01Port) {
challengeType = 'tls-sni-01';
} else /*if (args.http01Port)*/ {
challengeType = 'http-01';
}
if (args.webrootPath) {
// webrootPath is all that really matters here
leChallenge = require('./lib/webroot').create({ webrootPath: args.webrootPath });
}
else if (USE_DNS !== args.standalone) {
leChallenge = require('./lib/standalone').create({});
servers = require('./lib/servers').create(leChallenge).startServers(
args.http01Port || [80], args.tlsSni01Port || [443, 5001]
, { debug: args.debug }
);
}
leStore = require('le-store-certbot').create({
configDir: args.configDir
, privkeyPath: args.domainKeyPath || ':configDir/live/:hostname/privkey.pem' //args.privkeyPath
, fullchainPath: args.fullchainPath
, certPath: args.certPath
, chainPath: args.chainPath
, webrootPath: args.webrootPath
, domainKeyPath: args.domainKeyPath
, accountKeyPath: args.accountKeyPath
});
// let LE know that we're handling standalone / webroot here
var le = LE.create({
debug: args.debug
, server: args.server
, store: leStore
, challenge: leChallenge
, duplicate: args.duplicate
});
// Note: can't use args directly as null values will overwrite template values
le.register({
domains: args.domains
, email: args.email
, agreeTos: args.agreeTos
, challengeType: challengeType
, rsaKeySize: args.rsaKeySize
}).then(function (certs) {
if (servers) {
servers.closeServers();
}
// should get back account, path to certs, pems, etc?
console.log('\nCertificates installed at:');
console.log(Object.keys(args).filter(function (key) {
return /Path/.test(key);
}).map(function (key) {
return args[key];
}).join('\n').replace(/:hostname/, args.domains[0]));
console.log("");
console.log("Got certificate(s) for " + certs.altnames.join(', '));
console.log("\tIssued at " + new Date(certs.issuedAt).toISOString() + "");
console.log("\tValid until " + new Date(certs.expiresAt).toISOString() + "");
console.log("");
process.exit(0);
}, function (err) {
console.error('[Error]: letsencrypt-cli');
console.error(err.stack || new Error('get stack').stack);
process.exit(1);
});
};

89
lib/servers.js Normal file
View File

@ -0,0 +1,89 @@
'use strict';
var NOBJ = {};
module.exports.create = function (challenge) {
var servers = {
_servers: []
, httpResponder: function (req, res) {
console.log('[LE-CLI] httpResponder');
var acmeChallengePrefix = '/.well-known/acme-challenge/';
if (0 !== req.url.indexOf(acmeChallengePrefix)) {
res.end("Let's Encrypt! Command line tool");
return;
}
var token = req.url.slice(acmeChallengePrefix.length);
challenge.get(NOBJ, req.headers.host.replace(/:.*/, ''), token, function (err, val) {
res.end(val || '_ ERROR challenge not found _');
});
}
, startServers: function (plainPorts, tlsPorts, opts) {
opts = opts || {};
var httpsOptions = require('localhost.daplie.com-certificates');
var https = require('https');
var http = require('http');
if (servers._servers.length) {
return;
}
// http-01-port
plainPorts.forEach(function (port) {
var server = http.createServer(servers.httpResponder);
servers._servers.push(server);
server.listen(port, function () {
if (opts.debug) {
console.info('Listening http on', this.address());
}
});
server.on('error', function (err) {
if ('EADDRINUSE' === err.code) {
console.error("");
console.error("You already have a different server running on port '" + port + "'.");
console.error("You should probably use the --webroot-path option instead of --standalone.");
return;
}
throw err;
});
});
// tls-sni-01-port
tlsPorts.forEach(function (port) {
var server = https.createServer(httpsOptions, servers.httpResponder);
servers._servers.push(server);
servers.listen(port, function () {
if (opts.debug) {
console.info('Listening https on', this.address());
}
});
servers.on('error', function (err) {
if ('EADDRINUSE' === err.code) {
console.error("");
console.error("You already have a different server running on port '" + port + "'.");
console.error("You should probably use the --webroot-path option instead of --standalone.");
return;
}
throw err;
});
});
}
, closeServers: function () {
servers._servers.forEach(function (server) {
server.close();
});
servers._servers = [];
}
};
return servers;
};

View File

@ -1,75 +1,34 @@
'use strict';
module.exports.create = function () {
module.exports.create = function (defaults) {
var handlers = {
getOptions: function () {
return defaults;
}
//
// set,get,remove challenges
//
// Note: this is fine for a one-off CLI tool
// but a webserver using node-cluster or multiple
// servers should use a database of some sort
_challenges: {}
, setChallenge: function (args, key, value, cb) {
handlers._challenges[key] = value;
, _challenges: {}
, set: function (args, domain, token, secret, cb) {
console.log('bloh 1');
handlers._challenges[token] = secret;
cb(null);
}
, getChallenge: function (args, key, cb) {
, get: function (args, domain, token, cb) {
console.log('bloh 2');
// TODO keep in mind that, generally get args are just args.domains
// and it is disconnected from the flow of setChallenge and removeChallenge
cb(null, handlers._challenges[key]);
cb(null, handlers._challenges[token]);
}
, removeChallenge: function (args, key, cb) {
delete handlers._challenges[key];
, remove: function (args, domain, token, cb) {
console.log('balh 3');
delete handlers._challenges[token];
cb(null);
}
, _servers: []
, httpResponder: function (req, res) {
var acmeChallengePrefix = '/.well-known/acme-challenge/';
if (0 !== req.url.indexOf(acmeChallengePrefix)) {
res.end('Hello World!');
return;
}
var key = req.url.slice(acmeChallengePrefix.length);
handlers.getChallenge(req.headers.host, key, function (err, val) {
res.end(val || '_');
});
}
, startServers: function (plainPorts, tlsPorts, opts) {
opts = opts || {};
var httpsOptions = require('localhost.daplie.com-certificates');
var https = require('https');
var http = require('http');
// tls-sni-01-port
if (handlers._servers.length) {
return;
}
plainPorts.forEach(function (port) {
http.createServer(handlers.httpResponder).listen(port, function () {
if (opts.debug) {
console.info('Listening http on', this.address());
}
});
});
tlsPorts.forEach(function (port) {
https.createServer(httpsOptions, handlers.httpResponder).listen(port, function () {
if (opts.debug) {
console.info('Listening https on', this.address());
}
});
});
}
, closeServers: function () {
handlers._servers.forEach(function (server) {
server.close();
});
handlers._servers = [];
}
};
return handlers;

View File

@ -10,7 +10,7 @@ module.exports.create = function (defaults) {
// set,get,remove challenges
//
getOptions: function () {
return { webrootPath: defaults.webrootPath };
return defaults;
}
, set: function (args, domain, token, secret, cb) {

View File

@ -35,8 +35,10 @@
"dependencies": {
"cli": "^0.11.1",
"homedir": "^0.6.0",
"letsencrypt": "^1.4.3",
"localhost.daplie.com-certificates": "^1.1.2",
"le-acme-core": "^2.0.5",
"le-store-certbot": "^2.0.2",
"letsencrypt": "^2.0.3",
"localhost.daplie.com-certificates": "^1.2.0",
"mkdirp": "^0.5.1"
}
}