feat: more powerful config generation
the build was successful Details

config generation is now done with jinja2 instead of sed.
also added a way to do the dns challenge without fiddling with more than env vars.

BREAKING CHANGE: a lot of env vars and volumes changed.
This commit is contained in:
Jan Christian Grünhage 2018-04-23 18:55:55 +01:00
parent a6ef1720ca
commit 8369338216
11 changed files with 324 additions and 130 deletions

View File

@ -7,6 +7,7 @@ RUN apk add --no-cache \
libffi-dev \
build-base \
openssl-dev \
py2-pip \
&& apk add --no-cache \
--virtual .runtime-deps \
openssl \
@ -16,14 +17,26 @@ RUN apk add --no-cache \
bash \
su-exec \
libxml2-utils \
&& git clone https://github.com/lukas2511/dehydrated /dehydrated \
py2-pip \
python3 \
&& mkdir /opt \
&& git clone https://github.com/lukas2511/dehydrated.git /opt/dehydrated \
&& pip3 install requests[security] \
&& pip3 install dns-lexicon \
&& pip2 install j2cli[yaml] \
&& apk del .build-deps
ENV \
DEHYDRATED_CA="https://acme-staging-v02.api.letsencrypt.org/directory" \
DEHYDRATED_CHALLENGE="http-01" \
DEHYDRATED_KEYSIZE="4096" \
DEHYDRATED_HOOK="" \
DEHYDRATED_RENEW_DAYS="30" \
DEHYDRATED_KEY_RENEW="yes" \
DEHYDRATED_ACCEPT_TERMS="no" \
DEHYDRATED_EMAIL="user@example.org" \
DEHYDRATED_GENERATE_CONFIG="yes"
ADD root /
VOLUME /etc/dehydrated
VOLUME /var/www/dehydrated
VOLUME /certs
VOLUME /data

View File

