diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d0f308e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea +*.iml + +*.env \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..3bd7b62 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +sudo: required + +language: bash + +dist: trusty + +env: + - VERSION=0.3.1 + +services: + - docker + +install: + - git clone https://github.com/docker-library/official-images.git ~/official-images + +before_script: + - env | sort + - cd "$VERSION" + - image="willfarrell/letsencrypt:$VERSION" + +script: + - docker build --pull -t "$image" . + - docker run -d "$image" + - docker ps | grep "$image" | awk '{print $1}' || { exit 1; } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..925ed04 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +# TODO python 3 not-supported lexicon#68 +FROM library/python:2-alpine + +# deps - openssl curl sed grep mktemp +RUN apk --no-cache add bash openssl curl git \ + && cd /tmp \ + && git clone https://github.com/lukas2511/dehydrated.git --depth 1 \ + && chmod a+x dehydrated/dehydrated \ + && mv dehydrated/dehydrated /usr/bin/ \ + && git clone https://github.com/AnalogJ/lexicon.git --depth 1 \ + && chmod a+x lexicon/examples/dehydrated.default.sh \ + && mv lexicon/examples/dehydrated.default.sh /usr/bin/dehydrated-dns \ + && rm -rf /tmp/* \ + && pip install dns-lexicon dns-lexicon[route53] dns-lexicon[transip] \ + && apk del git + +COPY config /etc/dehydrated/config + +COPY docker-entrypoint.sh / +ENTRYPOINT ["/docker-entrypoint.sh"] + +CMD ["dehydrated","-h"] \ No newline at end of file diff --git a/README.md b/README.md index 5328f35..473f95d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,80 @@ # docker-letsencrypt -container to generate TLS certs + +Creates ECDSA certs based on ENV + +## Dockerfile +Use to set your own defaults +```Dockerfile +FROM willfarrell/letsencrypt + +COPY config /etc/dehydrated/config +``` + +## ENV +``` +# defaults to `staging`, use `production` when ready. +LE_ENV=staging +# Only required if you plan to use dns-01 challenges (use for private services) +PROVIDER=cloudflare +LEXICON_CLOUDFLARE_USERNAME= +LEXICON_CLOUDFLARE_TOKEN= +``` + +## Testing +```bash +docker build -t letsencrypt . +docker rm -f letsencrypt + +# private +docker run \ + --env-file letsencrypt.env \ + letsencrypt \ + dehydrated \ + --cron --domain letsencrypt.willfarrell.ca \ + --out /etc/ssl \ + --hook dehydrated-dns \ + --challenge dns-01 \ + --force + +# public +docker run -d \ + --volumes-from nginx_nginx_1 \ + --env-file letsencrypt.env \ + letsencrypt \ + dehydrated \ + --cron --domain letsencrypt.willfarrell.ca \ + --out /etc/ssl \ + --challenge http-01 \ + --force + +# reload nginx +docker exec -it nginx_nginx_1 /etc/scripts/make_hpkp && /etc/init.d/nginx reload +``` + +## Deploy +```bash +# private +docker run \ + --volumes-from nginx_nginx_1 \ + --env-file letsencrypt.env \ + letsencrypt \ + dehydrated \ + --cron --domain letsencrypt.willfarrell.ca \ + --out /etc/ssl \ + --hook dehydrated-dns \ + --challenge dns-01 + +# public +docker run -d \ + --volumes-from nginx_nginx_1 \ + --env-file letsencrypt.env \ + letsencrypt \ + dehydrated \ + --cron --domain letsencrypt.willfarrell.ca \ + --out /etc/ssl \ + --challenge http-01 +``` + +## TODO +- [ ] Update to python 3 (not-supported lexicon#68) +- [ ] TravisCI \ No newline at end of file diff --git a/config b/config new file mode 100644 index 0000000..1a066d9 --- /dev/null +++ b/config @@ -0,0 +1,88 @@ +######################################################## +# 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 # +######################################################## + +# Resolve names to addresses of IP version only. (curl) +# supported values: 4, 6 +# default: +#IP_VERSION= + +# Path to certificate authority (default: https://acme-v01.api.letsencrypt.org/directory) +CA="https://acme-staging.api.letsencrypt.org/directory" + +# Path to license agreement (default: https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf) +#LICENSE="https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf" + +# Which challenge should be used? Currently http-01 and dns-01 are supported +#CHALLENGETYPE="http-01" + +# 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: +#CONFIG_D= + +# 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="/etc/ssl" + +# 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="/var/www/.well-known/acme-challenge" + +# Default keysize for private keys (default: 4096) +#KEYSIZE="4096" + +# Path to openssl config file (default: - tries to figure out system default) +#OPENSSL_CNF= + +# 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: +#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="30" + +# Regenerate private keys instead of just signing new certificates on renewal (default: yes) +#PRIVATE_KEY_RENEW="yes" + +# 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=secp384r1 + +# E-mail to use during the registration (default: ) +#CONTACT_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="yes" \ No newline at end of file diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..c6ead1e --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e +#set -x +echo "run" +if [ "${1}" = 'dehydrated' ]; then + if [ "${LE_ENV}" == 'production' ]; then + sed -i 's@CA=.*@CA="https://acme-v01.api.letsencrypt.org/directory"@g' /etc/dehydrated/config + fi +fi + +echo "${@}" +exec "${@}" diff --git a/letsencrypt.env.sample b/letsencrypt.env.sample new file mode 100644 index 0000000..f3861f9 --- /dev/null +++ b/letsencrypt.env.sample @@ -0,0 +1,5 @@ +LE_ENV=staging + +PROVIDER=cloudflare +LEXICON_CLOUDFLARE_USERNAME= +LEXICON_CLOUDFLARE_TOKEN=