From 54b8289e08723382f899e6065619ed08e8732aee Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 26 Sep 2017 08:02:03 -0700 Subject: [PATCH] DuckDNS: Also generate a certificate with Let's Encrypt (0 config) (#181) * Initial Let's Encrypt + DuckDNS * Remove letsencrypt_duckdns * Add cert generation with lets encrypt to DuckDNS * Add that you have to accept terms * Update Dockerfile * Delete dehydrated.sh * Update Dockerfile * Update config.json * Update run.sh * Update hooks.sh * Update run.sh * Update hooks.sh * Update run.sh * Update config.json * Update hooks.sh * Update Dockerfile * Update run.sh * fix renew timer * Update run.sh * fix logic --- duckdns/Dockerfile | 8 +++++--- duckdns/config.json | 13 ++++++++++++- duckdns/hooks.sh | 32 +++++++++++++++++++++++++++++++ duckdns/run.sh | 46 +++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 duckdns/hooks.sh diff --git a/duckdns/Dockerfile b/duckdns/Dockerfile index 1da64a7..2bd91f3 100644 --- a/duckdns/Dockerfile +++ b/duckdns/Dockerfile @@ -5,10 +5,12 @@ FROM $BUILD_FROM ENV LANG C.UTF-8 # Setup base -RUN apk add --no-cache jq curl +RUN apk add --no-cache jq curl libressl \ + && curl -s -o /usr/bin/dehydrated https://raw.githubusercontent.com/lukas2511/dehydrated/v0.4.0/dehydrated \ + && chmod a+x /usr/bin/dehydrated # Copy data -COPY run.sh / -RUN chmod a+x /run.sh +COPY *.sh / +RUN chmod a+x /*.sh CMD [ "/run.sh" ] diff --git a/duckdns/config.json b/duckdns/config.json index d3fa32e..1b98ad4 100644 --- a/duckdns/config.json +++ b/duckdns/config.json @@ -1,17 +1,28 @@ { "name": "Duck DNS", - "version": "0.6", + "version": "1.0", "slug": "duckdns", "description": "Free Dynamic DNS (DynDNS or DDNS) service", "url": "https://home-assistant.io/addons/duckdns/", "startup": "services", "boot": "auto", + "map": ["ssl:rw"], "options": { + "lets_encrypt": { + "accept_terms": false, + "certfile": "fullchain.pem", + "keyfile": "privkey.pem" + }, "token": null, "domains": [null], "seconds": 300 }, "schema": { + "lets_encrypt": { + "accept_terms": "bool", + "certfile": "str", + "keyfile": "str" + }, "token": "str", "domains": ["str"], "seconds": "int" diff --git a/duckdns/hooks.sh b/duckdns/hooks.sh new file mode 100644 index 0000000..69ad3c1 --- /dev/null +++ b/duckdns/hooks.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +CONFIG_PATH=/data/options.json + +DOMAINS=$(jq --raw-output '.domains | join(",")' $CONFIG_PATH) +TOKEN=$(jq --raw-output '.token' $CONFIG_PATH) +CERTFILE=$(jq --raw-output '.lets_encrypt.certfile' $CONFIG_PATH) +KEYFILE=$(jq --raw-output '.lets_encrypt.keyfile' $CONFIG_PATH) + +case "$1" in + "deploy_challenge") + curl -s "https://www.duckdns.org/update?domains=$DOMAINS&token=$TOKEN&txt=$4" + ;; + "clean_challenge") + curl -s "https://www.duckdns.org/update?domains=$DOMAINS&token=$TOKEN&txt=removed&clear=true" + ;; + "deploy_cert") + cp -f "$5" "/ssl/$CERTFILE" + cp -f "$3" "/ssl/$KEYFILE" + ;; + "unchanged_cert") + ;; + "startup_hook") + ;; + "exit_hook") + ;; + *) + echo Unknown hook "${1}" + exit 0 + ;; +esac diff --git a/duckdns/run.sh b/duckdns/run.sh index c51ac85..01300cf 100644 --- a/duckdns/run.sh +++ b/duckdns/run.sh @@ -1,15 +1,57 @@ #!/bin/bash set -e +CERT_DIR=/data/letsencrypt +WORK_DIR=/data/workdir CONFIG_PATH=/data/options.json +# Let's encrypt +LE_TERMS=$(jq --raw-output '.lets_encrypt.accept_terms' $CONFIG_PATH) +LE_DOMAINS=$(jq --raw-output '.domains[]' $CONFIG_PATH) +LE_UPDATE="0" + +# DuckDNS TOKEN=$(jq --raw-output '.token' $CONFIG_PATH) DOMAINS=$(jq --raw-output '.domains | join(",")' $CONFIG_PATH) WAIT_TIME=$(jq --raw-output '.seconds' $CONFIG_PATH) +# Function that performe a renew +function le_renew() { + local domain_args=() + + # Prepare domain for Let's Encrypt + for domain in $LE_DOMAINS; do + domain_args+=("--domain" "$domain") + done + + dehydrated --cron --hook ./hooks.sh --challenge dns-01 "${domain_args[@]}" --out "$CERT_DIR" --config "$WORK_DIR/config" || true + LE_UPDATE="$(date +%s)" +} + +# Register/generate certificate if terms accepted +if [ "$LE_TERMS" == "true" ]; then + # Init folder structs + mkdir -p "$CERT_DIR" + mkdir -p "$WORK_DIR" + + # Generate new certs + if [ ! -d "$CERT_DIR/live" ]; then + # Create empty dehydrated config file so that this dir will be used for storage + touch "$WORK_DIR/config" + + dehydrated --register --accept-terms --config "$WORK_DIR/config" + fi +fi + # Run duckdns while true; do - ANSWER="$(curl -sk "https://www.duckdns.org/update?domains=$DOMAINS&token=$TOKEN&ip=&verbose=true")" || true - echo "$(date): $ANSWER" + answer="$(curl -sk "https://www.duckdns.org/update?domains=$DOMAINS&token=$TOKEN&ip=&verbose=true")" || true + echo "$(date): $answer" + + now="$(date +%s)" + if [ "$LE_TERMS" == "true" ] && [ $((now - LE_UPDATE)) -ge 43200 ]; then + le_renew + fi + sleep "$WAIT_TIME" done