@ -5,22 +5,57 @@
This is a docker container that wraps around [dehydrated](https://github.com/lukas2511/dehydrated).
## Usage
We have short tutorials for two different modi operandi: The `dns-01` and `http-01` challenge.
Both are fairly easy to use. The `dns-01` challenge requires less effort if your DNS provider
is supported by [lexicon](https://github.com/AnalogJ/lexicon/#providers), the `http-01` challenge otherwise.
For a short tutorial on how to use this container with zero-configuration to sign certificates
using the HTTP-Challenge, see ["Zero-Config"-Mode"](zero-config-mode.md).
For a short tutorial of getting a certificate with this container and the `dns-01` challenge,
go [here](dns-01.md), for the same using the `http-01` challenge, go [here](http-01.md).
## Environment variables
The following environment variables can be set to influence the container's behaviour:
- `$ENDPOINT` which ACME-Endpoint you want to use, supported values: "staging", "production" (default).
- `$CHALLENGE` what type of challenge should be used, supported values: "http-01" (default), "dns-01"
If the environment variables were not explicitely set, no modification to the configuration file is made
## Behaviour on startup
When the container is started, a script is run which looks for the configuration file in the places supported by dehydrated,
and if no configuration file is found, it will copy the [example configuration file](https://github.com/lukas2511/dehydrated/docs/examples/config)
into `/etc/dehydrated/config`.
## Behaviour
By default the container will attempt to generate a config as `/data/config`
with the default values for all the environment variables.
The defaults are explicitly meant to not work. Things you need to change:
- set `DEHYDRATED_ACCEPT_TERMS` to yes, ***after reading letsencrypts ToS***
- set `DEHYDRATED_EMAIL` to an email address you own
- set `DEHYDRATED_CA` to a production ACME CA, for example letsencrypt's ACME v2 endpoint, "https://acme-v02.api.letsencrypt.org/directory"
- Only do this ***after*** you have tried it with the default staging endpoint
and it worked and you got the certificates you want. If this fails too often,
letsencrypt will block your IP and domain for a week, so do your experiments
on the staging endpoint.
### Advanced configuration
- `DEHYDRATED_CA`:
This controls which ACME endpoint dehydrated contacts. The most common value for
production environments is "https://acme-v02.api.letsencrypt.org/directory",
while you should use "https://acme-staging-v02.api.letsencrypt.org/directory"
for experiments.
- `DEHYDRATED_CHALLENGE`:
You can either put `dns-01` or `http-01` here, depending on how you want letsencrypt
to verify that you are allowed to obtain this certificate.
- `DEHYDRATED_KEYSIZE`:
This defaults to `4096`, but you could also put `2048` or `3072` here, if you want
less secure but slightly faster keys. This only makes sense if your host or your clients
are *very slow*.
- `DEHYDRATED_HOOK`:
If you use the `dns-01` challenge, you need to supply a hook script,
which dehydrated will use to set dns records. The container ships with
lexicon installed and a lexicon hook in `/usr/local/bin/lexicon-hook`.
Apart from the `dns-01` challenge, you can also use hooks to deploy newly created
certificates. For more info see [dehydrated's project page](https://github.com/lukas2511/dehydrated).
- `DEHYDRATED_RENEW_DAYS`:
When dehydrated runs, it will check if any certificates need renewal and renew those.
All certificates which expire in the next `n` days will be renewed, where `n` is the
number you set here. Default is 30
- `DEHYDRATED_KEY_RENEW`:
Set this to yes to make dehydrated renew keys too when renewing certificates, or to
no to keep the keys.
- `DEHYDRATED_ACCEPT_TERMS`:
For the first run this needs to be set to yes, else dehydrated will not work.
Read the terms of service of letsencrypt before setting this to yes.
- `DEHYDRATED_EMAIL`:
Set your email address here.
- `DEHYDRATED_GENERATE_CONFIG`:
Set to yes by default. If you want to use a config supplied by you,
change this to no and put your own config in `/data/config`
- `UID` and `GID`: You can set the UID and GID of the things run in the docker container here.s

47
dns-01.md Normal file
View File

@ -0,0 +1,47 @@
## Prerequisites
For using the container with the `dns-01`-challenge, you need on your machine:
- docker-compose (and docker obviously)
Download [the docker-compose example](https://git.jcg.re/jcgruenhage/docker-dehydrated/raw/branch/master/examples/docker-compose.dns-01.yml)
as `docker-compose.yml` into an empty folder. Inside that folder, create the folder `data`.
Now, create a file `data/domains.txt` in which you list the domains you want to create certificates for,
using the following format:
- each certificate on a new line
- each line can contain a list of (sub)domains, separated by spaces
For more information on the format, see [the dehydrated documentation](https://github.com/lukas2511/dehydrated/blob/master/docs/domains_txt.md).
Before running, you need to set a few things in the `docker-compose.yml` file,
as explained [here in the README](https://git.jcg.re/jcgruenhage/docker-dehydrated#behaviour)
## Configuring DNS API access
Dehydrated needs to be able to add and remove DNS entries. For this,
the `docker-compose.yml` file contains an example how this works with Cloudflare.
Set your email and token there, and if you use another provider from
[this list](https://github.com/AnalogJ/lexicon/#providers), do the same but replace
cloudflare with your provider everywhere.
## Using docker-dehydrated
Inside the folder where you put the `docker-compose.yml` file, run this command:
```bash
docker-compose up
```
This will create the requested certificates, if possible.
It will also check once per week whether the certificates need to be renewed,
and do so if necessary. To start the container in the background, add ` -d`
to the end of the command above.
Please note that the container will `chown` the folders passed to it, so make sure your webserver can
still serve the contents of `data/wellknown`. You can configure the UID and GID
that the container uses by adding a UID and GID environment variable to the
`docker-compose.yml` file.
After the challenges have been run, the certificates will be stored in `data/certs`.

View File

@ -0,0 +1,20 @@
version: '2'
services:
dehydrated:
image: docker.jcg.re/dehydrated
restart: unless-stopped
volumes:
- ./data:/data
environment:
- DEHYDRATED_GENERATE_CONFIG="yes"
- DEHYDRATED_CA="https://acme-v02.api.letsencrypt.org/directory"
- DEHYDRATED_CHALLENGE="dns-01"
- DEHYDRATED_KEYSIZE="4096"
- DEHYDRATED_HOOK="/usr/local/bin/lexicon-hook"
- DEHYDRATED_RENEW_DAYS="30"
- DEHYDRATED_KEY_RENEW="yes"
- DEHYDRATED_EMAIL="user@example.org"
- DEHYDRATED_ACCEPT_TERMS=no
- PROVIDER=cloudflare
- LEXICON_CLOUDFLARE_USERNAME=user@example.org
- LEXICON_CLOUDFLARE_TOKEN=abcdefghijklmnopqrstuvwxyz01234567890

View File

@ -0,0 +1,16 @@
version: '2'
services:
dehydrated:
image: docker.jcg.re/dehydrated
restart: unless-stopped
volumes:
- ./data:/data
environment:
- DEHYDRATED_GENERATE_CONFIG="yes"
- DEHYDRATED_CA="https://acme-v02.api.letsencrypt.org/directory"
- DEHYDRATED_CHALLENGE="http-01"
- DEHYDRATED_KEYSIZE="4096"
- DEHYDRATED_RENEW_DAYS="30"
- DEHYDRATED_KEY_RENEW="yes"
- DEHYDRATED_EMAIL="user@example.org"
- DEHYDRATED_ACCEPT_TERMS=no

44
http-01.md Normal file
View File

@ -0,0 +1,44 @@
## Prerequisites
These are the things that you need to setup / already have set up in order to
use this container for creating certificates using the `http-01`-challenge:
- A working internet connection, with port 80 of your machine available publicly
- An http server to serve the `.well-known` folder
- docker-compose
Download [the docker-compose example](https://git.jcg.re/jcgruenhage/docker-dehydrated/raw/branch/master/examples/docker-compose.http-01.yml)
as `docker-compose.yml` into an empty folder. Inside that folder, create the folder `data/wellknown`.
Configure your Webserver to serve the contents of this folder under `domain/.well-known/acme-challenge`
(for all domains for which you want to create certificates).
Now, create a file `data/domains.txt` in which you list the domains you want to create certificates for,
using the following format:
- each certificate on a new line
- each line can contain a list of (sub)domains, separated by spaces
For more information on the format, see [the dehydrated documentation](https://github.com/lukas2511/dehydrated/blob/master/docs/domains_txt.md).
Before running, you need to set a few things in the `docker-compose.yml` file,
as explained [here in the README](https://git.jcg.re/jcgruenhage/docker-dehydrated#behaviour)
## Using docker-dehydrated
Inside the folder where you put the `docker-compose.yml` file, run this command:
```bash
docker-compose up
```
This will create the requested certificates, if possible.
It will also check once per week whether the certificates need to be renewed,
and do so if necessary. To start the container in the background, add ` -d`
to the end of the command above.
Please note that the container will `chown` the folders passed to it, so make sure your webserver can
still serve the contents of `data/wellknown`. You can configure the UID and GID
that the container uses by adding a UID and GID environment variable to the
`docker-compose.yml` file.
After the challenges have been run, the certificates will be stored in `data/certs`.

View File

@ -0,0 +1,116 @@
########################################################
# This is the main config file for dehydrated #
# #
# This file is looked for in the following locations: #
# $SCRIPTDIR/config (next to this script) #
# /usr/local/etc/dehydrated/config #
# /etc/dehydrated/config #
# ${PWD}/config (in current working-directory) #
# #
# Default values of this config are in comments #
########################################################
# Which user should dehydrated run as? This will be implictly enforced when running as root
#DEHYDRATED_USER=
# Which group should dehydrated run as? This will be implictly enforced when running as root
#DEHYDRATED_GROUP=
# Resolve names to addresses of IP version only. (curl)
# supported values: 4, 6
# default: <unset>
#IP_VERSION=
# Path to certificate authority (default: https://acme-v02.api.letsencrypt.org/directory)
CA={{ DEHYDRATED_CA }}
# Path to old certificate authority
# Set this value to your old CA value when upgrading from ACMEv1 to ACMEv2 under a different endpoint.
# If dehydrated detects an account-key for the old CA it will automatically reuse that key
# instead of registering a new one.
# default: https://acme-v01.api.letsencrypt.org/directory
#OLDCA="https://acme-v01.api.letsencrypt.org/directory"
# Which challenge should be used? Currently http-01 and dns-01 are supported
CHALLENGETYPE={{ DEHYDRATED_CHALLENGE }}
# Path to a directory containing additional config files, allowing to override
# the defaults found in the main configuration file. Additional config files
# in this directory needs to be named with a '.sh' ending.
# default: <unset>
#CONFIG_D=
# Base directory for account key, generated certificates and list of domains (default: $SCRIPTDIR -- uses config directory if undefined)
BASEDIR=/data
# 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: /var/www/dehydrated)
WELLKNOWN=/data/wellknown
# Default keysize for private keys (default: 4096)
keysize={{ DEHYDRATED_KEYSIZE }}
# Path to openssl config file (default: <unset> - tries to figure out system default)
#OPENSSL_CNF=
# Path to OpenSSL binary (default: "openssl")
#OPENSSL="openssl"
# Extra options passed to the curl binary (default: <unset>)
#CURL_OPTS=
# Program or function called in certain situations
#
# After generating the challenge-response, or after failed challenge (in this case altname is empty)
# Given arguments: clean_challenge|deploy_challenge altname token-filename token-content
#
# After successfully signing certificate
# Given arguments: deploy_cert domain path/to/privkey.pem path/to/cert.pem path/to/fullchain.pem
#
# BASEDIR and WELLKNOWN variables are exported and can be used in an external program
# default: <unset>
HOOK={{ DEHYDRATED_HOOK }}
# Chain clean_challenge|deploy_challenge arguments together into one hook call per certificate (default: no)
#HOOK_CHAIN="no"
# Minimum days before expiration to automatically renew certificate (default: 30)
RENEW_DAYS={{ DEHYDRATED_RENEW_DAYS }}
# Regenerate private keys instead of just signing new certificates on renewal (default: yes)
PRIVATE_KEY_RENEW={{ DEHYDRATED_KEY_RENEW }}
# Create an extra private key for rollover (default: no)
#PRIVATE_KEY_ROLLOVER="no"
# Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
#KEY_ALGO=rsa
# E-mail to use during the registration (default: <unset>)
CONTACT_EMAIL={{ DEHYDRATED_EMAIL }}
# 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"
# Fetch OCSP responses (default: no)
#OCSP_FETCH="no"
# Issuer chain cache directory (default: $BASEDIR/chains)
#CHAINCACHE="${BASEDIR}/chains"
# Automatic cleanup (default: no)
#AUTO_CLEANUP="no"
# ACME API version (default: auto)
#API=auto

View File

@ -1,4 +1,4 @@
#!/bin/bash
chown -R ${UID}:${GID} /etc/dehydrated /certs /var/www/dehydrated
chown -R ${UID}:${GID} /data
# Run dehydrated
su-exec ${UID}:${GID} /dehydrated/dehydrated --cron --keep-going
su-exec ${UID}:${GID} /opt/dehydrated/dehydrated --config /data/config --cron --keep-going

View File

@ -1,11 +1,18 @@
#!/bin/sh
#!/bin/bash
s6-svc -O /etc/s6.d/dehydrated
# If config generation is turned on, generate a config from the template and current env vars
if [[ "$DEHYDRATED_GENERATE_CONFIG" == "yes" ]]; then
j2 /etc/dehydrated/config.j2 > /data/config
fi
# Set ownership to dehydrated on the relevant folders
chown -R ${UID}:${GID} /etc/dehydrated /certs /var/www/dehydrated
chown -R ${UID}:${GID} /data
# Register to the CA
su-exec ${UID}:${GID} /dehydrated/dehydrated --register --accept-terms
if [[ "$DEHYDRATED_ACCEPT_TERMS" == "yes" ]]; then
su-exec ${UID}:${GID} /opt/dehydrated/dehydrated --config /data/config --register --accept-terms
fi
# Run the weekly script once
/etc/periodic/weekly/dehydrated

View File

@ -1,58 +0,0 @@
#!/bin/bash
s6-svc -O /etc/s6.d/setup
# Check if and which configuration file exists
CONFIGFILE="none"
for check_config in "/etc/dehydrated" "/usr/local/etc/dehydrated" "${PWD}" "${SCRIPTDIR}"; do
if [[ -f "${check_config}/config" ]]; then
CONFIGFILE="${check_config}/config"
fi
done
# At this point, if no configuration file exists, copy the example into /etc/dehydrated
if [[ "$CONFIGFILE" == "none" ]]; then
cp /dehydrated/docs/examples/config /etc/dehydrated/config
CONFIGFILE="/etc/dehydrated/config"
fi
# Determine if the staging endpoint should be used
case "$ENDPOINT" in
"staging")
# If CA=... is commented, uncomment and set it to staging, if it is set to production, set it to staging
sed -ie 's/#CA=.*$/CA="https:\/\/acme-staging.api.letsencrypt.org\/directory"/g' $CONFIGFILE
sed -ie 's/CA=.+acme-v01\.api\..+$/CA="https:\/\/acme-staging.api.letsencrypt.org\/directory"/g' $CONFIGFILE
# Same procedure for CA_TERMS=...
sed -ie 's/#CA_TERMS=.*$/CA_TERMS="https:\/\/acme-staging.api.letsencrypt.org\/terms"/g' $CONFIGFILE
sed -ie 's/CA_TERMS=.+acme-v01\.api\..+$/CA_TERMS="https:\/\/acme-staging.api.letsencrypt.org\/terms"/g' $CONFIGFILE
;;
"production")
# If CA=... is commented, uncomment and set to production, if it was set to staging, set it to production
sed -ie 's/#CA=.*$/CA="https:\/\/acme-v01.api.letsencrypt.org\/directory"/g' $CONFIGFILE
sed -ie 's/CA=.+acme-staging\.api\..+$/https:\/\/acme-v01.api.letsencrypt.org\/directory"/g' $CONFIGFILE
# Same thing for CA_TERMS=...
sed -ie 's/#CA_TERMS=.*$/CA_TERMS="https:\/\/acme-v01.api.letsencrypt.org\/terms"/g' $CONFIGFILE
sed -ie 's/CA_TERMS=.+acme-staging\.api\..+$/CA_TERMS="https:\/\/acme-v01.api.letsencrypt.org\/terms"/g' $CONFIGFILE
;;
*)
echo "INFO: No endpoint was specifically set, dehydrated will use its default"
;;
esac
# Determine which type of challenge should be used
case "$CHALLENGE" in
"http-01")
# If we have a "fresh" config, uncomment the challengetype-line and set our value
sed -ie 's/#CHALLENGETYPE=.*$/CHALLENGETYPE="http-01"/g' $CONFIGFILE
# If a challengetype is already set, overwrite it
sed -ie 's/CHALLENGETYPE=.+$/CHALLENGETYPE="http-01"/g' $CONFIGFILE
;;
"dns-01")
# If we have the default config, uncomment the line and set our challengetype
sed -ie 's/#CHALLENGETYPE=.*$/CHALLENGETYPE="dns-01"/g' $CONFIGFILE
# If a challengetype was already set, overwrite it with the new value
sed -ie 's/CHALLENGETYPE=.+$/CHALLENGETYPE="dns-01"/g' $CONFIGFILE
;;
*)
echo "INFO: No challenge-type was specified, the default from dehydrated will be used"
;;
esac

View File

@ -1,46 +0,0 @@
# Zero-configuration mode
This is a tutorial on how to use this container (without needing to configure anything) to create
certificates for a given set of domains (using the HTTP-Challenge).
## Prerequisites
These are the things that you need to setup / already have set up in order to use this container
for creating certificates using the HTTP-Challenge:
- A working internet connection, obviously
- HTTP Webserver to serve the ``.well-known`` which is used for the HTTP-Challenge
Now create a folder in which dehydrated can push the challenge-data later, in this tutorial it
will be called ``dehydrated-www``. Configure your Webserver to serve the contents of this folder
under ``domain/.well-known/acme-challenge`` (for all domains for which you want to create certificates).
Next create another folder in which dehydrated will place its configuration, certificates etc.,
in this tutorial it will be called ``dehydrated-data``. In this folder, create a file called
``domains.txt`` in which you list the domains you want to create certificates for, using the
following format:
- each domain on a new line
- subdomains of a domain on the same line as the domain.
For more information on the format, see [https://github.com/lukas2511/dehydrated/blob/master/docs/domains_txt.md](https://github.com/lukas2511/dehydrated/blob/master/docs/domains_txt.md)
## Using docker-dehydrated
Now you can just run the container, and as the default challenge is the HTTP-Challenge, you do
not need to pass environment variables to alter the default behaviour. To run the container,
execute:
```bash
$ docker run -v ./dehydrated-www:/var/www/dehydrated \
-v ./dehydrated-data:/etc/dehydrated docker.jcg.re/dehydrated
```
Please note that on SELinux-Systems, you need to set the "SELinux"-Flag when passing volumes:
``./dehydrated-www:/var/www/dehydrated:z`` (analog for ``dehydrated-data``).
Also, the container will ``chown`` the folders passed to himself, so make sure your webserver can
still serve the contents of ``dehydrated-www``.
After the challenges have been run, the certificates will be stored in ``dehydrated-data/certs``,
make sure to back this folder up!