diff --git a/README.md b/README.md new file mode 100644 index 0000000..7cbbeb6 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# certbot-DNS-challenge-hooks + +Simple scripts I use to auto renew my Let's encrypt wildcard SSL cert. + +But use [acme.sh](https://github.com/Neilpang/acme.sh) is always recommended. + +## Usage + +- Apply for a certificate use `certbot` and `dns-01` challenge +- Download this repo +- open `config.sh` of this repo, fill the `CLOUDFLARE_KEY` and `CLOUDFLARE_EMAIL` variables +- install `jq` package from your system package manager (apt, yum, etc) +- Add a crontab job (as root) as bellow: + +```bash +certbot renew --manual-auth-hook="/path/to/cloned/repo/cloudflare-update-dns.sh" --manual-cleanup-hook="/path/to/cloned/repo/cloudflare-clean-dns.sh" --post-hook="systemctl reload nginx" >> /path/to/log/crontab.renew.log +``` + +## Example + +```bash +sudo certbot renew --manual-auth-hook="/etc/letsencrypt/scripts/cloudflare-update-dns.sh" --manual-cleanup-hook="/etc/letsencrypt/scripts/cloudflare-clean-dns.sh" --post-hook="systemctl reload nginx" --dry-run + +Saving debug log to /var/log/letsencrypt/letsencrypt.log + +------------------------------------------------------------------------------- +Processing /etc/letsencrypt/renewal/7sdre.am.conf +------------------------------------------------------------------------------- +Cert not due for renewal, but simulating renewal for dry run +Plugins selected: Authenticator manual, Installer None +Renewing an existing certificate +Performing the following challenges: +dns-01 challenge for 7sdre.am +dns-01 challenge for 7sdre.am +Output from cloudflare-update-dns.sh: +CHALLENGE_DOMAIN: _acme-challenge.7sdre.am +CHALLENGE_VALUE: q_nAln58kJ0M8iaih2dWo_jMlYwogj_iIBQjj2LEIeU +DNS_SERVER: 8.8.8.8 +ZONE: d3ea0d3d2fa87bbd2915d6ce869e5f47 +Add record result: true +DNS records have not been propagate, sleep 10s... +DNS record have been propagated, finish + +Output from cloudflare-update-dns.sh: +CHALLENGE_DOMAIN: _acme-challenge.7sdre.am +CHALLENGE_VALUE: anaxLO1rs2XSTlDwOXiMSdgazBsr8erX5l-Tdx5KRAI +DNS_SERVER: 8.8.8.8 +ZONE: d3ea0d3d2fa87bbd2915d6ce869e5f47 +Add record result: true +DNS records have not been propagate, sleep 10s... +DNS records have not been propagate, sleep 10s... +DNS record have been propagated, finish + +Waiting for verification... +Cleaning up challenges +Output from cloudflare-clean-dns.sh: +DOMAIN: _acme-challenge.7sdre.am +ZONE: d3ea0d3d2fa87bbd2915d6ce869e5f47 +a0ba72127de4db56eea3541491cdbd36 +clean: a0ba72127de4db56eea3541491cdbd36 +true +clean: c11557b6f7766cc2749a36864a14584c +true +clean: 2191aed49f646c5c64209be36a2d3788 +true + +Output from cloudflare-clean-dns.sh: +DOMAIN: _acme-challenge.7sdre.am +ZONE: d3ea0d3d2fa87bbd2915d6ce869e5f47 + + + +------------------------------------------------------------------------------- +new certificate deployed without reload, fullchain is +/etc/letsencrypt/live/7sdre.am/fullchain.pem +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +** DRY RUN: simulating 'certbot renew' close to cert expiry +** (The test certificates below have not been saved.) + +Congratulations, all renewals succeeded. The following certs have been renewed: + /etc/letsencrypt/live/7sdre.am/fullchain.pem (success) +** DRY RUN: simulating 'certbot renew' close to cert expiry +** (The test certificates above have not been saved.) +------------------------------------------------------------------------------- +Running post-hook command: systemctl reload nginx +``` + +## LICENSE + +WTFPL diff --git a/cloudflare-clean-dns.sh b/cloudflare-clean-dns.sh new file mode 100755 index 0000000..0208944 --- /dev/null +++ b/cloudflare-clean-dns.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +DIR="$(dirname "$0")" +source "$DIR/config.sh" + +echo "DOMAIN: ${CHALLENGE_DOMAIN}" +echo "ZONE: ${CLOUDFLARE_ZONE}" + +records=($(curl -X GET "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE}/dns_records?type=TXT&name=${CHALLENGE_DOMAIN}&page=1&per_page=100" \ + -H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \ + -H "X-Auth-Key: ${CLOUDFLARE_KEY}" \ + -H "Content-Type: application/json" -s | jq -r ".result[].id")) + +echo "${records}" + +for record in "${records[@]}"; do + echo "clean: $record" + curl -X DELETE "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE}/dns_records/${record}" \ + -H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \ + -H "X-Auth-Key: ${CLOUDFLARE_KEY}" \ + -H "Content-Type: application/json" -s | jq -r "[.success, .errors[].message] | @csv" +done diff --git a/cloudflare-update-dns.sh b/cloudflare-update-dns.sh new file mode 100755 index 0000000..228f860 --- /dev/null +++ b/cloudflare-update-dns.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +DIR="$(dirname "$0")" +source "$DIR/config.sh" + +DNS_SERVER=8.8.8.8 + +echo "CHALLENGE_DOMAIN: ${CHALLENGE_DOMAIN}" +echo "CHALLENGE_VALUE: ${CERTBOT_VALIDATION}" +echo "DNS_SERVER: ${DNS_SERVER}" +echo "ZONE: ${CLOUDFLARE_ZONE}" + +ADD_RECORD_RESULT=$(curl -X POST "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE}/dns_records" \ + -H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \ + -H "X-Auth-Key: ${CLOUDFLARE_KEY}" \ + -H "Content-Type: application/json" \ + --data "{\"type\":\"TXT\",\"name\":\"_acme-challenge\",\"content\":\"${CERTBOT_VALIDATION}\", \"ttl\": \"120\"}" -s | jq -r "[.success, .errors[].message] | @csv") + +echo "Add record result: ${ADD_RECORD_RESULT}" + +if [[ ! $(echo "${ADD_RECORD_RESULT}" | grep "true") ]]; then + echo "Add record failed, exit" + exit 1 +fi + +while true; do + records=$(dig -t TXT ${CHALLENGE_DOMAIN} @${DNS_SERVER} +noall +answer +short | grep "${CERTBOT_VALIDATION}") + if [[ ${records} ]]; then + break + fi + echo "DNS records have not been propagate, sleep 10s..." + sleep 10 +done + +echo "DNS record have been propagated, finish" + diff --git a/config.sh b/config.sh new file mode 100644 index 0000000..c2e24fe --- /dev/null +++ b/config.sh @@ -0,0 +1,10 @@ +CLOUDFLARE_KEY= +CLOUDFLARE_EMAIL= + +CHALLENGE_PREFIX="_acme-challenge" +CHALLENGE_DOMAIN="${CHALLENGE_PREFIX}.${CERTBOT_DOMAIN}" + +CLOUDFLARE_ZONE=$(curl -X GET "https://api.cloudflare.com/client/v4/zones?name=${CERTBOT_DOMAIN}" \ + -H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \ + -H "X-Auth-Key: ${CLOUDFLARE_KEY}" \ + -H "Content-Type: application/json" -s | jq -r '.result[0].id')