Compare commits

..

No commits in common. "b9c5a26047cece5172c98843aecccfcbb5ee498c" and "076528320b557aa93929ee70a6fd4cb98c2d720c" have entirely different histories.

6 changed files with 197 additions and 377 deletions

267
README.md
View File

@ -9,7 +9,6 @@ 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**
@ -27,202 +26,168 @@ 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 ## Install Node
=======
Mac & Linux For **Windows**:
-----------
Open Terminal and run this install script: Choose **Stable** from <https://nodejs.org/en/>
For Linux and **OS X**:
```
curl -L bit.ly/nodejs-min | bash
```
# Install Greenlock
```bash ```bash
curl -fsS https://get.greenlock.app/ | bash npm install -g greenlock-cli@2.x
``` ```
This will install greenlock to `/opt/greenlock` and put a symlink to ## Usage
`/opt/greenlock/bin/greenlock` in `/usr/local/bin/greenlock` for convenience.
You can customize the installation: These commands are shown using the **testing server**.
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
export NODEJS_VER=v8.11.1 greenlock certonly \
export GREENLOCK_PATH=/opt/greenlock --agree-tos --email john.doe@example.com \
curl -fsS https://get.greenlock.app/ | bash --standalone \
--domains example.com,www.example.com \
--server https://acme-staging.api.letsencrypt.org/directory \
--config-dir ~/letsencrypt/etc
``` ```
This will change which version of node.js is bundled with greenlock Then you can see your certs at `~/letsencrypt/etc/live`.
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**.
``` ```
--acme-version draft-11 --server https://acme-v02.api.letsencrypt.org/directory \ ls ~/letsencrypt/etc/live
``` ```
## Standalone This option is great for testing, but since it requires the use of
the same ports that your webserver needs, it isn't a good choice
for production.
<small>**primarily for testing**</small> ### WebRoot
You can run in standalone mode **on your server** and get a cert instantly. You can specify the path to where you keep your `index.html` with `webroot`, as
long as your server is serving plain HTTP on port 80.
Note: No other webserver may be running at the time (use Webroot mode for that). For example, if I want to get a domain for `example.com` and my `index.html` is
at `/srv/www/example.com`, then I would use this command:
```bash ```bash
sudo greenlock certonly --standalone \ sudo greenlock certonly \
--acme-version draft-11 --acme-url https://acme-staging-v02.api.letsencrypt.org/directory \ --agree-tos --email john.doe@example.com \
--agree-tos --email jon@example.com --domains example.com,www.example.com \ --webroot --webroot-path /srv/www/example.com \
--community-member \ --config-dir /etc/letsencrypt \
--config-dir ~/acme/etc --domains example.com,www.example.com \
--server https://acme-staging.api.letsencrypt.org/directory
``` ```
## WebRoot Note that we use `sudo` because in this example we are using `/etc/letsencrypt`
as the cert directory rather than `~/letsencrypt/etc`, which we used in the previous example.
<small>**for testing and production**</small> Then see your brand new shiny certs:
With this method you must use **your existing http (port 80) server** (Apache, Nginx, HAProxy, etc). ```
You will specify the **path or template path** to your `public_html` or `www` webroot. ls /etc/letsencrypt/live/
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
``` ```
Now let's say that You can use a cron job to run the script above every 80 days (the certificates expire after 90 days)
so that you always have fresh certificates.
* I have many sites in `/srv/www/`, all by their name ### Interactive (for debugging)
* 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 --manual \ sudo greenlock certonly \
--acme-version draft-11 --acme-url https://acme-staging-v02.api.letsencrypt.org/directory \ --agree-tos --email john.doe@example.com \
--agree-tos --email jon@example.com --domains example.com \ --manual
--community-member \ --config-dir /etc/letsencrypt \
--config-dir ~/acme/etc --domains example.com,www.example.com \
--server https://acme-staging.api.letsencrypt.org/directory
``` ```
Certificate Locations ## Test with a free domain
=====================
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
sudo setcap cap_net_bind_service=+ep /opt/greenlock/bin/node # Install Daplie DNS
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'
``` ```
To allow greenlock to write to folders owned by another user, set it to run as that user. Example domain:
Otherwise, you can change the permissions on the folders, which is ```
**probably a BAD IDEA**. Probabry a **security risk**. rubber-duck-42.daplie.me
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/ssl /etc/acme sudo chown -R $(whoami) /etc/letsencrypt /var/lib/letsencrypt /var/log/letsencrypt
``` ```
Command Line Options ## Command Line Options
====================
``` ```
Usage: Usage:
greenlock [OPTIONS] [ARGS] greenlock [OPTIONS] [ARGS]
Options: Options:
--acme-version [STRING] 'draft-11' for Let's Encrypt v2 or 'v01' for Let's Encrypt v1. (default: null) --server [STRING] ACME Directory Resource URI. (Default is https://acme-v01.api.letsencrypt.org/directory))
--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
--community-member Submit stats to and receive updates from Greenlock --domains URL Domain names to apply. For multiple domains you can enter a comma
--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: 10) --renew-within [NUMBER] Renew certificates this many days before expiry. (default: 7)
--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)
@ -233,9 +198,6 @@ 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)
@ -252,26 +214,13 @@ 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,67 +5,49 @@ var cli = require('cli');
var mkdirp = require('mkdirp'); var mkdirp = require('mkdirp');
cli.parse({ cli.parse({
'acme-version': server: [ false, " ACME Directory Resource URI.", 'string', '' ]
[ false, " ACME / Let's Encrypt version. v01 or draft-11 (aka v02)", 'string', 'draft-11' ] , email: [ false, " Email used for registration and recovery contact. (default: null)", 'email' ]
, '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 ]
, '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' ]
, 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 ]
, 'cert-path': , duplicate: [ false, " Allow getting a certificate that duplicates an existing one/is an early renewal", 'boolean', false ]
[ false, " Path to where new cert.pem is saved", 'string' , 'rsa-key-size': [ false, " Size (in bits) of the RSA key.", 'int', 2048 ]
, ':configDir/live/:hostname/cert.pem' ] , 'cert-path': [ false, " Path to where new cert.pem is saved", 'string',':configDir/live/:hostname/cert.pem' ]
, 'fullchain-path': , 'fullchain-path': [ false, " Path to where new fullchain.pem (cert + chain) is saved", 'string', ':configDir/live/:hostname/fullchain.pem' ]
[ false, " Path to where new fullchain.pem (cert + chain) is saved", 'string' , 'chain-path': [ false, " Path to where new chain.pem is saved", 'string', ':configDir/live/:hostname/chain.pem' ]
, ':configDir/live/:hostname/fullchain.pem' ] , 'domain-key-path': [ false, " Path to privkey.pem to use for domain (default: generate new)", 'string' ]
, 'bundle-path': , 'account-key-path': [ false, " Path to privkey.pem to use for account (default: generate new)", 'string' ]
[ false, " Path to where new bundle.pem (fullchain + privkey) is saved", 'string' , 'config-dir': [ false, " Configuration directory.", 'string', '~/letsencrypt/etc/' ]
, ':configDir/live/:hostname/bundle.pem' ] , 'tls-sni-01-port': [ false, " Use TLS-SNI-01 challenge type with this port (only port 443 is valid with most production servers)", 'int' ]
, '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 ]
, debug: [ false, " show traces and logs", 'boolean', false ] , webroot: [ false, " Obtain certs by placing files in a webroot directory.", 'boolean', false ]
, 'root': [ false, " public_html / webroot path (may use the :hostname template such as /srv/www/:hostname)", 'string' ] , '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' ]
// backwards compat , 'hooks-server': [ false, " Type of webserver to configure.", 'string' ]
// , 'hooks-template': [ false, " Template to use for hooks configuration file.", 'string' ]
, duplicate: , 'hooks-bind': [ false, " IP address to use in configuration for hooks.", 'string' ]
[ false, " Allow getting a certificate that duplicates an existing one/is an early renewal", 'boolean', false ] , 'hooks-port': [ false, " Port to use in configuration for hooks.", 'string' ]
, 'rsa-key-size': , 'hooks-webroot': [ false, " Webroot to use in configuration for hooks (e.g. empty dir).", 'string' ]
[ false, " Size (in bits) of the RSA key.", 'int', 2048 ] , 'hooks-pre-enable': [ false, " Hook to check the webserver configuration prior to enabling.", 'string' ]
, server: , 'hooks-enable': [ false, " Hook to enable the webserver configuration.", 'string' ]
[ false, " alias of acme-url for certbot compatibility", 'string', '' ] , 'hooks-pre-reload': [ false, " Hook to check the webserver configuration prior to reloading.", 'string' ]
, 'domain-key-path': , 'hooks-reload': [ false, " Hook to reload the webserver.", 'string' ]
[ false, " Path to privkey.pem to use for domain (default: generate new)", 'string' ] , 'hooks-disable': [ false, " Hook to disable the webserver configuration.", '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'] //, '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/' ] , debug: [ false, " show traces and logs", 'boolean', false ]
, 'logs-dir': [ false, "for certbot compatibility (ignored)", 'string', '~/letsencrypt/var/log/' ] , 'work-dir': [ false, "(ignored)", 'string', '~/letsencrypt/var/lib/' ]
, 'logs-dir': [ false, "(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('os').homedir(); var homedir = require('homedir')();
Object.keys(options).forEach(function (key) { Object.keys(options).forEach(function (key) {
var val = options[key]; var val = options[key];
@ -92,15 +74,20 @@ 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 || !args.acmeVersion || (!args.server && !args.acmeUrl)) { if (!(Array.isArray(args.domains) && args.domains.length) || !args.email || !args.agreeTos) {
console.error("\nUsage:\n\ngreenlock certonly --standalone \\"); console.error("\nUsage: greenlock certonly --standalone --domains example.com --email user@example.com --agree-tos");
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,21 +2,22 @@
var DAY = 24 * 60 * 60 * 1000; var DAY = 24 * 60 * 60 * 1000;
var Greenlock = require('greenlock'); var LE = 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;
args.acmeUrl = args.server = (args.acmeUrl || args.server); var challengeType;
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';
} }
@ -24,43 +25,57 @@ 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.privkeyPath || args.domainKeyPath || ':configDir/live/:hostname/privkey.pem'; //args.privkeyPath var 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
, bundlePath: args.bundlePath , webrootPath: args.webrootPath
, webrootPath: args.root
, domainKeyPath: args.domainKeyPath , domainKeyPath: args.domainKeyPath
, accountKeyPath: args.accountKeyPath , accountKeyPath: args.accountKeyPath
}); });
if (!args.acmeUrl) { if (!args.server) {
throw new Error("You must specify the ACME server url with --acme-url"); throw new Error("You must specify a server to use with --server");
}
if (!args.acmeVersion) {
throw new Error("You must specify the ACME API version with --acme-version");
} }
// let Greenlock know that we're handling standalone / webroot here // let LE know that we're handling standalone / webroot here
var leChallenges = {}; var leChallenges = {};
leChallenges[challengeType] = leChallenge; leChallenges[challengeType] = leChallenge;
var greenlock = Greenlock.create({ var le = LE.create({
debug: args.debug debug: args.debug
, server: args.acmeUrl , server: args.server
, version: args.acmeVersion
, store: leStore , store: leStore
, challenges: leChallenges , challenges: leChallenges
, renewWithin: args.renewWithin * DAY , renewWithin: args.renewWithin * DAY
@ -71,7 +86,7 @@ module.exports.run = function (args) {
if (args.tlsSni01Port) { if (args.tlsSni01Port) {
servers.startServers( servers.startServers(
[], args.tlsSni01Port [], args.tlsSni01Port
, { debug: args.debug, tlsOptions: greenlock.tlsOptions } , { debug: args.debug, httpsOptions: le.httpsOptions }
); );
} }
else { else {
@ -83,11 +98,10 @@ 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 greenlock.register({ return le.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
@ -120,12 +134,10 @@ 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
, args.bundlePath || '' ].join('\n')
].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])
); );

View File

@ -1,128 +0,0 @@
#!/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,22 +7,17 @@ module.exports.create = function (challenge) {
_servers: [] _servers: []
, httpResponder: function (req, res) { , httpResponder: function (req, res) {
console.info(req.method + ' ' + req.headers.host + req.url); console.log('[LE-CLI] httpResponder');
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("Greenlock™ Commandline: https://git.coolaj86.com/coolaj86/greenlock-cli.js"); res.end("Let's Encrypt! Command line tool");
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 _');
}); });
} }
@ -30,7 +25,7 @@ module.exports.create = function (challenge) {
, startServers: function (plainPorts, tlsPorts, opts) { , startServers: function (plainPorts, tlsPorts, opts) {
opts = opts || {}; opts = opts || {};
var tlsOptions = opts.tlsOptions || {}; var httpsOptions = opts.httpsOptions || require('localhost.daplie.me-certificates');
var https = require('https'); var https = require('https');
var http = require('http'); var http = require('http');
@ -61,7 +56,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(tlsOptions, servers.httpResponder); var server = https.createServer(httpsOptions, 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.3.0", "version": "2.2.13",
"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,11 +34,16 @@
}, },
"homepage": "https://git.coolaj86.com/coolaj86/greenlock-cli.js", "homepage": "https://git.coolaj86.com/coolaj86/greenlock-cli.js",
"dependencies": { "dependencies": {
"cli": "^1.0.1", "cli": "^0.11.1",
"greenlock": "^2.2.15", "greenlock": "^2.1.16",
"le-challenge-manual": "^2.1.0", "homedir": "^0.6.0",
"le-challenge-standalone": "^2.1.0", "le-acme-core": "^2.0.5",
"le-store-certbot": "^2.1.0", "le-challenge-hooks": "^2.0.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"
} }
} }