Skip to content

Commit

Permalink
Merge pull request #1 from lukas2511/master
Browse files Browse the repository at this point in the history
Update to latest from fork
  • Loading branch information
aareet authored Jun 16, 2016
2 parents 429c525 + 2042b17 commit f4decca
Show file tree
Hide file tree
Showing 17 changed files with 592 additions and 178 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
private_key.pem
private_key.json
domains.txt
config.sh
config
hook.sh
certs/*
archive/*
accounts/*
.acme-challenges/*
37 changes: 37 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Change Log
This file contains a log of major changes in letsencrypt.sh

## [x.x.x] - xxxx-xx-xx
## Changed
- Config is now named `config` instead of `config.sh`!
- Location of domains.txt is now configurable via DOMAINS_TXT config variable
- Location of certs directory is now configurable via CERTDIR config variable
- signcsr command now also outputs chain certificate
- Location of account-key(s) changed

## Added
- Added option to add CSR-flag indicating OCSP stapling to be mandatory
- Initial support for configuration on per-certificate base
- Support for per-CA account keys and custom config for output cert directory, license, etc.

## Fixed
- letsencrypt.sh no longer stores account keys from invalid registrations

## [0.2.0] - 2016-05-22
### Changed
- PRIVATE_KEY config parameter has been renamed to ACCOUNT_KEY to avoid confusion with certificate keys
- deploy_cert hook now also has the certificates timestamp as standalone parameter
- Temporary files are now identifiable (template: letsencrypt.sh-XXXXXX)
- Private keys are now regenerated by default

### Added
- Added documentation to repository

### Fixed
- Fixed bug with uppercase names in domains.txt (script now converts everything to lowercase)
- mktemp no longer uses the deprecated `-t` parameter.
- Compatibility with "pretty" json

## [0.1.0] - 2016-03-25
### Changed
- This is the first numbered version of letsencrypt.sh
89 changes: 13 additions & 76 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@ Current features:
- Renewal if a certificate is about to expire or SAN (subdomains) changed
- Certificate revocation

If you want to import existing keys from the official letsencrypt client have a look at [Import from official letsencrypt client](https://github.com/lukas2511/letsencrypt.sh/wiki/Import-from-official-letsencrypt-client).

**Please note that you should use the staging URL when testing so as not to hit rate limits.** See the [Staging](#staging) section, below.

Please keep in mind that this software and even the acme-protocol are relatively young and may still have some unresolved issues.
Feel free to report any issues you find with this script or contribute by submitting a pullrequest.

### Getting started

For getting started I recommend taking a look at [docs/domains_txt.md](docs/domains_txt.md), [docs/wellknown.md](docs/wellknown.md) and the [Usage](#usage) section on this page (you'll probably only need the `-c` option).

Generally you want to set up your WELLKNOWN path first, and then fill in domains.txt.

**Please note that you should use the staging URL when experimenting with this script to not hit letsencrypts rate limits.** See [docs/staging.md](docs/staging.md).

If you have any problems take a look at our [Troubleshooting](docs/troubleshooting.md) guide.

## Usage:

```text
Expand All @@ -37,80 +43,11 @@ Commands:
Parameters:
--domain (-d) domain.tld Use specified domain name(s) instead of domains.txt entry (one certificate!)
--force (-x) Force renew of certificate even if it is longer valid than value in RENEW_DAYS
--ocsp Sets option in CSR indicating OCSP stapling to be mandatory
--privkey (-p) path/to/key.pem Use specified private key instead of account key (useful for revocation)
--config (-f) path/to/config.sh Use specified config file
--config (-f) path/to/config Use specified config file
--hook (-k) path/to/hook.sh Use specified script for hooks
--out (-o) certs/directory Output certificates into the specified directory
--challenge (-t) http-01|dns-01 Which challenge should be used? Currently http-01 and dns-01 are supported
--algo (-a) rsa|prime256v1|secp384r1 Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
```

### domains.txt

The file `domains.txt` should have the following format:

```text
example.com www.example.com
example.net www.example.net wiki.example.net
```

This states that there should be two certificates `example.com` and `example.net`,
with the other domains in the corresponding line being their alternative names.

### $WELLKNOWN / challenge-response

Boulder (acme-server) is looking for challenge responses under your domain in the `.well-known/acme-challenge` directory

This script uses `http-01`-type verification (for now) so you need to have that directory available over normal http (redirect to https will be acceptable).

A full URL would look like `http://example.org/.well-known/acme-challenge/c3VjaC1jaGFsbGVuZ2UtbXVjaA-aW52YWxpZC13b3c`.

An example setup to get this to work would be:

nginx.conf:
```
...
location /.well-known/acme-challenge {
alias /var/www/letsencrypt;
}
...
```

config.sh:
```bash
...
WELLKNOWN="/var/www/letsencrypt"
...
```

An alternative to setting the WELLKNOWN variable would be to create a symlink to the default location next to the script (or BASEDIR):
`ln -s /var/www/letsencrypt .acme-challenges`

### Staging

Let’s Encrypt has stringent rate limits in place during the public beta period. If you start testing using the production endpoint (which is the default), you will quickly hit these limits and find yourself locked out. To avoid this, please set the CA property to the Let’s Encrypt staging server URL in your `config.sh` file:

```bash
CA="https://acme-staging.api.letsencrypt.org/directory"
```

### dns-01 challenge

This script also supports the new `dns-01`-type verification. This type of verification requires you to be able to create a specific `TXT` DNS record for each hostname included in the certificate.

You need a hook script that deploys the challenge to your DNS server!

The hook script (indicated in the config.sh file or the --hook/-k command line argument) gets four arguments: an operation name (clean_challenge, deploy_challenge, or deploy_cert) and some operands for that. For deploy_challenge $2 is the domain name for which the certificate is required, $3 is a "challenge token" (which is not needed for dns-01), and $4 is a token which needs to be inserted in a TXT record for the domain.

Typically, you will need to split the subdomain name in two, the subdomain name and the domain name separately. For example, for "my.example.com", you'll need "my" and "example.com" separately. You then have to prefix "_acme-challenge." before the subdomain name, as in "_acme-challenge.my" and set a TXT record for that on the domain (e.g. "example.com") which has the value supplied in $4

That could be done manually (as most providers don't have a DNS API), by having your hook script echo $1, $2 and $4 and then wait (read -s -r -e < /dev/tty) - give it a little time to get into their DNS system. Usually providers give you a boxes to put "_acme-challenge.my" and the token value in, and a dropdown to choose the record type, TXT.

Or when you do have a DNS API, pass the details accordingly to achieve the same thing.

You can delete the TXT record when called with operation clean_challenge, when $2 is also the domain name.

Here are some examples: [Examples for DNS-01 hooks](https://github.com/lukas2511/letsencrypt.sh/wiki/Examples-for-DNS-01-hooks)

### Elliptic Curve Cryptography (ECC)

This script also supports certificates with Elliptic Curve public keys! Be aware that at the moment this is not available on the production servers from letsencrypt. Please read https://community.letsencrypt.org/t/ecdsa-testing-on-staging/8809/ for the current state of ECC support.
22 changes: 22 additions & 0 deletions docs/dns-verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
### dns-01 challenge

This script also supports the new `dns-01`-type verification. This type of verification requires you to be able to create a specific `TXT` DNS record for each hostname included in the certificate.

You need a hook script that deploys the challenge to your DNS server!

The hook script (indicated in the config file or the --hook/-k command line argument) gets four arguments: an operation name (clean_challenge, deploy_challenge, or deploy_cert) and some operands for that. For deploy_challenge $2 is the domain name for which the certificate is required, $3 is a "challenge token" (which is not needed for dns-01), and $4 is a token which needs to be inserted in a TXT record for the domain.

Typically, you will need to split the subdomain name in two, the subdomain name and the domain name separately. For example, for "my.example.com", you'll need "my" and "example.com" separately. You then have to prefix "_acme-challenge." before the subdomain name, as in "_acme-challenge.my" and set a TXT record for that on the domain (e.g. "example.com") which has the value supplied in $4

```
_acme-challenge IN TXT $4
_acme-challenge.my IN TXT $4
```

That could be done manually (as most providers don't have a DNS API), by having your hook script echo $1, $2 and $4 and then wait (read -s -r -e < /dev/tty) - give it a little time to get into their DNS system. Usually providers give you a boxes to put "_acme-challenge.my" and the token value in, and a dropdown to choose the record type, TXT.

Or when you do have a DNS API, pass the details accordingly to achieve the same thing.

You can delete the TXT record when called with operation clean_challenge, when $2 is also the domain name.

Here are some examples: [Examples for DNS-01 hooks](https://github.com/lukas2511/letsencrypt.sh/wiki/Examples-for-DNS-01-hooks)
13 changes: 13 additions & 0 deletions docs/domains_txt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
### domains.txt

letsencrypt.sh uses the file `domains.txt` as configuration for which certificates should be requested.

The file should have the following format:

```text
example.com www.example.com
example.net www.example.net wiki.example.net
```

This states that there should be two certificates `example.com` and `example.net`,
with the other domains in the corresponding line being their alternative names.
5 changes: 5 additions & 0 deletions docs/ecc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Elliptic Curve Cryptography (ECC)

This script also supports certificates with Elliptic Curve public keys!
Be aware that at the moment this is not available on the production servers from letsencrypt.
Please read https://community.letsencrypt.org/t/ecdsa-testing-on-staging/8809/ for the current state of ECC support.
29 changes: 18 additions & 11 deletions config.sh.example → docs/examples/config
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
#!/usr/bin/env bash

########################################################
# This is the main config file for letsencrypt.sh #
# #
# This file is looked for in the following locations: #
# $SCRIPTDIR/config.sh (next to this script) #
# /usr/local/etc/letsencrypt.sh/config.sh #
# /etc/letsencrypt.sh/config.sh #
# ${PWD}/config.sh (in current working-directory) #
# $SCRIPTDIR/config (next to this script) #
# /usr/local/etc/letsencrypt.sh/config #
# /etc/letsencrypt.sh/config #
# ${PWD}/config (in current working-directory) #
# #
# Default values of this config are in comments #
########################################################
Expand All @@ -30,12 +28,18 @@
# Base directory for account key, generated certificates and list of domains (default: $SCRIPTDIR -- uses config directory if undefined)
#BASEDIR=$SCRIPTDIR

# File containing the list of domains to request certificates for (default: $BASEDIR/domains.txt)
#DOMAINS_TXT="${BASEDIR}/domains.txt"

# Output directory for generated certificates
#CERTDIR="${BASEDIR}/certs"

# Directory for account keys and registration information
#ACCOUNTDIR="${BASEDIR}/accounts"

# Output directory for challenge-tokens to be served by webserver or deployed in HOOK (default: $BASEDIR/.acme-challenges)
#WELLKNOWN="${BASEDIR}/.acme-challenges"

# Location of private account key (default: $BASEDIR/private_key.pem)
#PRIVATE_KEY="${BASEDIR}/private_key.pem"

# Default keysize for private keys (default: 4096)
#KEYSIZE="4096"

Expand All @@ -60,8 +64,8 @@
# Minimum days before expiration to automatically renew certificate (default: 30)
#RENEW_DAYS="30"

# Regenerate private keys instead of just signing new certificates on renewal (default: no)
#PRIVATE_KEY_RENEW="no"
# Regenerate private keys instead of just signing new certificates on renewal (default: yes)
#PRIVATE_KEY_RENEW="yes"

# Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
#KEY_ALGO=rsa
Expand All @@ -71,3 +75,6 @@

# Lockfile location, to prevent concurrent access (default: $BASEDIR/lock)
#LOCKFILE="${BASEDIR}/lock"

# Option to add CSR-flag indicating OCSP stapling to be mandatory (default: no)
#OCSP_MUST_STAPLE="no"
File renamed without changes.
24 changes: 23 additions & 1 deletion hook.sh.example → docs/examples/hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function clean_challenge {
}

function deploy_cert {
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}"
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}" TIMESTAMP="${6}"

# This hook is called once for each certificate that has been
# produced. Here you might, for instance, copy your new certificates
Expand All @@ -50,6 +50,28 @@ function deploy_cert {
# The path of the file containing the full certificate chain.
# - CHAINFILE
# The path of the file containing the intermediate certificate(s).
# - TIMESTAMP
# Timestamp when the specified certificate was created.
}

function unchanged_cert {
local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAINFILE="${5}"

# This hook is called once for each certificate that is still
# valid and therefore wasn't reissued.
#
# Parameters:
# - DOMAIN
# The primary domain name, i.e. the certificate common
# name (CN).
# - KEYFILE
# The path of the file containing the private key.
# - CERTFILE
# The path of the file containing the signed certificate.
# - FULLCHAINFILE
# The path of the file containing the full certificate chain.
# - CHAINFILE
# The path of the file containing the intermediate certificate(s).
}

HANDLER=$1; shift; $HANDLER $@
63 changes: 63 additions & 0 deletions docs/hook_chain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# HOOK_CHAIN

If you want to deploy (and clean) all challenges for a single certificate in one hook call you can use `HOOK_CHAIN=yes` in your config.

Calls to your hook script change in a way that instead of having only X parameters on deploy_challenge and clean_challenge it will have Y*X parameters,
where Y is the number of domains in your cert, and you'll have to iterate over a set of X parameters at a time in your hook script.

See below for an example on how the calls change:

### HOOK_CHAIN="no" (default behaviour)
```
# INFO: Using main config file /etc/letsencrypt.sh/config
Processing lukas.im with alternative names: www.lukas.im
+ Checking domain name(s) of existing cert... unchanged.
+ Checking expire date of existing cert...
+ Valid till Jul 7 20:54:00 2016 GMT (Longer than 30 days). Ignoring because renew was forced!
+ Signing domains...
+ Generating private key...
+ Generating signing request...
+ Requesting challenge for lukas.im...
+ Requesting challenge for www.lukas.im...
HOOK: deploy_challenge lukas.im blablabla blablabla.supersecure
+ Responding to challenge for lukas.im...
HOOK: clean_challenge lukas.im blablabla blablabla.supersecure
+ Challenge is valid!
HOOK: deploy_challenge www.lukas.im blublublu blublublu.supersecure
+ Responding to challenge for www.lukas.im...
HOOK: clean_challenge www.lukas.im blublublu blublublu.supersecure
+ Challenge is valid!
+ Requesting certificate...
+ Checking certificate...
+ Done!
+ Creating fullchain.pem...
HOOK: deploy_cert lukas.im /etc/letsencrypt.sh/certs/lukas.im/privkey.pem /etc/letsencrypt.sh/certs/lukas.im/cert.pem /etc/letsencrypt.sh/certs/lukas.im/fullchain.pem /etc/letsencrypt.sh/certs/lukas.im/chain.pem 1460152442
+ Done!
```

### HOOK_CHAIN="yes"
```
# INFO: Using main config file /etc/letsencrypt.sh/config
Processing lukas.im with alternative names: www.lukas.im
+ Checking domain name(s) of existing cert... unchanged.
+ Checking expire date of existing cert...
+ Valid till Jul 7 20:52:00 2016 GMT (Longer than 30 days). Ignoring because renew was forced!
+ Signing domains...
+ Generating private key...
+ Generating signing request...
+ Requesting challenge for lukas.im...
+ Requesting challenge for www.lukas.im...
HOOK: deploy_challenge lukas.im blablabla blablabla.supersecure www.lukas.im blublublu blublublu.supersecure
+ Responding to challenge for lukas.im...
+ Challenge is valid!
+ Responding to challenge for www.lukas.im...
+ Challenge is valid!
HOOK: clean_challenge lukas.im blablabla blablabla.supersecure www.lukas.im blublublu blublublu.supersecure
+ Requesting certificate...
+ Checking certificate...
+ Done!
+ Creating fullchain.pem...
HOOK: deploy_cert lukas.im /etc/letsencrypt.sh/certs/lukas.im/privkey.pem /etc/letsencrypt.sh/certs/lukas.im/cert.pem /etc/letsencrypt.sh/certs/lukas.im/fullchain.pem /etc/letsencrypt.sh/certs/lukas.im/chain.pem 1460152408
+ Done!
```

3 changes: 3 additions & 0 deletions docs/import-from-official-client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Import

If you want to import existing keys from the official letsencrypt client have a look at [Import from official letsencrypt client](https://github.com/lukas2511/letsencrypt.sh/wiki/Import-from-official-letsencrypt-client).
18 changes: 18 additions & 0 deletions docs/per-certificate-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Config on per-certificate base

letsencrypt.sh allows a few configuration variables to be set on a per-certificate base.

To use this feature create a `config` file in the certificates output directory (e.g. `certs/example.org/config`).

Currently supported options:

- PRIVATE_KEY_RENEW
- KEY_ALGO
- KEYSIZE
- OCSP_MUST_STAPLE
- CHALLENGETYPE
- HOOK
- HOOK_CHAIN
- WELLKNOWN
- OPENSSL_CNF
- RENEW_DAYS
15 changes: 15 additions & 0 deletions docs/staging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Staging

Let’s Encrypt has stringent rate limits in place during the public beta period.

If you start testing using the production endpoint (which is the default),
you will quickly hit these limits and find yourself locked out.

To avoid this, please set the CA property to the Let’s Encrypt staging server URL in your config file:

```bash
CA="https://acme-staging.api.letsencrypt.org/directory"
```

Please keep in mind that at the time of writing this letsencrypt.sh doesn't have support for registration management,
so if you change CA you'll have to move your `private_key.pem` (and, if you care, `private_key.json`) out of the way.
Loading

0 comments on commit f4decca

Please sign in to comment.