diff --git a/dns_scripts/dns_add_cloudflare b/dns_scripts/dns_add_cloudflare index 4e3198e..7bc3ea9 100755 --- a/dns_scripts/dns_add_cloudflare +++ b/dns_scripts/dns_add_cloudflare @@ -1,44 +1,44 @@ #!/usr/bin/env bash +# need to add your email address and API key to cloudflare below or set as env variables +email=${CF_EMAIL:-''} +key=${CF_KEY:-''} -# need to add your email address and key to cloudflare below -email='' -key='' - -fulldomain="$1" -token="$2" +fulldomain="${1:?Need full domain name as first parameter}" +token="${2:?Need Let’s Encrypt challenge token as second parameter}" API='https://api.cloudflare.com/client/v4/zones' -AUTH=( -H "X-Auth-Email: $email" -H "X-Auth-Key: $key" -H "Content-Type: application/json" ) +PARAMS=( -H "X-Auth-Email: $email" -H "X-Auth-Key: $key" -H 'Content-Type: application/json' ) # get a list of all domain names from cloudflare -# If you have a lot, you may need add "&page=1&per_page=1000" -resp=$(curl --silent "${AUTH[@]}" -X GET "$API?match=all&status=active") +# If you have a lot, you may need add "&page=1&per_page=1000" and/or "&status=active" +resp=$(curl --silent "${PARAMS[@]}" -X GET "$API?match=all") + +re='"result":\[(([^][]*\[[^][]*])+[^][]*)]' # find result section +[[ "${resp// }" =~ $re ]] && resp="${BASH_REMATCH[1]}" +while [[ "$resp" ]]; do # iterate through domains returned + re='[^}{]*\{(([^}{]*\{[^}{]*})+[^}{]*)}(.*)' + [[ "$resp" =~ $re ]]; first="${BASH_REMATCH[1]}"; resp="${BASH_REMATCH[3]}" -# treat all names with dot as domain names -while read -d ' ' i; do - [[ $i =~ \"name\":\"([^\"]+\.[^\"]+)\" ]] && all_domains="${all_domains:+$all_domains }${BASH_REMATCH[1]}" -done <<<${resp//[ ,\[\{\}\]]/ } + # remove subsections - leave only domain level + while [[ "$first" =~ (.*)[\[\{][^]\{\}[]*[\]\}](.*) ]]; do first="${BASH_REMATCH[1]}${BASH_REMATCH[2]}"; done -[ -z "$all_domains" ] && { echo 'no active domains found on your cloudflare account'; exit 1; } + re='"name":"([^"]*)"'; [[ "$first" =~ $re ]] && domains=( "${domains[@]}" "${BASH_REMATCH[1]}" ) + re='"id":"([^"]*)"'; [[ "$first" =~ $re ]] && ids=( "${ids[@]}" "${BASH_REMATCH[1]}" ) +done -# select right CF domain (longest one) +# select right cloudflare domain (longest one) domain=$fulldomain. -while [[ "$domain" && ! "$all_domains" =~ "${domain%?}" ]]; do domain=${domain#*.}; done +# shellcheck disable=SC2076 +while [[ "$domain" && ! "${domains[@]/#/@}" == *"@${domain%?}"* ]]; do domain=${domain#*.}; done domain=${domain%?} - [ -z "$domain" ] && { echo 'domain name not found on your cloudflare account'; exit 1; } -resp=$(curl --silent "${AUTH[@]}" -X GET "$API?name=$domain&match=any&status=active") - -# select result section -[[ "$resp" =~ \"result\"[^\{]*\{([^\{\}]*\{[^\{\}]*\}[^\{\}]*)+\} ]] -resp="${BASH_REMATCH[0]%\}*}"; resp="${resp#*\{}" - -# remove subsections - leave only domain level -while [[ "$resp" =~ (.*)[\[\{][^]\{\}[]*[\]\}](.*) ]]; do resp="${BASH_REMATCH[1]}${BASH_REMATCH[2]}"; done - -# must match - we ask for already verified domain -[[ "${resp// }" =~ \"id\":\"([^\"]+)\" ]] -domain_id=${BASH_REMATCH[1]} +for i in "${!domains[@]}"; do [[ ${domains[i]} == "$domain" ]] && break; done +domain_id=${ids[i]} -curl --silent "${AUTH[@]}" -X POST "$API/$domain_id/dns_records" \ - --data "{\"type\":\"TXT\",\"name\":\"_acme-challenge.${fulldomain%.$domain}\",\"content\":\"$token\",\"ttl\":300}" +resp=$(curl --silent "${PARAMS[@]}" -X POST "$API/$domain_id/dns_records" \ + --data "{\"type\":\"TXT\",\"name\":\"_acme-challenge.${fulldomain%.$domain}\",\"content\":\"$token\",\"ttl\":300}") +# code 81057 = The record already exists. +if [[ "${resp// }" == *'"success":false'* && ! "${resp// }" == *'"code":81057[^0-9]'* ]]; then + re='"message":"([^"]+)"'; [[ "$resp" =~ $re ]] + echo "Error: DNS challenge not added: ${BASH_REMATCH[1]:-unknown error}"; exit 2 +fi