Merge branch 'master' of github.com:Daplie/letiny-core

This commit is contained in:
AJ ONeal 2015-12-16 09:49:23 +00:00
commit 78c9daa059
3 changed files with 241 additions and 115 deletions

3
AUTHORS Normal file
View File

@ -0,0 +1,3 @@
ISRG
Anatol Sommer <anatol@anatol.at>
AJ ONeal <aj@daplie.com> (https://daplie.com/)

292
README.md
View File

@ -14,11 +14,11 @@ Supports all of:
This is a library / framework for building letsencrypt clients. This is a library / framework for building letsencrypt clients.
You probably want one of these pre-built clients instead: You probably want one of these pre-built clients instead:
* `letsencrypt` (100% compatible with the official client) * [`letsencrypt`](https://github.com/Daplie/node-letsencrypt) (compatible with the official client)
* `letiny` (lightweight client) * `letiny` (lightweight client cli)
* `letsencrypt-express` (automatic https for express) * `letsencrypt-express` (automatic https for express)
## Usage: ## Install & Usage:
```bash ```bash
npm install --save letiny-core npm install --save letiny-core
@ -34,116 +34,69 @@ You will follow these steps to obtain certificates:
* implement a method to get the challenge token as `getChallenge` * implement a method to get the challenge token as `getChallenge`
* implement a method to remove the challenge token as `removeChallenge` * implement a method to remove the challenge token as `removeChallenge`
```javascript ### Demo
'use strict';
var LeCore = require('letiny-core'); You can see this working for yourself, but you'll need to be on an internet connected computer with a domain.
var accountPrivateKeyPem = '...'; // leCrypto.generateRsaKeypair(bitLen, exp, cb) Get a temporary domain for testing
var domainPrivateKeyPem = '...'; // (same)
var challengeStore = { /*get, set, remove*/ }; // see below for example
LeCore.getAcmeUrls( ```bash
LeCore.stagingServerUrl // or choose LeCore.productionServerUrl npm install -g ddns-cli
, function (err, urls) { ddns --random --email user@example.com --agree
LeCore.registerNewAccount(
{ newRegUrl: urls.newReg
, email: 'user@example.com'
, accountPrivateKeyPem: accountPrivateKeyPem
, agreeToTerms: function (tosUrl, done) {
// agree to these exact terms
done(null, tosUrl);
}
}
, function (err, regr) {
// Note: you should save the registration
// record to disk (or db)
LeCore.getCertificate(
{ newAuthzUrl: urls.newAuthz
, newCertUrl: urls.newCert
, domainPrivateKeyPem: domainPrivateKeyPem
, accountPrivateKeyPem: accountPrivateKeyPem
, setChallenge: challengeStore.set
, removeChallenge: challengeStore.remove
}
, function (err, certs) {
// Note: you should save certs to disk (or db)
}
)
}
);
}
);
``` ```
That will fail unless you have a webserver running on 80 and 443 (or 5001) Note: use **YOUR EMAIL** and accept the terms of service (run `ddns --help` to see them).
to respond to `/.well-known/acme-challenge/xxxxxxxx` with the proper token
```javascript <!-- TODO tutorial on ddns -->
var localCerts = require('localhost.daplie.com-certificates'); // needs default certificates
var http = require('http');
var httsp = require('https');
function acmeResponder(req, res) { Install letiny-core and its dependencies. **Note**: it's okay if you're on windows
if (0 !== req.url.indexOf(LeCore.acmeChallengePrefixUrl)) { and `ursa` fails to compile. It'll still work.
res.end('Hello World!');
return;
}
LeCore. ```bash
} git clone https://github.com/Daplie/letiny-core.git ~/letiny-core
pushd ~/letiny-core
http.createServer() npm install
``` ```
Finally, you need an implementation of `challengeStore`: Run the demo:
```javascript ```bash
var challengeCache = {}; node examples/letsencrypt.js user@example.com example.com
var challengeStore = {
set: function (hostname, key, value, cb) {
challengeCache[key] = value;
cb(null);
}
, get: function (hostname, key, cb) {
cb(null, challengeCache[key]);
}
, remove: function (hostname, key, cb) {
delete challengeCache[key];
cb(null);
}
};
``` ```
Note: use **YOUR TEMPORARY DOMAIN** and **YOUR EMAIL**.
## API ## API
The Goodies The Goodies
```javascript ```javascript
{ newRegUrl: '...' // no defaults, specify LeCore.nproductionServerUrl
// Accounts // Accounts
LeCore.registerNewAccount(options, cb) // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert}) LeCore.registerNewAccount(options, cb) // returns "regr" registration data
{ newRegUrl: '...' // no defaults, specify LeCore.newAuthz { newRegUrl: '<url>' // no defaults, specify acmeUrls.newAuthz
, email: '...' // valid email (server checks MX records) , email: '<email>' // valid email (server checks MX records)
, agreeToTerms: fn (tosUrl, cb) {} // callback to allow user interaction for tosUrl , accountPrivateKeyPem: '<ASCII PEM>' // callback to allow user interaction for tosUrl
// cb(err=null, agree=tosUrl) // must specify agree=tosUrl to continue (or falsey to end) , agreeToTerms: fn (tosUrl, cb) {} // must specify agree=tosUrl to continue (or falsey to end)
} }
// Registration // Registration
LeCore.getCertificate(options, cb) LeCore.getCertificate(options, cb) // returns (err, pems={ key, cert, ca })
{ newAuthzUrl: '...' // no defaults, specify acmeUrls.newAuthz { newAuthzUrl: '<url>' // specify acmeUrls.newAuthz
, newCertUrl: '<url>' // specify acmeUrls.newCert
, domainPrivateKeyPem: '<ASCII PEM>'
, accountPrivateKeyPem: '<ASCII PEM>'
, domains: ['example.com']
, setChallenge: fn (hostname, key, val, cb)
, removeChallenge: fn (hostname, key, cb)
}
// Discovery URLs
LeCore.getAcmeUrls(acmeDiscoveryUrl, cb) // returns (err, acmeUrls={newReg,newAuthz,newCert,revokeCert})
``` ```
Helpers & Stuff Helpers & Stuff
@ -194,6 +147,169 @@ LeCore.getAcmeUrls(discoveryUrl, function (err, urls) {
}); });
``` ```
## Example
Below you'll find a stripped-down example. You can see the full example in the example folder.
* [example/](https://github.com/Daplie/letiny-core/blob/master/example/)
#### Register Account & Domain
This is how you **register an ACME account** and **get an HTTPS certificate**
```javascript
'use strict';
var LeCore = require('letiny-core');
var email = 'user@example.com'; // CHANGE TO YOUR EMAIL
var domains = 'example.com'; // CHANGE TO YOUR DOMAIN
var acmeDiscoveryUrl = LeCore.stagingServerUrl; // CHANGE to production, when ready
var accountPrivateKeyPem = null;
var domainPrivateKeyPem = null;
var acmeUrls = null;
LeCore.leCrypto.generateRsaKeypair(2048, 65537, function (err, pems) {
// ...
LeCore.getAcmeUrls(acmeDiscoveryUrl, function (err, urls) {
// ...
runDemo();
});
});
function runDemo() {
LeCore.registerNewAccount(
{ newRegUrl: acmeUrls.newReg
, email: email
, accountPrivateKeyPem: accountPrivateKeyPem
, agreeToTerms: function (tosUrl, done) {
// agree to the exact version of these terms
done(null, tosUrl);
}
}
, function (err, regr) {
LeCore.getCertificate(
{ newAuthzUrl: acmeUrls.newAuthz
, newCertUrl: acmeUrls.newCert
, domainPrivateKeyPem: domainPrivateKeyPem
, accountPrivateKeyPem: accountPrivateKeyPem
, domains: domains
, setChallenge: challengeStore.set
, removeChallenge: challengeStore.remove
}
, function (err, certs) {
// Note: you should save certs to disk (or db)
certStore.set(domains[0], certs, function () {
// ...
});
}
);
}
);
}
```
**But wait**, there's more!
See [example/letsencrypt.js](https://github.com/Daplie/letiny-core/blob/master/example/letsencrypt.js)
#### Run a Server on 80, 443, and 5001 (https/tls)
That will fail unless you have a webserver running on 80 and 443 (or 5001)
to respond to `/.well-known/acme-challenge/xxxxxxxx` with the proper token
```javascript
var https = require('https');
var http = require('http');
var LeCore = deps.LeCore;
var httpsOptions = deps.httpsOptions;
var challengeStore = deps.challengeStore;
var certStore = deps.certStore;
//
// Challenge Handler
//
function acmeResponder(req, res) {
if (0 !== req.url.indexOf(LeCore.acmeChallengePrefix)) {
res.end('Hello World!');
return;
}
var key = req.url.slice(LeCore.acmeChallengePrefix.length);
challengeStore.get(req.hostname, key, function (err, val) {
res.end(val || 'Error');
});
}
//
// Server
//
https.createServer(httpsOptions, acmeResponder).listen(5001, function () {
console.log('Listening https on', this.address());
});
http.createServer(acmeResponder).listen(80, function () {
console.log('Listening http on', this.address());
});
```
**But wait**, there's more!
See [example/serve.js](https://github.com/Daplie/letiny-core/blob/master/example/serve.js)
#### Put some storage in place
Finally, you need an implementation of `challengeStore`:
```javascript
var challengeCache = {};
var challengeStore = {
set: function (hostname, key, value, cb) {
challengeCache[key] = value;
cb(null);
}
, get: function (hostname, key, cb) {
cb(null, challengeCache[key]);
}
, remove: function (hostname, key, cb) {
delete challengeCache[key];
cb(null);
}
};
var certCache = {};
var certStore = {
set: function (hostname, certs, cb) {
certCache[hostname] = certs;
cb(null);
}
, get: function (hostname, cb) {
cb(null, certCache[hostname]);
}
, remove: function (hostname, cb) {
delete certCache[hostname];
cb(null);
}
};
```
**But wait**, there's more!
See
* [example/challenge-store.js](https://github.com/Daplie/letiny-core/blob/master/challenge-store.js)
* [example/cert-store.js](https://github.com/Daplie/letiny-core/blob/master/cert-store.js)
## Authors ## Authors
* ISRG * ISRG

View File

@ -1,18 +1,25 @@
{ {
"name": "letiny-core", "name": "letiny-core",
"version": "1.0.0", "version": "1.0.1",
"description": "A framework for building letsencrypt clients, forked from `letiny`", "description": "A framework for building letsencrypt clients, forked from letiny",
"authors": [
"Anatol Sommer <anatol@anatol.at>",
"AJ ONeal <aj@daplie.com>"
],
"main": "node.js", "main": "node.js",
"browser": "browser.js", "browser": "browser.js",
"license": "MPL-2.0", "directories": {
"example": "example",
"test": "test"
},
"scripts": {
"test": "node example/letsencrypt.js"
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/Daplie/letiny-core.git" "url": "git+https://github.com/Daplie/letiny-core.git"
}, },
"license": "MPL-2.0",
"bugs": {
"url": "https://github.com/Daplie/letiny-core/issues"
},
"homepage": "https://github.com/Daplie/letiny-core#readme",
"keywords": [ "keywords": [
"tiny", "tiny",
"acme", "acme",