Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
AJ ONeal | 6a770303f0 | |
AJ ONeal | 8cf13b329a | |
AJ ONeal | c30e25e25b | |
AJ ONeal | 4e447ec9cd | |
AJ ONeal | 216384e096 | |
AJ ONeal | 0217bae134 | |
AJ ONeal | 5da8c6aa2d | |
AJ ONeal | 6a0f25c685 |
347
README.md
347
README.md
|
@ -1,44 +1,216 @@
|
|||
# greenlock-cli (letsencrypt-cli for node.js)
|
||||
![Greenlock Logo](https://git.coolaj86.com/coolaj86/greenlock.js/raw/branch/master/logo/greenlock-1063x250.png "Greenlock Logo")
|
||||
|
||||
| [greenlock (library)](https://git.coolaj86.com/coolaj86/greenlock.js)
|
||||
| **greenlock-cli**
|
||||
| [greenlock-express](https://git.coolaj86.com/coolaj86/greenlock-express.js)
|
||||
| [greenlock-koa](https://git.coolaj86.com/coolaj86/greenlock-koa.js)
|
||||
| [greenlock-hapi](https://git.coolaj86.com/coolaj86/greenlock-hapi.js)
|
||||
|
|
||||
# Greenlock™ Certificate Manager for Web Servers
|
||||
|
||||
CLI for node-greenlock modeled after the official client.
|
||||
A server-friendly commandline tool for Free SSL, Free Wildcard SSL, and Fully Automated HTTPS
|
||||
<small>certificates issued by Let's Encrypt v2 via ACME</small>
|
||||
|
||||
* Free SSL Certificates
|
||||
* 90-day certificate lifetime
|
||||
* One-off standalone registration / renewal
|
||||
* On-the-fly registration / renewal via webroot
|
||||
Greenlock is also available
|
||||
[for Browsers](https://git.coolaj86.com/coolaj86/greenlock.html),
|
||||
[for node.js](https://git.coolaj86.com/coolaj86/greenlock-express.js),
|
||||
and [for API integrations](https://git.coolaj86.com/coolaj86/greenlock.js)
|
||||
|
||||
## Install Node
|
||||
Why use Greenlock? Two Reasons:
|
||||
===============================
|
||||
|
||||
For **Windows**:
|
||||
One
|
||||
---
|
||||
|
||||
Choose **Stable** from <https://nodejs.org/en/>
|
||||
|
||||
For Linux and **OS X**:
|
||||
|
||||
```
|
||||
curl -L bit.ly/nodejs-min | bash
|
||||
```
|
||||
|
||||
# Install Greenlock
|
||||
You want to be able to run a command like this:
|
||||
|
||||
```bash
|
||||
npm install -g greenlock-cli@2.x
|
||||
sudo greenlock --domains example.com --config /etc/greenlock/greenlock.yml
|
||||
```
|
||||
|
||||
## Usage
|
||||
And then get awesome results like this:
|
||||
|
||||
```
|
||||
/etc/ssl/acme
|
||||
├── accounts
|
||||
│ └── acme-staging-v02.api.letsencrypt.org/directory
|
||||
│ └── c07a31a70c691d64f6b4d31f51a6dd9c
|
||||
│ ├── meta.json
|
||||
│ ├── private_key.json
|
||||
│ └── regr.json
|
||||
└── live
|
||||
└── example.com <-- Free SSL like magic! Wow!
|
||||
├── bundle.pem
|
||||
├── cert.pem
|
||||
├── chain.pem
|
||||
├── fullchain.pem
|
||||
└── privkey.pem
|
||||
```
|
||||
|
||||
That you use with your existing webserver - Apache, Nginx, HAProxy, node.js, etc
|
||||
|
||||
And install to renew so that you never worry about ssl again.
|
||||
|
||||
Two
|
||||
---
|
||||
|
||||
You want to be able to run a command like this:
|
||||
|
||||
```bash
|
||||
sudo greenlock --install systemd --config /etc/greenlock.yml --webroot '/srv/www/:hostname'
|
||||
```
|
||||
|
||||
To immediately secure and publish any and all sites you have in a web root like this:
|
||||
|
||||
```
|
||||
/srv/www/
|
||||
├── coolsite.rocks
|
||||
├── example.com
|
||||
└── whatever.app
|
||||
```
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
- [x] Works with Windows, Mac, and Linux
|
||||
- [x] Works with Apache, Nginx, node.js, HAProxy, etc
|
||||
- [x] Great for VPS services - AWS, Digital Ocean, Vultr, etc
|
||||
- [x] Great for Tiny Computers - Raspberry Pi, etc
|
||||
- [x] Automatic HTTPS
|
||||
- [x] Free SSL
|
||||
- [x] Free Wildcard SSL
|
||||
- [x] Multiple domain support (up to 100 altnames per SAN)
|
||||
- [x] Virtual Hosting (vhost)
|
||||
- [x] Automatical renewal (10 to 14 days before expiration)
|
||||
- [x] Let's Encrypt v2 ACME API
|
||||
- [x] Extensible via Plugins
|
||||
- [x] HTTP Challenge Plugins - AWS S3, Azure, Consul, etcd
|
||||
- [x] DNS Challenge Plugins - AWS Route53, CloudFlare, Digital Ocean
|
||||
- [x] Account & Certificate Storage Plugins - AWS S3, Redis
|
||||
- [x] Built-in WebServer
|
||||
|
||||
Install
|
||||
=======
|
||||
|
||||
Mac
|
||||
---
|
||||
|
||||
Open Terminal and run this install script:
|
||||
|
||||
```bash
|
||||
curl -fsS https://get.greenlock.app/ | bash
|
||||
```
|
||||
|
||||
Linux
|
||||
-----
|
||||
|
||||
Open Terminal and run this install script:
|
||||
|
||||
```bash
|
||||
curl -fsS https://get.greenlock.app/ | bash
|
||||
```
|
||||
|
||||
Windows & Node.js
|
||||
-----------------
|
||||
|
||||
1. Install [node.js](https://nodejs.org)
|
||||
2. Open _Node.js_
|
||||
2. Run the command `npm install -g greenlock-cli`
|
||||
|
||||
Important: How to Not Get Blocked
|
||||
===================
|
||||
|
||||
PLEASE READ ALL THREE SENTENCES:
|
||||
|
||||
* These examples use the PRODUCTION ENVIRONMENT (where you can be blocked)
|
||||
* If an example DOESN'T WORK on the first try, STOP!
|
||||
* UNCOMMENT the `--staging` flag and see the TROUBLESHOOTING SECTION
|
||||
|
||||
Quick Examples
|
||||
==============
|
||||
|
||||
The most basic options are exposed as commandline flags,
|
||||
just so that we can do little domes like this.
|
||||
|
||||
The config file is explained after the troubleshooting section.
|
||||
|
||||
### The Greenlock HTTPS WebServer
|
||||
|
||||
Easy to run on your server, nothing else required:
|
||||
|
||||
```bash
|
||||
sudo greenlock --webserver \
|
||||
--agree-tos --email jon@example.com \
|
||||
--domains example.com,www.example.com \
|
||||
--webroot /srv/www/example.com \
|
||||
--config-dir ~/acme/etc #--staging
|
||||
```
|
||||
|
||||
### Add SSL to an Existing WebServer
|
||||
|
||||
For all the Apache, Nginx, and HAProxy fans out there:
|
||||
|
||||
(use your existing webroot)
|
||||
|
||||
```bash
|
||||
sudo greenlock --agree-tos --email jon@example.com \
|
||||
--domains example.com,www.example.com \
|
||||
--webroot /srv/www/example.com \
|
||||
--privkey-path /etc/ssl/example.com/privkey.pem \
|
||||
--fullchain-path /etc/ssl/example.com/fullchain.pem \
|
||||
--bundle-path /etc/ssl/example.com/bundle.pem \
|
||||
--config-dir /etc/ssl/acme #--staging
|
||||
```
|
||||
|
||||
### Get SSL Certificates Interactively
|
||||
|
||||
Run this manual process on your laptop and copy the certificates
|
||||
to you server afterwards:
|
||||
|
||||
```bash
|
||||
greenlock --agree-tos --email jon@example.com \
|
||||
--domains example.com,www.example.com \
|
||||
--privkey-path /etc/ssl/example.com/privkey.pem \
|
||||
--fullchain-path /etc/ssl/example.com/fullchain.pem \
|
||||
--bundle-path /etc/ssl/example.com/bundle.pem \
|
||||
--manual \
|
||||
--config-dir /etc/ssl/acme #--staging
|
||||
```
|
||||
|
||||
### Standalone SSL Certificate Retrieval
|
||||
|
||||
Run this on a server standalone just to retrieve
|
||||
certificates:
|
||||
|
||||
```bash
|
||||
sudo greenlock --agree-tos --email jon@example.com \
|
||||
--domains example.com,www.example.com \
|
||||
--privkey-path /etc/ssl/example.com/privkey.pem \
|
||||
--fullchain-path /etc/ssl/example.com/fullchain.pem \
|
||||
--bundle-path /etc/ssl/example.com/bundle.pem \
|
||||
--standalone \
|
||||
--config-dir ~/etc/ssl/acme #--staging
|
||||
```
|
||||
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
Watch the [Troubleshooting Screencast](https://youtu.be/e8vaR4CEZ5s?t=397)
|
||||
|
||||
**Note**: Replace `whatever.com` with your domain, use your real email, etc.
|
||||
|
||||
0. Use the `--staging` flag while troubleshooting
|
||||
1. Do you have a valid A record for `whatever.com`?
|
||||
2. When you `ping whatever.com` do you see that same address?
|
||||
3. Can you confirm that's your server's address with `ifconfig` or `ipconfig`?
|
||||
4. Do you have write access to all of the directories you've specified?
|
||||
|
||||
**Important**: Don't forget to delete the directory specified in `--config-dir`
|
||||
when you get things figured out and remove `--staging`.
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
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`
|
||||
1. change server to `--server https://acme-v02.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.
|
||||
|
@ -54,8 +226,9 @@ greenlock certonly \
|
|||
--agree-tos --email john.doe@example.com \
|
||||
--standalone \
|
||||
--domains example.com,www.example.com \
|
||||
--server https://acme-staging.api.letsencrypt.org/directory \
|
||||
--config-dir ~/letsencrypt/etc
|
||||
--server https://acme-staging-v02.api.letsencrypt.org/directory \
|
||||
--acme-version draft-11
|
||||
--config-dir ~/acme/etc
|
||||
```
|
||||
|
||||
or
|
||||
|
@ -65,8 +238,9 @@ greenlock certonly \
|
|||
--agree-tos --email john.doe@example.com \
|
||||
--standalone --tls-sni-01-port 443 \
|
||||
--domains example.com,www.example.com \
|
||||
--server https://acme-staging.api.letsencrypt.org/directory \
|
||||
--config-dir ~/letsencrypt/etc
|
||||
--server https://acme-staging-v02.api.letsencrypt.org/directory \
|
||||
--acme-version draft-11
|
||||
--config-dir ~/acme/etc
|
||||
```
|
||||
|
||||
Then you can see your certs at `~/letsencrypt/etc/live`.
|
||||
|
@ -79,7 +253,7 @@ 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.
|
||||
|
||||
### WebRoot (production option 1)
|
||||
### WebRoot
|
||||
|
||||
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.
|
||||
|
@ -93,7 +267,8 @@ sudo greenlock certonly \
|
|||
--webroot --webroot-path /srv/www/example.com \
|
||||
--config-dir /etc/letsencrypt \
|
||||
--domains example.com,www.example.com \
|
||||
--server https://acme-staging.api.letsencrypt.org/directory
|
||||
--server https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
--acme-version draft-11
|
||||
```
|
||||
|
||||
Note that we use `sudo` because in this example we are using `/etc/letsencrypt`
|
||||
|
@ -108,70 +283,6 @@ ls /etc/letsencrypt/live/
|
|||
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.
|
||||
|
||||
### Hooks (production option 2)
|
||||
|
||||
You can also integrate with a secure server. This is more complicated than the
|
||||
webroot option, but it allows you to obtain certificates with only port 443
|
||||
open. This facility can work with any web server as long as it supports server
|
||||
name indication (SNI) and you can provide a configuration file template and
|
||||
shell hooks to install and uninstall the configuration (without downtime). In
|
||||
fact, it doesn't even need to be a webserver (though it must run on port 443);
|
||||
it could be another server that performs SSL/TLS negotiation with SNI.
|
||||
|
||||
The process works something like this. You would run:
|
||||
|
||||
```bash
|
||||
sudo greenlock certonly \
|
||||
--agree-tos --email john.doe@example.com \
|
||||
--hooks --hooks-server apache2-debian \
|
||||
--config-dir /etc/letsencrypt \
|
||||
--domains example.com,www.example.com \
|
||||
--server https://acme-staging.api.letsencrypt.org/directory
|
||||
```
|
||||
|
||||
Three files are then generated:
|
||||
|
||||
* a configuration fragment: `some-long-string.conf`
|
||||
* a challenge-fulfilling certificate: `the-same-long-string.crt`
|
||||
* a private key: `the-same-long-string.key`
|
||||
|
||||
A hook is then run to enable the fragment, e.g. by linking it (it should not be
|
||||
moved) into a `conf.d` directory (for Apache on Debian, `sites-enabled`). A
|
||||
second hook is then run to check the configuration is valid, to avoid
|
||||
accidental downtime, and then another to signal to the server to reload the
|
||||
configuration. The server will now serve the generated certificate on a special
|
||||
domain to prove you own the domain you're getting a certificate for.
|
||||
|
||||
After the domain has been validated externally, hooks are run to disable the
|
||||
configuration fragment, and again check and reload the configuration.
|
||||
|
||||
You can then find your brand new certs in:
|
||||
|
||||
```
|
||||
ls /etc/letsencrypt/live/
|
||||
```
|
||||
|
||||
Tailor to your server and distro using the `--hooks-server` option. So far, the
|
||||
following are supported (contributions for additional servers welcome):
|
||||
|
||||
* apache2-debian
|
||||
|
||||
To tweak it for your setup and taste, see all the `hooks-` options in the
|
||||
Command Line Options section below. Also note that the following substitutions
|
||||
are available for use in the hooks and the template:
|
||||
|
||||
* `{{{token}}}`: the token
|
||||
* `{{{domain}}}`: the domain for which a certificate is being sought (beware of
|
||||
this if using multiple domains per certificate)
|
||||
* `{{{subject}}}`: the domain for which the generated challenge-fulfilling
|
||||
certificate must be used (only available when generating it)
|
||||
* `{{{cert}}}`: the path to the generated certificate: `hooks-path/token.crt`
|
||||
* `{{{privkey}}}`: the path to the generated private key: `hooks-path/token.key`
|
||||
* `{{{conf}}}`: the path to the generated config file: `hooks-path/token.conf`
|
||||
* `{{{bind}}}`: the value of the `hooks-bind` option
|
||||
* `{{{port}}}`: the value of the `hooks-port` option
|
||||
* `{{{webroot}}}`: the value of the `hooks-webroot` option
|
||||
|
||||
### Interactive (for debugging)
|
||||
|
||||
The token (for all challenge types) and keyAuthorization (only for https-01)
|
||||
|
@ -182,9 +293,10 @@ will be printed to the screen and you will be given time to copy it wherever
|
|||
sudo greenlock certonly \
|
||||
--agree-tos --email john.doe@example.com \
|
||||
--manual
|
||||
--config-dir /etc/letsencrypt \
|
||||
--config-dir /etc/acme \
|
||||
--domains example.com,www.example.com \
|
||||
--server https://acme-staging.api.letsencrypt.org/directory
|
||||
--server https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
--acme-version draft-11
|
||||
```
|
||||
|
||||
## Test with a free domain
|
||||
|
@ -248,11 +360,6 @@ Options:
|
|||
|
||||
--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
|
||||
(Default is :conf/live/:hostname/cert.pem)
|
||||
|
||||
|
@ -264,13 +371,8 @@ Options:
|
|||
|
||||
--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)
|
||||
|
||||
--config-dir STRING Configuration directory. (Default is ~/letsencrypt/etc/)
|
||||
|
||||
--tls-sni-01-port NUMBER Use TLS-SNI-01 challenge type with this port.
|
||||
(must be 443 with most production servers) (Boulder allows 5001 in testing mode)
|
||||
|
||||
--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)
|
||||
|
||||
|
@ -285,35 +387,6 @@ Options:
|
|||
|
||||
--webroot-path STRING public_html / webroot path.
|
||||
|
||||
--hooks BOOLEAN Obtain certs with hooks that configure a webserver to meet TLS-SNI-01 challenges.
|
||||
|
||||
--hooks-path STRING Path in which to store files for hooks.
|
||||
(Default is ~/letsencrypt/apache)
|
||||
|
||||
--hooks-server STRING Type of webserver to configure. Sets defaults for all the following --hooks- options.
|
||||
Either --hooks-server or --hooks-template must be given.
|
||||
(See the Hooks section above for a list of supported servers.)
|
||||
|
||||
--hooks-template STRING Template to use for hooks configuration file.
|
||||
Either --hooks-server or --hooks-template must be given.
|
||||
|
||||
--hooks-bind STRING IP address to use in configuration for hooks. (Default is *)
|
||||
|
||||
--hooks-port STRING Port to use in configuration for hooks. (Default is 443)
|
||||
|
||||
--hooks-webroot STRING Webroot to use in configuration for hooks (e.g. empty dir).
|
||||
Nothing should actually be served from here. (Default is /var/www)
|
||||
|
||||
--hooks-pre-enable STRING Hook to check the webserver configuration prior to enabling.
|
||||
|
||||
--hooks-enable STRING Hook to enable the webserver configuration.
|
||||
|
||||
--hooks-pre-reload STRING Hook to check the webserver configuration prior to reloading.
|
||||
|
||||
--hooks-reload STRING Hook to reload the webserver.
|
||||
|
||||
--hooks-disable STRING Hook to disable the webserver configuration.
|
||||
|
||||
--debug BOOLEAN show traces and logs
|
||||
|
||||
-h, --help Display help and usage details
|
||||
|
|
101
bin/greenlock.js
101
bin/greenlock.js
|
@ -5,49 +5,76 @@ var cli = require('cli');
|
|||
var mkdirp = require('mkdirp');
|
||||
|
||||
cli.parse({
|
||||
server: [ false, " ACME Directory Resource URI.", '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 ]
|
||||
, 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 ]
|
||||
, 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 ]
|
||||
, '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/' ]
|
||||
, 'tls-sni-01-port': [ false, " Use TLS-SNI-01 challenge type with this port (only port 443 is valid with most production servers)", '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 ]
|
||||
, 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 ]
|
||||
, 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 ]
|
||||
, 'work-dir': [ false, "(ignored)", 'string', '~/letsencrypt/var/lib/' ]
|
||||
, 'logs-dir': [ false, "(ignored)", 'string', '~/letsencrypt/var/log/' ]
|
||||
'acme-version':
|
||||
[ false, " v01 (Let's Encrypt v01) or draft-11 (Let's Encrypt v02) (default: draft-11)", 'string'
|
||||
, 'draft-11' ]
|
||||
, 'acme-url':
|
||||
[ false, " ACME API Directory URL (default: https://acme-v02.api.letsencrypt.org/directory", 'string'
|
||||
, 'https://acme-staging-v02.api.letsencrypt.org/directory' ]
|
||||
|
||||
, 'aol-keyword-www':
|
||||
[ false, " Travel back in time to 1995 where we redirect bare domains as to have a triple-w prefix", 'string'
|
||||
, false ]
|
||||
, config:
|
||||
[ 'c', " Path to configuration file --config /etc/greenlock/greenlock.yml (default: '')", 'string' ]
|
||||
, serve:
|
||||
[ false, " Run as webserver (default: false)", 'boolean'
|
||||
, false ]
|
||||
, email:
|
||||
[ false, " Email used for registration and recovery contact (default: '')", 'email' ]
|
||||
, analytics:
|
||||
[ false, " Share analytics with greenlock (default: false)", 'boolean'
|
||||
, false ]
|
||||
, community:
|
||||
[ false, " Join the greenlock community to get important updates (default: false)", 'boolean'
|
||||
, false ]
|
||||
, 'agree-tos':
|
||||
[ false, " Agree to the Let's Encrypt Subscriber Agreement", 'boolean'
|
||||
, false ]
|
||||
, domains:
|
||||
[ false, " Comma-separated list of domains to secure (default: [])", 'string' ]
|
||||
, 'config-dir':
|
||||
[ false, " Configuration directory.", 'string'
|
||||
, '~/acme/etc/' ]
|
||||
, 'cert-path':
|
||||
[ false, " Path where new cert.pem is saved", 'string'
|
||||
, ':configDir/live/:hostname/cert.pem' ]
|
||||
, 'fullchain-path':
|
||||
[ false, " Path where new fullchain.pem (cert + chain) is saved", 'string'
|
||||
, ':configDir/live/:hostname/fullchain.pem' ]
|
||||
, 'chain-path':
|
||||
[ false, " Path where new chain.pem is saved", 'string'
|
||||
, ':configDir/live/:hostname/chain.pem' ]
|
||||
, 'bundle-path':
|
||||
[ false, " Path where new bundle.pem (fullchain + privkey) is saved", 'string'
|
||||
, ':configDir/live/:hostname/bundle.pem' ]
|
||||
, 'privkey-path':
|
||||
[ false, " Path where (new or existing) domain privkey.pem is saved", 'string'
|
||||
, ':configDir/live/:hostname/privkey.pem' ]
|
||||
, 'webroot':
|
||||
[ false, " public_html / webroot path such as /srv/www/:hostname", 'string' ]
|
||||
, 'renew-within':
|
||||
[ false, " Renew certificates this many days before expiry", 'int'
|
||||
, 11 ]
|
||||
, staging:
|
||||
[ false, " Use Let's Encrypt v02 staging API", '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 ]
|
||||
, debug:
|
||||
[ false, " show traces and logs", 'boolean'
|
||||
, false ]
|
||||
});
|
||||
|
||||
// ignore certonly and extraneous arguments
|
||||
cli.main(function(_, options) {
|
||||
console.log('');
|
||||
var args = {};
|
||||
var homedir = require('homedir')();
|
||||
var homedir = require('os').homedir();
|
||||
|
||||
Object.keys(options).forEach(function (key) {
|
||||
var val = options[key];
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
daemon: true
|
||||
http_port: 80
|
||||
https_port: 443
|
||||
version: draft-11
|
||||
server: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
webroot_path: /srv/www
|
||||
store:
|
||||
module: 'le-store-certbot'
|
||||
options:
|
||||
config_dir: /etc/greenlock/
|
||||
domains:
|
||||
'*':
|
||||
check:
|
||||
module: 'fs-exists'
|
||||
options:
|
||||
path: /srv/www/:hostname
|
||||
email: MY_EMAIL
|
||||
agree_tos: MY_AGREE
|
||||
analytics: true
|
||||
challenges:
|
||||
'https-01':
|
||||
module: 'le-challenge-fs'
|
||||
'example.com,www.example.com':
|
||||
path: /srv/www/example.com
|
||||
email: MY_EMAIL
|
||||
agree_tos: MY_AGREE
|
||||
analytics: true
|
||||
challenges:
|
||||
'dns-01':
|
||||
module: 'le-challenge-cloudflare'
|
||||
options:
|
||||
email: 'jon@example.com'
|
||||
key: 'xxxxxxxxxxxxxxxxx'
|
||||
'.example.com':
|
||||
check: null
|
||||
path: /srv/www/example.com
|
||||
email: MY_EMAIL
|
||||
agree_tos: MY_AGREE
|
||||
analytics: true
|
||||
challenges:
|
||||
'dns-01':
|
||||
module: 'le-challenge-digitalocean'
|
||||
options:
|
||||
email: 'jon@example.com'
|
||||
key: 'xxxxxxxxxxxxxxxxx'
|
|
@ -0,0 +1,22 @@
|
|||
daemon: true
|
||||
http_port: 80
|
||||
https_port: 443
|
||||
version: draft-11
|
||||
server: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
webroot_path: /srv/www
|
||||
store:
|
||||
module: 'le-store-certbot'
|
||||
options:
|
||||
config_dir: /etc/greenlock/
|
||||
domains:
|
||||
'*':
|
||||
email: MY_EMAIL
|
||||
agree_tos: MY_AGREE
|
||||
analytics: true
|
||||
checks:
|
||||
- module: 'greenlock-plugin-approve-fs'
|
||||
options:
|
||||
www: false
|
||||
path: /srv/www/:hostname
|
||||
challenges:
|
||||
module: 'le-challenge-fs'
|
|
@ -0,0 +1,69 @@
|
|||
[Unit]
|
||||
Description=MY_DESC
|
||||
Documentation=MY_DOCS
|
||||
After=network-online.target
|
||||
Wants=network-online.target systemd-networkd-wait-online.service
|
||||
|
||||
[Service]
|
||||
# Restart on crash (bad signal), and on 'clean' failure (error exit code)
|
||||
# Allow up to 3 restarts within 10 seconds
|
||||
# (it's unlikely that a user or properly-running script will do this)
|
||||
Restart=on-failure
|
||||
StartLimitInterval=10
|
||||
StartLimitBurst=3
|
||||
|
||||
# The v8 VM will output a "clean" exit for JavaScript errors.
|
||||
# If we knew we were never going to accidentally exit cleanly
|
||||
# we would use on-abnormal:
|
||||
; Restart=on-abnormal
|
||||
|
||||
# User and group the process will run as
|
||||
# (www-data is the de facto standard on most systems)
|
||||
User=MY_USER
|
||||
Group=MY_GROUP
|
||||
|
||||
# If we need to pass environment variables in the future
|
||||
Environment=NODE_PATH=MY_GREENLOCK_PATH/lib/node_modules NPM_CONFIG_PREFIX=MY_GREENLOCK_PATH
|
||||
|
||||
# Set a sane working directory, sane flags, and specify how to reload the config file
|
||||
WorkingDirectory=MY_GREENLOCK_PATH
|
||||
ExecStart=MY_GREENLOCK_PATH/bin/node MY_GREENLOCK_PATH/bin/greenlock --daemon --config MY_CONFIG_PATH
|
||||
ExecReload=/bin/kill -USR1 $MAINPID
|
||||
|
||||
# Limit the number of file descriptors and processes; see `man systemd.exec` for more limit settings.
|
||||
# Unmodified greenlock is not expected to use more than this.
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=64
|
||||
|
||||
# Use private /tmp and /var/tmp, which are discarded after greenlock stops.
|
||||
PrivateTmp=true
|
||||
# Use a minimal /dev
|
||||
PrivateDevices=true
|
||||
# Hide /home, /root, and /run/user. Nobody will steal your SSH-keys.
|
||||
ProtectHome=true
|
||||
# Make /usr, /boot, /etc and possibly some more folders read-only.
|
||||
ProtectSystem=full
|
||||
# … except TLS/SSL, ACME, and Let's Encrypt certificates
|
||||
# and /var/log/greenlock, because we want a place where logs can go.
|
||||
# This merely retains r/w access rights, it does not add any new. Must still be writable on the host!
|
||||
ReadWriteDirectories=MY_RW_DIRS
|
||||
# you may also want to add other directories such as /opt/greelock /etc/acme /etc/letsencrypt
|
||||
|
||||
# Note: in v231 and above ReadWritePaths has been renamed to ReadWriteDirectories
|
||||
; ReadWritePaths=/etc/greenlock /var/log/greenlock
|
||||
|
||||
# The following additional security directives only work with systemd v229 or later.
|
||||
# They further retrict privileges that can be gained.
|
||||
# Note that you may have to add capabilities required by any plugins in use.
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
NoNewPrivileges=true
|
||||
|
||||
# Caveat: Some plugins need additional capabilities.
|
||||
# For example "upload" needs CAP_LEASE
|
||||
; CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_LEASE
|
||||
; AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_LEASE
|
||||
; NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,8 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
node bin/letsencrypt certonly \
|
||||
--agree-tos --email 'john.doe@gmail.com' \
|
||||
--standalone \
|
||||
--domains example.com,www.example.com \
|
||||
--server https://acme-staging.api.letsencrypt.org/directory \
|
||||
--config-dir ~/letsencrypt.test/etc
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
greenlock \
|
||||
--agree-tos --email 'john.doe@gmail.com' \
|
||||
--serve
|
||||
--root /root/www/example.com \
|
||||
--domains example.com,www.example.com \
|
||||
--config-dir ~/acme.test/etc
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
greenlock \
|
||||
--agree-tos --email 'john.doe@gmail.com' \
|
||||
--standalone \
|
||||
--domains example.com,www.example.com \
|
||||
--config-dir ~/acme.test/etc
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
greenlock \
|
||||
--agree-tos --email 'john.doe@gmail.com' \
|
||||
--root /root/www/example.com \
|
||||
--domains example.com,www.example.com \
|
||||
--config-dir ~/acme.test/etc
|
2
index.js
2
index.js
|
@ -86,7 +86,7 @@ module.exports.run = function (args) {
|
|||
if (args.tlsSni01Port) {
|
||||
servers.startServers(
|
||||
[], args.tlsSni01Port
|
||||
, { debug: args.debug, httpsOptions: le.httpsOptions }
|
||||
, { debug: args.debug, tlsOptions: le.tlsOptions }
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
#!/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"
|
||||
|
||||
echo "Installing node.js dependencies into $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 >/dev/null 2>/dev/null
|
||||
|
||||
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 "Installing Greenlock into $GREENLOCK_PATH"
|
||||
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
|
||||
# -o means overwrite, and there is no option to strip
|
||||
$my_unzip -o $my_tmp/greenlock-$my_tree.zip -d $GREENLOCK_PATH/ > /dev/null
|
||||
cp -ar $GREENLOCK_PATH/greenlock-cli.js/* $GREENLOCK_PATH/ > /dev/null
|
||||
rm -rf $GREENLOCK_PATH/greenlock-cli.js
|
||||
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 >/dev/null
|
||||
$my_npm install >/dev/null 2>/dev/null
|
||||
popd >/dev/null
|
||||
|
||||
cat << EOF > $GREENLOCK_PATH/bin/greenlock
|
||||
#!/bin/bash
|
||||
$my_node $GREENLOCK_PATH/bin/greenlock.js
|
||||
EOF
|
||||
chmod a+x $GREENLOCK_PATH/bin/greenlock
|
||||
echo "Creating link to 'greenlock' in /usr/local/bin"
|
||||
ln -sf $GREENLOCK_PATH/bin/greenlock /usr/local/bin/greenlock
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
echo "Installed successfully. Try it out:"
|
||||
echo ""
|
||||
echo " greenlock --help"
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
#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
|
|
@ -0,0 +1,31 @@
|
|||
'use strict';
|
||||
|
||||
module.exports.install = function (opts, cb) {
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var service = '/etc/systemd/system/greenlock.service';
|
||||
var rwDirs = [ '/etc/greenlock', '/srv/www', '/var/log/greenlock', '/opt/greenlock' ];
|
||||
|
||||
fs.readFile(path.join(__dirname, '../dist', service), 'utf8', function (e, text) {
|
||||
if (e) { throw e; }
|
||||
|
||||
text = text
|
||||
.replace(/MY_DESC/g, opts.description || 'Greenlock Secure Web Server')
|
||||
.replace(/MY_DOCS/g, opts.homepage || 'https://git.coolaj86.com/coolaj86/greenlock-cli.js')
|
||||
.replace(/MY_GREENLOCK_PATH/g, opts.greenlockPath || '/opt/greenlock')
|
||||
.replace(/MY_CONFIG_PATH/g, opts.greenlockPath || '/etc/greenlock/greenlock.yml')
|
||||
.replace(/MY_USER/g, opts.user || 'greenlock')
|
||||
.replace(/MY_GROUP/g, opts.user || 'greenlock')
|
||||
.replace(/MY_RW_DIRS/g, (opts.writableDirs || rwDirs).join(' '))
|
||||
;
|
||||
fs.writeFile(service, text, 'utf8', function (e) {
|
||||
if (e) { throw e; }
|
||||
|
||||
console.log("Now reload configs and enable to start on boot:");
|
||||
console.log("");
|
||||
console.log("\tsudo systemctl daemon-reload");
|
||||
console.log("\tsudo systemctl enable greenlock");
|
||||
cb(null);
|
||||
});
|
||||
});
|
||||
};
|
|
@ -25,7 +25,7 @@ module.exports.create = function (challenge) {
|
|||
, startServers: function (plainPorts, tlsPorts, opts) {
|
||||
opts = opts || {};
|
||||
|
||||
var httpsOptions = opts.httpsOptions || require('localhost.daplie.me-certificates');
|
||||
var tlsOptions = opts.tlsOptions;
|
||||
var https = require('https');
|
||||
var http = require('http');
|
||||
|
||||
|
@ -56,7 +56,7 @@ module.exports.create = function (challenge) {
|
|||
|
||||
// tls-sni-01-port
|
||||
tlsPorts.forEach(function (port) {
|
||||
var server = https.createServer(httpsOptions, servers.httpResponder);
|
||||
var server = https.createServer(tlsOptions, servers.httpResponder);
|
||||
|
||||
servers._servers.push(server);
|
||||
server.listen(port, function () {
|
||||
|
|
15
package.json
15
package.json
|
@ -34,16 +34,11 @@
|
|||
},
|
||||
"homepage": "https://git.coolaj86.com/coolaj86/greenlock-cli.js",
|
||||
"dependencies": {
|
||||
"cli": "^0.11.1",
|
||||
"greenlock": "^2.1.16",
|
||||
"homedir": "^0.6.0",
|
||||
"le-acme-core": "^2.0.5",
|
||||
"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",
|
||||
"cli": "^1.0.1",
|
||||
"greenlock": "^2.2.11",
|
||||
"le-challenge-manual": "^2.1.0",
|
||||
"le-challenge-standalone": "^2.1.0",
|
||||
"le-store-certbot": "^2.1.0",
|
||||
"mkdirp": "^0.5.1"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue