From 9361765285a597b50a4f010d690dfee0e253a021 Mon Sep 17 00:00:00 2001 From: MrMeeb Date: Sun, 12 May 2024 20:18:34 +0000 Subject: [PATCH 1/4] Add support for alerting on success or failure of renewal Installs apprise and allows config of alerting for successful or failed renewals. Currently only supports renewals, not inital issuing. Not necessarily a problem as I'd hope you'd be watching the logs on first run. I don't love how I create /config/.renew-list.sh. I should readdress this somehow --- Dockerfile | 7 ++-- requirements.txt | 5 +-- root/certbot-prepare.sh | 73 ++++++++++++++++++++++++++++++++--------- root/certbot-renew.sh | 6 ---- root/container-init.sh | 8 ++++- 5 files changed, 73 insertions(+), 26 deletions(-) delete mode 100644 root/certbot-renew.sh diff --git a/Dockerfile b/Dockerfile index 055e060..8edfeac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,9 @@ ENV TZ=UTC ENV GENERATE_DHPARAM=true ENV INTERVAL="0 */6 * * *" ENV ONE_SHOT=false +ENV APPRISE_URL= +ENV NOTIFY_ON_FAILURE=false +ENV NOTIFY_ON_SUCCESS=false # Single domain ENV DOMAINS= @@ -62,14 +65,14 @@ RUN apk add --no-cache --virtual .deps gcc python3-dev libc-dev libffi-dev && \ /app/certbot/bin/pip install wheel setuptools && \ /app/certbot/bin/pip install "Cython<3.0" pyyaml --no-build-isolation && \ /app/certbot/bin/pip install -r /app/certbot/requirements.txt && \ - ln -s /app/certbot/bin/certbot /usr/bin/certbot &&\ + ln -s /app/certbot/bin/certbot /usr/bin/certbot && \ + ln -s /app/certbot/bin/apprise /usr/bin/apprise && \ apk del .deps COPY root / RUN chmod +x /container-init.sh && \ chmod +x /certbot-prepare.sh && \ - chmod +x /certbot-renew.sh && \ chmod +x /check-one-shot.sh && \ chown -R ${PUID}:${PGID} /app /config diff --git a/requirements.txt b/requirements.txt index f26379d..cb0d741 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ -# For pinning Certbot packages to then be parsed by Renovate +# For pinning Python packages to then be parsed by Renovate certbot ==2.10.0 -certbot-dns-cloudflare ==2.10.0 \ No newline at end of file +certbot-dns-cloudflare ==2.10.0 +apprise ==1.8.0 \ No newline at end of file diff --git a/root/certbot-prepare.sh b/root/certbot-prepare.sh index 950a130..2464296 100644 --- a/root/certbot-prepare.sh +++ b/root/certbot-prepare.sh @@ -46,9 +46,48 @@ function better_exit { } +# Check APPRISE_URL is set if either NOTIFY_ON_SUCCESS or NOTIFY_ON_FAILURE are set +if [ ! -z "${NOTIFY_ON_SUCCESS}" ] || [ ! -z "${NOTIFY_ON_FAILURE}" ] && [ -z "${APPRISE_URL}" ]; then + + echo "You have notifications enabled but have not set APPRISE_URL. Please set APPRISE_URL and restart the container." + better_exit + +fi + # Cleanup renew list and create it fresh, ready for commands to be run and added -echo "#!/command/with-contenv bash" > /config/.renew-list.sh -echo "" >> /config/.renew-list.sh +echo "#!/command/with-contenv bash + +date +echo \"Attempting to renew certificates\" +function renew() { + + #Variables: + + #\$1 = Certbot command + + RENEWAL_DOMAINS=\$(echo \$1 | sed -r 's/.*\s-d\s(\S*).*/\1/') + CUSTOM_CA_PATH=\$(echo \$1 | sed -r 's/REQUESTS_CA_BUNDLE=(\S*)\s(.*)/\1/') + CERTBOT_COMMAND=\$(echo \$1 | sed -r 's/REQUESTS_CA_BUNDLE=(\S*)\s(.*)/\2/') + + echo \"Renewing certificate for \${RENEWAL_DOMAINS}\" + + echo \"REQUESTS_CA_BUNDLE=\${CUSTOM_CA_PATH} \${CERTBOT_COMMAND}\" | bash + + if [ \$? = 0 ]; then + echo \"Renewal attempt of certificate for \${RENEWAL_DOMAINS} succeeded\" + if [ \"\${NOTIFY_ON_SUCCESS}\" = \"true\" ]; then + apprise -b \"Renewal of certificate for \${RENEWAL_DOMAINS} succeeded\" \${APPRISE_URL} + fi + else + echo \"Renewal attempt of certificate for \${RENEWAL_DOMAINS} failed\" + if [ \"\${NOTIFY_ON_FAILURE}\" = \"true\" ]; then + apprise -b \"Renewal of certificate for \${RENEWAL_DOMAINS} failed\" \${APPRISE_URL} + fi + fi + +} +" > /config/.renew-list.sh +chmod +x /config/.renew-list.sh # Create original config file to track changes to environmental variables if [ ! -f /config/.donoteditthisfile ] @@ -306,14 +345,14 @@ function single_domain { echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY" ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --staging # Add to renewal list - echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --staging" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --staging\"" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" elif [ $STAGING = false ] then echo "Using production endpoint" ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini # Add to renewal list - echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds $PROPOGATION_TIME --dns-cloudflare-credentials /config/.secrets/cloudflare.ini\"" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" else echo "Unrecognised option for STAGING variable - check your configuration" @@ -332,14 +371,14 @@ function single_domain { echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY" REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --standalone --staging # Add to renewal list - echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --standalone --staging" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --standalone --staging\"" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" elif [ $STAGING = false ] then echo "Using production endpoint" REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --standalone # Add to renewal list - echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --standalone" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --standalone\"" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" else echo "Unrecognised option for STAGING variable - check your configuration" @@ -358,14 +397,14 @@ function single_domain { echo "Using staging endpoint - THIS SHOULD BE USED FOR TESTING ONLY" REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot --staging # Add to renewal list - echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot --staging" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot --staging\"" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" elif [ $STAGING = false ] then echo "Using production endpoint" REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot # Add to renewal list - echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot\"" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" else echo "Unrecognised option for STAGING variable - check your configuration" @@ -631,7 +670,7 @@ echo \ ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds ${PROPOGATION_TIME_MULTI} --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --staging # Add to renewal list echo "## Certificate ${x}" >> /config/.renew-list.sh - echo "${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds ${PROPOGATION_TIME_MULTI} --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --staging" >> /config/.renew-list.sh + echo "renew \"${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds ${PROPOGATION_TIME_MULTI} --dns-cloudflare-credentials /config/.secrets/cloudflare.ini --staging\"" >> /config/.renew-list.sh echo "" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" elif [ ${STAGING_MULTI} = false ] @@ -640,7 +679,7 @@ echo \ ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds ${PROPOGATION_TIME_MULTI} --dns-cloudflare-credentials /config/.secrets/cloudflare.ini # Add to renewal list echo "## Certificate ${x}" >> /config/.renew-list.sh - echo "REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds ${PROPOGATION_TIME_MULTI} --dns-cloudflare-credentials /config/.secrets/cloudflare.ini" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=$CUSTOM_CA_PATH ${BASE_COMMAND[@]} --dns-cloudflare --dns-cloudflare-propagation-seconds ${PROPOGATION_TIME_MULTI} --dns-cloudflare-credentials /config/.secrets/cloudflare.ini\"" >> /config/.renew-list.sh echo "" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" else @@ -661,7 +700,7 @@ echo \ REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --standalone --staging # Add to renewal list echo "## Certificate ${x}" >> /config/.renew-list.sh - echo "REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --standalone --staging" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --standalone --staging\"" >> /config/.renew-list.sh echo "" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" elif [ ${STAGING_MULTI} = false ] @@ -670,7 +709,7 @@ echo \ REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --standalone # Add to renewal list echo "## Certificate ${x}" >> /config/.renew-list.sh - echo "REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --standalone" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --standalone\"" >> /config/.renew-list.sh echo "" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" else @@ -691,7 +730,7 @@ echo \ REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot --staging # Add to renewal list echo "## Certificate ${x}" >> /config/.renew-list.sh - echo "REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot --staging" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot --staging\"" >> /config/.renew-list.sh echo "" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" elif [ ${STAGING_MULTI} = false ] @@ -700,7 +739,7 @@ echo \ REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot # Add to renewal list echo "## Certificate ${x}" >> /config/.renew-list.sh - echo "REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot" >> /config/.renew-list.sh + echo "renew \"REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH_MULTI} ${BASE_COMMAND[@]} --webroot --webroot-path /config/webroot\"" >> /config/.renew-list.sh echo "" >> /config/.renew-list.sh echo "Creation/renewal attempt complete" else @@ -754,6 +793,10 @@ else echo "CERT_COUNT varaible not recognised. It needs to be a value of 1 or greater." fi +# Finish /config/.renew-list.sh now all certs have been added +echo " +echo \"Renewal attempts complete\"" >> /config/.renew-list.sh + if [ $GENERATE_DHPARAM = true ] && [ ! -s /config/letsencrypt/keys/ssl-dhparams.pem ] then echo "" @@ -769,7 +812,7 @@ if [ $ONE_SHOT == "true" ]; then elif [ $ONE_SHOT == "false" ]; then - echo "$INTERVAL /certbot-renew.sh >> /config/logs/renew.log" > /config/.crontab.txt + echo "$INTERVAL /config/.renew-list.sh >> /config/logs/renew.log" > /config/.crontab.txt echo "" diff --git a/root/certbot-renew.sh b/root/certbot-renew.sh deleted file mode 100644 index acedd1c..0000000 --- a/root/certbot-renew.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/command/with-contenv bash -# shellcheck shell=bash -echo '' -date -echo "Attempting to renew certificates" -bash /config/.renew-list.sh \ No newline at end of file diff --git a/root/container-init.sh b/root/container-init.sh index 87406fa..b7b1f86 100644 --- a/root/container-init.sh +++ b/root/container-init.sh @@ -31,7 +31,13 @@ TZ=${TZ} ONE_SHOT=${ONE_SHOT} INTERVAL=${INTERVAL} GENERATE_DHPARAM=${GENERATE_DHPARAM} -CERT_COUNT=${CERT_COUNT}" +CERT_COUNT=${CERT_COUNT} +NOTIFY_ON_SUCCESS=${NOTIFY_ON_SUCCESS} +NOTIFY_ON_FAILURE=${NOTIFY_ON_FAILURE}" +if [ ! -z ${APPRISE_URL} ]; then +echo \ +"APPRISE_URL=[hidden]" +fi ## Send extra detail to logs if single certificate config if [ ${CERT_COUNT} == 1 ]; then echo \ From 074c152b1eaadd6971c5e0080befba9db8f57f25 Mon Sep 17 00:00:00 2001 From: MrMeeb Date: Sun, 12 May 2024 20:21:17 +0000 Subject: [PATCH 2/4] Add new env vars to readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 25c3564..8d66f82 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,9 @@ Core options to the container | GENERATE_DHPARAM | true (case-sensitive) | Generate Diffie-Hellman keys in /config/letsencrypt/keys | | INTERVAL | 0 */6 * * * | How often certbot attempts to renew the certificate. Cron syntax | | CERT_COUNT | 1 | How many certificates certbot will try to issue. [Details here](https://git.mrmeeb.stream/MrMeeb/certbot-cron-docker#multiple-certificates) | +| APPRISE_URL | None | URL for Apprise notifications. [Syntax](https://github.com/caronc/apprise?tab=readme-ov-file#supported-notifications) +| NOTIFY_ON_SUCCESS | false | Notify on a successful renewal attempt. Note that this isn't just when the cert is renewed, but on every renewal attempt. | +| NOTIFY_ON_FAILURE | false | Notify on a failed renewal attempt. ### Certificate Options From 222b8f86a49f88acad7324b765796a92c511fcdc Mon Sep 17 00:00:00 2001 From: MrMeeb Date: Thu, 30 May 2024 19:26:57 +0000 Subject: [PATCH 3/4] Move renewal function to its own file --- Dockerfile | 4 +--- root/certbot-prepare.sh | 28 +--------------------------- root/renew-function.sh | 27 +++++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 30 deletions(-) create mode 100644 root/renew-function.sh diff --git a/Dockerfile b/Dockerfile index 8edfeac..a0b96dd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -71,9 +71,7 @@ RUN apk add --no-cache --virtual .deps gcc python3-dev libc-dev libffi-dev && \ COPY root / -RUN chmod +x /container-init.sh && \ - chmod +x /certbot-prepare.sh && \ - chmod +x /check-one-shot.sh && \ +RUN chmod +x /container-init.sh /certbot-prepare.sh /check-one-shot.sh /renew-function.sh && \ chown -R ${PUID}:${PGID} /app /config ENTRYPOINT [ "/init" ] diff --git a/root/certbot-prepare.sh b/root/certbot-prepare.sh index 2464296..6c4d93f 100644 --- a/root/certbot-prepare.sh +++ b/root/certbot-prepare.sh @@ -59,33 +59,7 @@ echo "#!/command/with-contenv bash date echo \"Attempting to renew certificates\" -function renew() { - - #Variables: - - #\$1 = Certbot command - - RENEWAL_DOMAINS=\$(echo \$1 | sed -r 's/.*\s-d\s(\S*).*/\1/') - CUSTOM_CA_PATH=\$(echo \$1 | sed -r 's/REQUESTS_CA_BUNDLE=(\S*)\s(.*)/\1/') - CERTBOT_COMMAND=\$(echo \$1 | sed -r 's/REQUESTS_CA_BUNDLE=(\S*)\s(.*)/\2/') - - echo \"Renewing certificate for \${RENEWAL_DOMAINS}\" - - echo \"REQUESTS_CA_BUNDLE=\${CUSTOM_CA_PATH} \${CERTBOT_COMMAND}\" | bash - - if [ \$? = 0 ]; then - echo \"Renewal attempt of certificate for \${RENEWAL_DOMAINS} succeeded\" - if [ \"\${NOTIFY_ON_SUCCESS}\" = \"true\" ]; then - apprise -b \"Renewal of certificate for \${RENEWAL_DOMAINS} succeeded\" \${APPRISE_URL} - fi - else - echo \"Renewal attempt of certificate for \${RENEWAL_DOMAINS} failed\" - if [ \"\${NOTIFY_ON_FAILURE}\" = \"true\" ]; then - apprise -b \"Renewal of certificate for \${RENEWAL_DOMAINS} failed\" \${APPRISE_URL} - fi - fi - -} +source /renew-function.sh " > /config/.renew-list.sh chmod +x /config/.renew-list.sh diff --git a/root/renew-function.sh b/root/renew-function.sh new file mode 100644 index 0000000..6f342b0 --- /dev/null +++ b/root/renew-function.sh @@ -0,0 +1,27 @@ +function renew() { + + #Variables: + + #$1 = Certbot command + + RENEWAL_DOMAINS=$(echo $1 | sed -r 's/.*\s-d\s(\S*).*/\1/') + CUSTOM_CA_PATH=$(echo $1 | sed -r 's/REQUESTS_CA_BUNDLE=(\S*)\s(.*)/\1/') + CERTBOT_COMMAND=$(echo $1 | sed -r 's/REQUESTS_CA_BUNDLE=(\S*)\s(.*)/\2/') + + echo "Renewing certificate for ${RENEWAL_DOMAINS}" + + echo "REQUESTS_CA_BUNDLE=${CUSTOM_CA_PATH} ${CERTBOT_COMMAND}" | bash + + if [ $? = 0 ]; then + echo "Renewal attempt of certificate for ${RENEWAL_DOMAINS} succeeded" + if [ "${NOTIFY_ON_SUCCESS}" = "true" ]; then + apprise -b "Renewal of certificate for ${RENEWAL_DOMAINS} succeeded" ${APPRISE_URL} + fi + else + echo "Renewal attempt of certificate for ${RENEWAL_DOMAINS} failed" + if [ "${NOTIFY_ON_FAILURE}" = "true" ]; then + apprise -b "Renewal of certificate for ${RENEWAL_DOMAINS} failed" ${APPRISE_URL} + fi + fi + +} \ No newline at end of file From 5bda2c108106348a8065eb4ea8c3d60382463754 Mon Sep 17 00:00:00 2001 From: MrMeeb Date: Thu, 30 May 2024 19:55:39 +0000 Subject: [PATCH 4/4] Correct detection of missing APPRISE_URL --- root/certbot-prepare.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/root/certbot-prepare.sh b/root/certbot-prepare.sh index 6c4d93f..7931fc0 100644 --- a/root/certbot-prepare.sh +++ b/root/certbot-prepare.sh @@ -47,7 +47,7 @@ function better_exit { } # Check APPRISE_URL is set if either NOTIFY_ON_SUCCESS or NOTIFY_ON_FAILURE are set -if [ ! -z "${NOTIFY_ON_SUCCESS}" ] || [ ! -z "${NOTIFY_ON_FAILURE}" ] && [ -z "${APPRISE_URL}" ]; then +if [ "${NOTIFY_ON_SUCCESS}" = "true" ] || [ "${NOTIFY_ON_FAILURE}" = "true" ] && [ -z "${APPRISE_URL}" ]; then echo "You have notifications enabled but have not set APPRISE_URL. Please set APPRISE_URL and restart the container." better_exit