Compare commits

...

2 Commits

Author SHA1 Message Date
AJ ONeal b9c5a26047 v2.3.0 2018-05-16 01:30:13 +00:00
AJ ONeal 658e7543a0 update docs, update to greenlock-v2.2.x 2018-05-16 01:29:58 +00:00
6 changed files with 377 additions and 197 deletions

267
README.md
View File

@ -9,6 +9,7 @@ Free SSL, Free Wildcard SSL, and Fully Automated HTTPS made dead simple<br>
!["Lifetime Downloads"](https://img.shields.io/npm/dt/greenlock.svg "Lifetime Download Count can't be shown") !["Lifetime Downloads"](https://img.shields.io/npm/dt/greenlock.svg "Lifetime Download Count can't be shown")
!["Monthly Downloads"](https://img.shields.io/npm/dm/greenlock.svg "Monthly Download Count can't be shown") !["Monthly Downloads"](https://img.shields.io/npm/dm/greenlock.svg "Monthly Download Count can't be shown")
!["Weekly Downloads"](https://img.shields.io/npm/dw/greenlock.svg "Weekly Download Count can't be shown") !["Weekly Downloads"](https://img.shields.io/npm/dw/greenlock.svg "Weekly Download Count can't be shown")
!["Stackoverflow Questions"](https://img.shields.io/stackexchange/stackoverflow/t/greenlock.svg "S.O. Question count can't be shown")
| Sponsored by [ppl](https://ppl.family) | Sponsored by [ppl](https://ppl.family)
| **Greenlock for Web Servers** | **Greenlock for Web Servers**
@ -26,168 +27,202 @@ Features
- [x] One-off standalone registration / renewal - [x] One-off standalone registration / renewal
- [x] On-the-fly registration / renewal via webroot - [x] On-the-fly registration / renewal via webroot
## Install Node Install
=======
For **Windows**: Mac & Linux
-----------
Choose **Stable** from <https://nodejs.org/en/> Open Terminal and run this install script:
For Linux and **OS X**:
```
curl -L bit.ly/nodejs-min | bash
```
# Install Greenlock
```bash ```bash
npm install -g greenlock-cli@2.x curl -fsS https://get.greenlock.app/ | bash
``` ```
## Usage This will install greenlock to `/opt/greenlock` and put a symlink to
`/opt/greenlock/bin/greenlock` in `/usr/local/bin/greenlock` for convenience.
These commands are shown using the **testing server**. You can customize the installation:
Want to use the **live server**?
1. change server to `--server https://acme-v01.api.letsencrypt.org/directory`
**Note**: This has really only been tested with single domains so if
multiple domains doesn't work for you, file a bug.
### Standalone (primarily for testing)
You can run standalone mode to get a cert **on the server**. You either use an
http-01 challenge (the default) on port 80. Like so:
```bash ```bash
greenlock certonly \ export NODEJS_VER=v8.11.1
--agree-tos --email john.doe@example.com \ export GREENLOCK_PATH=/opt/greenlock
--standalone \ curl -fsS https://get.greenlock.app/ | bash
--domains example.com,www.example.com \
--server https://acme-staging.api.letsencrypt.org/directory \
--config-dir ~/letsencrypt/etc
``` ```
Then you can see your certs at `~/letsencrypt/etc/live`. This will change which version of node.js is bundled with greenlock
and the path to which greenlock installs.
Windows & Node.js
-----------------
1. Install [node.js](https://nodejs.org)
2. Open _Node.js_
2. Run the command `npm install -g greenlock-cli`
Usage
=====
We have a few different examples of issuing SSL certificates:
* Standalone (testing): Issue a one-off certificate
* Webroot (production): Automatic certificate renewal for Apache, Nginx, HAProxy, etc
* Manual (debugging): Go through the certificate proccess step-by-step
<!-- * Server (production): Leave it all to Greenlock -->
**Important Note**: Staging vs Production
Each of these examples are using the **staging server**.
Once you've successfully gotten certificates with the staging server
you must **delete** `--config-dir` (i.e. `rm -rf ~/acme`) and then
switch to the **production server**.
``` ```
ls ~/letsencrypt/etc/live --acme-version draft-11 --server https://acme-v02.api.letsencrypt.org/directory \
``` ```
This option is great for testing, but since it requires the use of ## Standalone
the same ports that your webserver needs, it isn't a good choice
for production.
### WebRoot <small>**primarily for testing**</small>
You can specify the path to where you keep your `index.html` with `webroot`, as You can run in standalone mode **on your server** and get a cert instantly.
long as your server is serving plain HTTP on port 80.
For example, if I want to get a domain for `example.com` and my `index.html` is Note: No other webserver may be running at the time (use Webroot mode for that).
at `/srv/www/example.com`, then I would use this command:
```bash ```bash
sudo greenlock certonly \ sudo greenlock certonly --standalone \
--agree-tos --email john.doe@example.com \ --acme-version draft-11 --acme-url https://acme-staging-v02.api.letsencrypt.org/directory \
--webroot --webroot-path /srv/www/example.com \ --agree-tos --email jon@example.com --domains example.com,www.example.com \
--config-dir /etc/letsencrypt \ --community-member \
--domains example.com,www.example.com \ --config-dir ~/acme/etc
--server https://acme-staging.api.letsencrypt.org/directory
``` ```
Note that we use `sudo` because in this example we are using `/etc/letsencrypt` ## WebRoot
as the cert directory rather than `~/letsencrypt/etc`, which we used in the previous example.
Then see your brand new shiny certs: <small>**for testing and production**</small>
``` With this method you must use **your existing http (port 80) server** (Apache, Nginx, HAProxy, etc).
ls /etc/letsencrypt/live/ You will specify the **path or template path** to your `public_html` or `www` webroot.
For example:
* I want to get an SSL cert for `example.com`
* `index.html` lives at `/srv/www/example.com`
* I would use this command:
```bash
sudo greenlock certonly --webroot \
--acme-version draft-11 --acme-url https://acme-staging-v02.api.letsencrypt.org/directory \
--agree-tos --email jon@example.com --domains example.com \
--community-member \
--root /srv/www/example.com \
--config-dir ~/acme/etc
``` ```
You can use a cron job to run the script above every 80 days (the certificates expire after 90 days) Now let's say that
so that you always have fresh certificates.
### Interactive (for debugging) * I have many sites in `/srv/www/`, all by their name
* I already store my ssl certs in the format `/etc/apache/ssl/:hostname/{key.pem,ssl.crt}`
* I'll run this command instead:
```bash
sudo greenlock certonly --webroot \
--acme-version draft-11 --acme-url https://acme-staging-v02.api.letsencrypt.org/directory \
--agree-tos --email jon@example.com --domains example.com,whatever.com,foobar.net \
--community-member \
--root "/srv/www/:hostname" \
--privkey-path "/etc/apache/ssl/:hostname/key.pem" \
--fullchain-path "/etc/apache/ssl/:hostname/ssl.crt" \
--config-dir ~/acme/etc
```
### Run with cron
Those commands are safe to be run **daily** with cron.
The certificates will automatically renew 2 weeks before expiring.
## Interactive
<small>**primarily for debugging**</small>
The token (for all challenge types) and keyAuthorization (only for https-01) The token (for all challenge types) and keyAuthorization (only for https-01)
will be printed to the screen and you will be given time to copy it wherever will be printed to the screen and you will be given time to copy it wherever
(file, dns record, database, etc) and the process will complete once you hit `enter`. (file, dns record, database, etc) and the process will complete once you hit `enter`.
```bash ```bash
sudo greenlock certonly \ sudo greenlock certonly --manual \
--agree-tos --email john.doe@example.com \ --acme-version draft-11 --acme-url https://acme-staging-v02.api.letsencrypt.org/directory \
--manual --agree-tos --email jon@example.com --domains example.com \
--config-dir /etc/letsencrypt \ --community-member \
--domains example.com,www.example.com \ --config-dir ~/acme/etc
--server https://acme-staging.api.letsencrypt.org/directory
``` ```
## Test with a free domain Certificate Locations
=====================
Then you can see your certs at `~/acme/etc/live`.
```
~/acme/etc/
└── example.com
├── cert.pem
├── chain.pem
├── fullchain.pem (Apache, Nginx, node.js)
├── privkey.pem (Apache, Nginx, node.js)
└── bundle.pem (HAProxy)
```
## Run without root (no sudo)
`sudo` is used to allow greenlock to use port 80 and write to httpd-owned directories.
Allow greenlock to bind on system ports without root:
```bash ```bash
# Install Daplie DNS sudo setcap cap_net_bind_service=+ep /opt/greenlock/bin/node
npm install -g ddns-cli
# see terms of use
ddns --help
# agree to terms and get domain
ddns --random --email user@example.com --agree
# the default is to use the ip address from which
# you can the command, but you can also assign the
# ip manually
ddns --random --email user@example.com --agree -a '127.0.0.1'
``` ```
Example domain: To allow greenlock to write to folders owned by another user, set it to run as that user.
``` Otherwise, you can change the permissions on the folders, which is
rubber-duck-42.daplie.me **probably a BAD IDEA**. Probabry a **security risk**.
``` But since some of you are going to do it anyway I might as well tell you how:
## Run without Root
If you'd like to allow node.js to use privileged ports `80` and `443`
(and everything under 1024 really) without being run as `root` or `sudo`,
you can use `setcap` to do so. (it may need to be run any time you reinstall node as well)
```bash
sudo setcap cap_net_bind_service=+ep /usr/local/bin/node
```
By default `node-greenlock` assumes your home directory `~/letsencrypt/`, but if
you really want to use `/etc/letsencrypt`, `/var/lib/letsencrypt/`, and `/var/log/letsencrypt`
you could change the permissions on them. **Probably a BAD IDEA**. Probabry a security risk.
``` ```
# PROBABLY A BAD IDEA # PROBABLY A BAD IDEA
sudo chown -R $(whoami) /etc/letsencrypt /var/lib/letsencrypt /var/log/letsencrypt sudo chown -R $(whoami) /etc/ssl /etc/acme
``` ```
## Command Line Options Command Line Options
====================
``` ```
Usage: Usage:
greenlock [OPTIONS] [ARGS] greenlock [OPTIONS] [ARGS]
Options: Options:
--server [STRING] ACME Directory Resource URI. (Default is https://acme-v01.api.letsencrypt.org/directory)) --acme-version [STRING] 'draft-11' for Let's Encrypt v2 or 'v01' for Let's Encrypt v1. (default: null)
--acme-url [URL] Directory URL for ACME API. Let's Encrypt URLs are:
draft-11
https://acme-staging-v02.api.letsencrypt.org/directory
https://acme-v02.api.letsencrypt.org/directory
v01
https://acme-staging.api.letsencrypt.org/directory
https://acme-v01.api.letsencrypt.org/directory
--email EMAIL Email used for registration and recovery contact. (default: null) --email EMAIL Email used for registration and recovery contact. (default: null)
--agree-tos BOOLEAN Agree to the Let's Encrypt Subscriber Agreement --agree-tos BOOLEAN Agree to the Let's Encrypt Subscriber Agreement
--domains URL Domain names to apply. For multiple domains you can enter a comma --community-member Submit stats to and receive updates from Greenlock
--domains HOSTNAME Domain names to apply. For multiple domains you can enter a comma
separated list of domains as a parameter. (default: []) separated list of domains as a parameter. (default: [])
--renew-within [NUMBER] Renew certificates this many days before expiry. (default: 7) --renew-within [NUMBER] Renew certificates this many days before expiry. (default: 10)
--duplicate BOOLEAN Allow getting a certificate that duplicates an existing one/is
an early renewal.
--rsa-key-size [NUMBER] Size (in bits) of the RSA key. (Default is 2048)
--cert-path STRING Path to where new cert.pem is saved --cert-path STRING Path to where new cert.pem is saved
(Default is :conf/live/:hostname/cert.pem) (Default is :conf/live/:hostname/cert.pem)
@ -198,6 +233,9 @@ Options:
--chain-path [STRING] Path to where new chain.pem is saved --chain-path [STRING] Path to where new chain.pem is saved
(Default is :conf/live/:hostname/chain.pem) (Default is :conf/live/:hostname/chain.pem)
--bundle-path [STRING] Path to where new bundle.pem (fullchain + privkey) is saved
(Default is :conf/live/:hostname/bundle.pem)
--domain-key-path STRING Path to privkey.pem to use for domain (default: generate new) --domain-key-path STRING Path to privkey.pem to use for domain (default: generate new)
--account-key-path STRING Path to privkey.pem to use for account (default: generate new) --account-key-path STRING Path to privkey.pem to use for account (default: generate new)
@ -214,13 +252,26 @@ Options:
--manual [BOOLEAN] Print the token and key to the screen and wait for you to hit enter, --manual [BOOLEAN] Print the token and key to the screen and wait for you to hit enter,
giving you time to copy it somewhere before continuing. (Default is false) giving you time to copy it somewhere before continuing. (Default is false)
--webroot BOOLEAN Obtain certs by placing files in a webroot directory.
--webroot-path STRING public_html / webroot path.
--debug BOOLEAN show traces and logs --debug BOOLEAN show traces and logs
-h, --help Display help and usage details -h, --help Display help and usage details
``` ```
Certbot Command Line Options
============================
These options are maintained for compatability with certbot:
```
--server [STRING] ACME Directory Resource URI. (Default is https://acme-v01.api.letsencrypt.org/directory))
--duplicate BOOLEAN Allow getting a certificate that duplicates an existing one/is
an early renewal.
--webroot BOOLEAN Obtain certs by placing files in a webroot directory.
--webroot-path STRING public_html / webroot path.
```
Note: some of the options may not be fully implemented. If you encounter a problem, please report a bug on the issues page. Note: some of the options may not be fully implemented. If you encounter a problem, please report a bug on the issues page.

View File

@ -5,49 +5,67 @@ var cli = require('cli');
var mkdirp = require('mkdirp'); var mkdirp = require('mkdirp');
cli.parse({ cli.parse({
server: [ false, " ACME Directory Resource URI.", 'string', '' ] 'acme-version':
, email: [ false, " Email used for registration and recovery contact. (default: null)", 'email' ] [ false, " ACME / Let's Encrypt version. v01 or draft-11 (aka v02)", 'string', 'draft-11' ]
, 'acme-url':
[ false, " ACME Directory Resource URL", 'string', '' ]
, email:
[ false, " Email used for registration and recovery contact. (default: null)", 'email' ]
, 'agree-tos': [ false, " Agree to the Let's Encrypt Subscriber Agreement", 'boolean', false ] , 'agree-tos': [ false, " Agree to the Let's Encrypt Subscriber Agreement", 'boolean', false ]
, domains: [ false, " Domain names to apply. For multiple domains you can enter a comma separated list of domains as a parameter. (default: [])", 'string' ] , 'community-member': [ false, " Submit stats to and get updates from Greenlock", 'boolean', false ]
, domains:
[ false, " Domain names to apply. For multiple domains you can enter a comma separated list of domains as a parameter. (default: [])", 'string' ]
, 'renew-within': [ false, " Renew certificates this many days before expiry", 'int', 7 ] , 'renew-within': [ false, " Renew certificates this many days before expiry", 'int', 7 ]
, duplicate: [ false, " Allow getting a certificate that duplicates an existing one/is an early renewal", 'boolean', false ] , 'cert-path':
, 'rsa-key-size': [ false, " Size (in bits) of the RSA key.", 'int', 2048 ] [ false, " Path to where new cert.pem is saved", 'string'
, 'cert-path': [ false, " Path to where new cert.pem is saved", 'string',':configDir/live/:hostname/cert.pem' ] , ':configDir/live/:hostname/cert.pem' ]
, 'fullchain-path': [ false, " Path to where new fullchain.pem (cert + chain) is saved", 'string', ':configDir/live/:hostname/fullchain.pem' ] , 'fullchain-path':
, 'chain-path': [ false, " Path to where new chain.pem is saved", 'string', ':configDir/live/:hostname/chain.pem' ] [ false, " Path to where new fullchain.pem (cert + chain) is saved", 'string'
, 'domain-key-path': [ false, " Path to privkey.pem to use for domain (default: generate new)", 'string' ] , ':configDir/live/:hostname/fullchain.pem' ]
, 'account-key-path': [ false, " Path to privkey.pem to use for account (default: generate new)", 'string' ] , 'bundle-path':
, 'config-dir': [ false, " Configuration directory.", 'string', '~/letsencrypt/etc/' ] [ false, " Path to where new bundle.pem (fullchain + privkey) is saved", 'string'
, 'tls-sni-01-port': [ false, " Use TLS-SNI-01 challenge type with this port (only port 443 is valid with most production servers)", 'int' ] , ':configDir/live/:hostname/bundle.pem' ]
, 'chain-path':
[ false, " Path to where new chain.pem is saved", 'string'
, ':configDir/live/:hostname/chain.pem' ]
, 'privkey-path':
[ false, " Path to where privkey.pem is saved", 'string'
, ':configDir/live/:hostname/privkey.pem' ]
, 'config-dir':
[ false, " Configuration directory.", 'string'
, '~/letsencrypt/etc/' ]
, 'http-01-port': [ false, " Use HTTP-01 challenge type with this port (only port 80 is valid with most production servers) (default: 80)", 'int' ] , 'http-01-port': [ false, " Use HTTP-01 challenge type with this port (only port 80 is valid with most production servers) (default: 80)", 'int' ]
, 'dns-01': [ false, " Use DNS-01 challange type", 'boolean', false ] , 'dns-01': [ false, " Use DNS-01 challange type", 'boolean', false ]
, standalone: [ false, " Obtain certs using a \"standalone\" webserver.", 'boolean', false ] , standalone: [ false, " Obtain certs using a \"standalone\" webserver.", 'boolean', false ]
, manual: [ false, " Print the token and key to the screen and wait for you to hit enter, giving you time to copy it somewhere before continuing (default: false)", 'boolean', false ] , manual: [ false, " Print the token and key to the screen and wait for you to hit enter, giving you time to copy it somewhere before continuing (default: false)", 'boolean', false ]
, webroot: [ false, " Obtain certs by placing files in a webroot directory.", 'boolean', false ]
, 'webroot-path': [ false, " public_html / webroot path.", 'string' ]
, hooks: [ false, " Obtain certs with hooks that configure a webserver to meet TLS-SNI-01 challenges.", 'boolean', false ]
, 'hooks-path': [ false, " Path in which to store files for hooks.", 'string' ]
, 'hooks-server': [ false, " Type of webserver to configure.", 'string' ]
, 'hooks-template': [ false, " Template to use for hooks configuration file.", 'string' ]
, 'hooks-bind': [ false, " IP address to use in configuration for hooks.", 'string' ]
, 'hooks-port': [ false, " Port to use in configuration for hooks.", 'string' ]
, 'hooks-webroot': [ false, " Webroot to use in configuration for hooks (e.g. empty dir).", 'string' ]
, 'hooks-pre-enable': [ false, " Hook to check the webserver configuration prior to enabling.", 'string' ]
, 'hooks-enable': [ false, " Hook to enable the webserver configuration.", 'string' ]
, 'hooks-pre-reload': [ false, " Hook to check the webserver configuration prior to reloading.", 'string' ]
, 'hooks-reload': [ false, " Hook to reload the webserver.", 'string' ]
, 'hooks-disable': [ false, " Hook to disable the webserver configuration.", 'string' ]
//, 'standalone-supported-challenges': [ false, " Supported challenges, order preferences are randomly chosen. (default: http-01,tls-sni-01)", 'string', 'http-01,tls-sni-01']
, debug: [ false, " show traces and logs", 'boolean', false ] , debug: [ false, " show traces and logs", 'boolean', false ]
, 'work-dir': [ false, "(ignored)", 'string', '~/letsencrypt/var/lib/' ] , 'root': [ false, " public_html / webroot path (may use the :hostname template such as /srv/www/:hostname)", 'string' ]
, 'logs-dir': [ false, "(ignored)", 'string', '~/letsencrypt/var/log/' ]
//
// backwards compat
//
, duplicate:
[ false, " Allow getting a certificate that duplicates an existing one/is an early renewal", 'boolean', false ]
, 'rsa-key-size':
[ false, " Size (in bits) of the RSA key.", 'int', 2048 ]
, server:
[ false, " alias of acme-url for certbot compatibility", 'string', '' ]
, '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' ]
, webroot: [ false, " for certbot compatibility", 'boolean', false ]
, 'webroot-path': [ false, "alias of '--root' for certbot compatibility", 'string' ]
//, 'standalone-supported-challenges': [ false, " Supported challenges, order preferences are randomly chosen. (default: http-01,tls-sni-01)", 'string', 'http-01,tls-sni-01']
, 'work-dir': [ false, "for certbot compatibility (ignored)", 'string', '~/letsencrypt/var/lib/' ]
, 'logs-dir': [ false, "for certbot compatibility (ignored)", 'string', '~/letsencrypt/var/log/' ]
}); });
// ignore certonly and extraneous arguments // ignore certonly and extraneous arguments
cli.main(function(_, options) { cli.main(function(_, options) {
console.log(''); console.log('');
var args = {}; var args = {};
var homedir = require('homedir')(); var homedir = require('os').homedir();
Object.keys(options).forEach(function (key) { Object.keys(options).forEach(function (key) {
var val = options[key]; var val = options[key];
@ -74,20 +92,15 @@ cli.main(function(_, options) {
args.domains = args.domains.split(','); args.domains = args.domains.split(',');
} }
if (!(Array.isArray(args.domains) && args.domains.length) || !args.email || !args.agreeTos) { if (!(Array.isArray(args.domains) && args.domains.length) || !args.email || !args.agreeTos || !args.acmeVersion || (!args.server && !args.acmeUrl)) {
console.error("\nUsage: greenlock certonly --standalone --domains example.com --email user@example.com --agree-tos"); console.error("\nUsage:\n\ngreenlock certonly --standalone \\");
console.error("\t--acme-version draft-11 --acme-url https://acme-staging-v02.api.letsencrypt.org/directory \\");
console.error("\t--agree-tos --email user@example.com --domains example.com \\");
console.error("\t--config-dir ~/acme/etc \\");
console.error("\nSee greenlock --help for more details\n"); console.error("\nSee greenlock --help for more details\n");
return; return;
} }
if (args.tlsSni01Port) {
// [@agnat]: Coerce to string. cli returns a number although we request a string.
args.tlsSni01Port = "" + args.tlsSni01Port;
args.tlsSni01Port = args.tlsSni01Port.split(',').map(function (port) {
return parseInt(port, 10);
});
}
if (args.http01Port) { if (args.http01Port) {
// [@agnat]: Coerce to string. cli returns a number although we request a string. // [@agnat]: Coerce to string. cli returns a number although we request a string.
args.http01Port = "" + args.http01Port; args.http01Port = "" + args.http01Port;

View File

@ -2,22 +2,21 @@
var DAY = 24 * 60 * 60 * 1000; var DAY = 24 * 60 * 60 * 1000;
var LE = require('greenlock'); var Greenlock = require('greenlock');
module.exports.run = function (args) { module.exports.run = function (args) {
var leChallenge; var leChallenge;
var leStore; var leStore;
var servers; var servers;
var USE_DNS = {}; var USE_DNS = {};
var challengeType; var challengeType;
args.acmeUrl = args.server = (args.acmeUrl || args.server);
args.root = args.webrootPath = (args.root || args.webrootPath);
if (args.dns01) { if (args.dns01) {
challengeType = 'dns-01'; challengeType = 'dns-01';
args.webrootPath = ''; args.webrootPath = '';
args.standalone = USE_DNS; args.standalone = USE_DNS;
} else if (args.tlsSni01Port || args.hooks) {
challengeType = 'tls-sni-01';
args.webrootPath = '';
} else /*if (args.http01Port)*/ { } else /*if (args.http01Port)*/ {
challengeType = 'http-01'; challengeType = 'http-01';
} }
@ -25,57 +24,43 @@ module.exports.run = function (args) {
if (args.manual) { if (args.manual) {
leChallenge = require('le-challenge-manual').create({}); leChallenge = require('le-challenge-manual').create({});
} }
else if (args.hooks) {
leChallenge = require('le-challenge-hooks').create({
hooksPath: args.hooksPath
, hooksServer: args.hooksServer
, hooksTemplate: args.hooksTemplate
, hooksBind: args.hooksBind
, hooksPort: args.hooksPort
, hooksWebroot: args.hooksWebroot
, hooksPreEnable: args.hooksPreEnable
, hooksEnable: args.hooksEnable
, hooksPreReload: args.hooksPreReload
, hooksReload: args.hooksReload
, hooksDisable: args.hooksDisable
});
}
else if (args.webrootPath) { else if (args.webrootPath) {
// webrootPath is all that really matters here // webrootPath is all that really matters here
// TODO rename le-challenge-fs to le-challenge-webroot // TODO rename le-challenge-fs to le-challenge-webroot
leChallenge = require('./lib/webroot').create({ webrootPath: args.webrootPath }); leChallenge = require('./lib/webroot').create({ webrootPath: args.webrootPath });
} }
else if (args.tlsSni01Port) {
leChallenge = require('le-challenge-sni').create({});
servers = require('./lib/servers').create(leChallenge);
}
else if (USE_DNS !== args.standalone) { else if (USE_DNS !== args.standalone) {
leChallenge = require('le-challenge-standalone').create({}); leChallenge = require('le-challenge-standalone').create({});
servers = require('./lib/servers').create(leChallenge); servers = require('./lib/servers').create(leChallenge);
} }
var privkeyPath = args.domainKeyPath || ':configDir/live/:hostname/privkey.pem'; //args.privkeyPath var privkeyPath = args.privkeyPath || args.domainKeyPath || ':configDir/live/:hostname/privkey.pem'; //args.privkeyPath
leStore = require('le-store-certbot').create({ leStore = require('le-store-certbot').create({
configDir: args.configDir configDir: args.configDir
, privkeyPath: privkeyPath , privkeyPath: privkeyPath
, fullchainPath: args.fullchainPath , fullchainPath: args.fullchainPath
, certPath: args.certPath , certPath: args.certPath
, chainPath: args.chainPath , chainPath: args.chainPath
, webrootPath: args.webrootPath , bundlePath: args.bundlePath
, webrootPath: args.root
, domainKeyPath: args.domainKeyPath , domainKeyPath: args.domainKeyPath
, accountKeyPath: args.accountKeyPath , accountKeyPath: args.accountKeyPath
}); });
if (!args.server) { if (!args.acmeUrl) {
throw new Error("You must specify a server to use with --server"); throw new Error("You must specify the ACME server url with --acme-url");
}
if (!args.acmeVersion) {
throw new Error("You must specify the ACME API version with --acme-version");
} }
// let LE know that we're handling standalone / webroot here // let Greenlock know that we're handling standalone / webroot here
var leChallenges = {}; var leChallenges = {};
leChallenges[challengeType] = leChallenge; leChallenges[challengeType] = leChallenge;
var le = LE.create({ var greenlock = Greenlock.create({
debug: args.debug debug: args.debug
, server: args.server , server: args.acmeUrl
, version: args.acmeVersion
, store: leStore , store: leStore
, challenges: leChallenges , challenges: leChallenges
, renewWithin: args.renewWithin * DAY , renewWithin: args.renewWithin * DAY
@ -86,7 +71,7 @@ module.exports.run = function (args) {
if (args.tlsSni01Port) { if (args.tlsSni01Port) {
servers.startServers( servers.startServers(
[], args.tlsSni01Port [], args.tlsSni01Port
, { debug: args.debug, httpsOptions: le.httpsOptions } , { debug: args.debug, tlsOptions: greenlock.tlsOptions }
); );
} }
else { else {
@ -98,10 +83,11 @@ module.exports.run = function (args) {
} }
// Note: can't use args directly as null values will overwrite template values // Note: can't use args directly as null values will overwrite template values
return le.register({ return greenlock.register({
debug: args.debug debug: args.debug
, email: args.email , email: args.email
, agreeTos: args.agreeTos , agreeTos: args.agreeTos
, communityMember: args.communityMember
, domains: args.domains , domains: args.domains
, rsaKeySize: args.rsaKeySize , rsaKeySize: args.rsaKeySize
, challengeType: challengeType , challengeType: challengeType
@ -134,10 +120,12 @@ module.exports.run = function (args) {
console.log('Certificates installed at:'); console.log('Certificates installed at:');
console.log( console.log(
[ [
// args.privkeyPath
args.certPath args.certPath
, args.chainPath , args.chainPath
, args.fullchainPath , args.fullchainPath
].join('\n') , args.bundlePath || ''
].join('\n').replace(/\n+/g, '\n')
.replace(/:configDir/g, args.configDir) .replace(/:configDir/g, args.configDir)
.replace(/:hostname/g, args.domains[0]) .replace(/:hostname/g, args.domains[0])
); );

128
installer/get.sh Normal file
View File

@ -0,0 +1,128 @@
#!/bin/bash
# This is a 3 step process
# 1. First we need to figure out whether to use wget or curl for fetching remote files
# 2. Next we need to figure out whether to use unzip or tar for downloading releases
# 3. We need to actually install the stuff
set -e
set -u
###############################
# #
# http_get #
# boilerplate for curl / wget #
# #
###############################
# See https://git.coolaj86.com/coolaj86/snippets/blob/master/bash/http-get.sh
_my_http_get=""
_my_http_opts=""
_my_http_out=""
detect_http_get()
{
set +e
if type -p curl >/dev/null 2>&1; then
_my_http_get="curl"
_my_http_opts="-fsSL"
_my_http_out="-o"
elif type -p wget >/dev/null 2>&1; then
_my_http_get="wget"
_my_http_opts="--quiet"
_my_http_out="-O"
else
echo "Aborted, could not find curl or wget"
return 7
fi
set -e
}
http_get()
{
$_my_http_get $_my_http_opts $_my_http_out "$2" "$1"
touch "$2"
}
http_bash()
{
_http_url=$1
my_args=${2:-}
rm -rf my-tmp-runner.sh
$_my_http_get $_my_http_opts $_my_http_out my-tmp-runner.sh "$_http_url"; bash my-tmp-runner.sh $my_args; rm my-tmp-runner.sh
}
detect_http_get
###############################
## END HTTP_GET ##
###############################
echo ""
echo ""
echo ""
if [ -z "${GREENLOCK_PATH:-}" ]; then
echo 'GREENLOCK_PATH="'${GREENLOCK_PATH:-}'"'
GREENLOCK_PATH=/opt/greenlock
fi
echo "Installing Greenlock to '$GREENLOCK_PATH'"
echo ""
echo "sudo mkdir -p '$GREENLOCK_PATH'"
sudo mkdir -p "$GREENLOCK_PATH"
echo "sudo chown -R $(whoami) '$GREENLOCK_PATH'"
sudo chown -R $(whoami) "$GREENLOCK_PATH"
# until node v10.x gets fix for ursa we have no advantage to switching from 8.x
export NODEJS_VER=v8.11.1
export NODE_PATH="$GREENLOCK_PATH/lib/node_modules"
export NPM_CONFIG_PREFIX="$GREENLOCK_PATH"
export PATH="$GREENLOCK_PATH/bin:$PATH"
sleep 1
http_bash https://git.coolaj86.com/coolaj86/node-installer.sh/raw/branch/master/install.sh --no-dev-deps
my_tree="master"
my_node="$GREENLOCK_PATH/bin/node"
my_npm="$my_node $GREENLOCK_PATH/bin/npm"
my_tmp="$GREENLOCK_PATH/tmp"
mkdir -p $my_tmp
echo "blah"
set +e
my_unzip=$(type -p unzip)
my_tar=$(type -p tar)
if [ -n "$my_unzip" ]; then
rm -f $my_tmp/greenlock-$my_tree.zip
http_get https://git.coolaj86.com/coolaj86/greenlock-cli.js/archive/$my_tree.zip $my_tmp/greenlock-$my_tree.zip
# -j is the same as --strip 1, it nixes the top-level directory
$my_unzip -j $my_tmp/greenlock-$my_tree.zip -d $GREENLOCK_PATH/
elif [ -n "$my_tar" ]; then
rm -f $my_tmp/greenlock-$my_tree.tar.gz
http_get https://git.coolaj86.com/coolaj86/greenlock-cli.js/archive/$my_tree.tar.gz $my_tmp/greenlock-$my_tree.tar.gz
ls -lah $my_tmp/greenlock-$my_tree.tar.gz
$my_tar -xzf $my_tmp/greenlock-$my_tree.tar.gz --strip 1 -C $GREENLOCK_PATH/
else
echo "Neither tar nor unzip found. Abort."
exit 13
fi
set -e
pushd $GREENLOCK_PATH
$my_npm install
popd
cat << EOF > $GREENLOCK_PATH/bin/greenlock
!#/bin/bash
$my_node $GREENLOCK_PATH/bin/greenlock.js
EOF
chmod a+x $GREENLOCK_PATH/bin/greenlock
ln -sf $GREENLOCK_PATH/bin/greenlock /usr/local/bin/greenlock
#sudo setcap cap_net_bind_service=+ep $GREENLOCK_PATH/bin/node
#https://git.coolaj86.com/coolaj86/greenlock-cli.js.git
#https://git.coolaj86.com/coolaj86/greenlock-cli.js/archive/:tree:.tar.gz
#https://git.coolaj86.com/coolaj86/greenlock-cli.js/archive/:tree:.zip

View File

@ -7,17 +7,22 @@ module.exports.create = function (challenge) {
_servers: [] _servers: []
, httpResponder: function (req, res) { , httpResponder: function (req, res) {
console.log('[LE-CLI] httpResponder'); console.info(req.method + ' ' + req.headers.host + req.url);
var acmeChallengePrefix = '/.well-known/acme-challenge/'; var acmeChallengePrefix = '/.well-known/acme-challenge/';
if (0 !== req.url.indexOf(acmeChallengePrefix)) { if (0 !== req.url.indexOf(acmeChallengePrefix)) {
res.end("Let's Encrypt! Command line tool"); res.end("Greenlock™ Commandline: https://git.coolaj86.com/coolaj86/greenlock-cli.js");
return; return;
} }
var token = req.url.slice(acmeChallengePrefix.length); var token = req.url.slice(acmeChallengePrefix.length);
challenge.get(NOBJ, req.headers.host.replace(/:.*/, ''), token, function (err, val) { challenge.get(NOBJ, req.headers.host.replace(/:.*/, ''), token, function (err, val) {
if (val) {
console.info("Responding with authorization token '" + val + "'");
} else {
console.info("No authorization token found");
}
res.end(val || '_ ERROR challenge not found _'); res.end(val || '_ ERROR challenge not found _');
}); });
} }
@ -25,7 +30,7 @@ module.exports.create = function (challenge) {
, startServers: function (plainPorts, tlsPorts, opts) { , startServers: function (plainPorts, tlsPorts, opts) {
opts = opts || {}; opts = opts || {};
var httpsOptions = opts.httpsOptions || require('localhost.daplie.me-certificates'); var tlsOptions = opts.tlsOptions || {};
var https = require('https'); var https = require('https');
var http = require('http'); var http = require('http');
@ -56,7 +61,7 @@ module.exports.create = function (challenge) {
// tls-sni-01-port // tls-sni-01-port
tlsPorts.forEach(function (port) { tlsPorts.forEach(function (port) {
var server = https.createServer(httpsOptions, servers.httpResponder); var server = https.createServer(tlsOptions, servers.httpResponder);
servers._servers.push(server); servers._servers.push(server);
server.listen(port, function () { server.listen(port, function () {

View File

@ -1,6 +1,6 @@
{ {
"name": "greenlock-cli", "name": "greenlock-cli",
"version": "2.2.13", "version": "2.3.0",
"description": "CLI for node-greenlock modeled after the official ACME client", "description": "CLI for node-greenlock modeled after the official ACME client",
"main": "index.js", "main": "index.js",
"bin": { "bin": {
@ -34,16 +34,11 @@
}, },
"homepage": "https://git.coolaj86.com/coolaj86/greenlock-cli.js", "homepage": "https://git.coolaj86.com/coolaj86/greenlock-cli.js",
"dependencies": { "dependencies": {
"cli": "^0.11.1", "cli": "^1.0.1",
"greenlock": "^2.1.16", "greenlock": "^2.2.15",
"homedir": "^0.6.0", "le-challenge-manual": "^2.1.0",
"le-acme-core": "^2.0.5", "le-challenge-standalone": "^2.1.0",
"le-challenge-hooks": "^2.0.0", "le-store-certbot": "^2.1.0",
"le-challenge-manual": "^2.0.0",
"le-challenge-sni": "^2.0.0",
"le-challenge-standalone": "^2.0.0",
"le-store-certbot": "^2.0.2",
"localhost.daplie.me-certificates": "^1.3.2",
"mkdirp": "^0.5.1" "mkdirp": "^0.5.1"
} }
} }