Matthias Andreas Benkard | b382b10 | 2021-01-02 15:32:21 +0100 | [diff] [blame] | 1 | #!/bin/bash |
| 2 | |
| 3 | log_f() { |
| 4 | if [[ ${2} == "no_nl" ]]; then |
| 5 | echo -n "$(date) - ${1}" |
| 6 | elif [[ ${2} == "no_date" ]]; then |
| 7 | echo "${1}" |
| 8 | elif [[ ${2} != "redis_only" ]]; then |
| 9 | echo "$(date) - ${1}" |
| 10 | fi |
| 11 | if [[ ${3} == "b64" ]]; then |
| 12 | ${REDIS_CMDLINE} LPUSH ACME_LOG "{\"time\":\"$(date +%s)\",\"message\":\"base64,$(printf '%s' "${MAILCOW_HOSTNAME} - ${1}")\"}" > /dev/null |
| 13 | else |
| 14 | ${REDIS_CMDLINE} LPUSH ACME_LOG "{\"time\":\"$(date +%s)\",\"message\":\"$(printf '%s' "${MAILCOW_HOSTNAME} - ${1}" | \ |
| 15 | tr '%&;$"[]{}-\r\n' ' ')\"}" > /dev/null |
| 16 | fi |
| 17 | } |
| 18 | |
| 19 | verify_hash_match(){ |
| 20 | CERT_HASH=$(openssl x509 -in "${1}" -noout -pubkey | openssl md5) |
| 21 | KEY_HASH=$(openssl pkey -in "${2}" -pubout | openssl md5) |
| 22 | if [[ ${CERT_HASH} != ${KEY_HASH} ]]; then |
| 23 | log_f "Certificate and key hashes do not match!" |
| 24 | return 1 |
| 25 | else |
| 26 | log_f "Verified hashes." |
| 27 | return 0 |
| 28 | fi |
| 29 | } |
| 30 | |
| 31 | get_ipv4(){ |
| 32 | local IPV4= |
| 33 | local IPV4_SRCS= |
| 34 | local TRY= |
| 35 | IPV4_SRCS[0]="ip4.mailcow.email" |
| 36 | IPV4_SRCS[1]="ip4.korves.net" |
| 37 | until [[ ! -z ${IPV4} ]] || [[ ${TRY} -ge 10 ]]; do |
| 38 | IPV4=$(curl --connect-timeout 3 -m 10 -L4s ${IPV4_SRCS[$RANDOM % ${#IPV4_SRCS[@]} ]} | grep -E "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$") |
| 39 | [[ ! -z ${TRY} ]] && sleep 1 |
| 40 | TRY=$((TRY+1)) |
| 41 | done |
| 42 | echo ${IPV4} |
| 43 | } |
| 44 | |
| 45 | get_ipv6(){ |
| 46 | local IPV6= |
| 47 | local IPV6_SRCS= |
| 48 | local TRY= |
| 49 | IPV6_SRCS[0]="ip6.korves.net" |
| 50 | IPV6_SRCS[1]="ip6.mailcow.email" |
| 51 | until [[ ! -z ${IPV6} ]] || [[ ${TRY} -ge 10 ]]; do |
| 52 | IPV6=$(curl --connect-timeout 3 -m 10 -L6s ${IPV6_SRCS[$RANDOM % ${#IPV6_SRCS[@]} ]} | grep "^\([0-9a-fA-F]\{0,4\}:\)\{1,7\}[0-9a-fA-F]\{0,4\}$") |
| 53 | [[ ! -z ${TRY} ]] && sleep 1 |
| 54 | TRY=$((TRY+1)) |
| 55 | done |
| 56 | echo ${IPV6} |
| 57 | } |
| 58 | |
| 59 | check_domain(){ |
| 60 | DOMAIN=$1 |
| 61 | A_DOMAIN=$(dig A ${DOMAIN} +short | tail -n 1) |
| 62 | AAAA_DOMAIN=$(dig AAAA ${DOMAIN} +short | tail -n 1) |
| 63 | # Check if CNAME without v6 enabled target |
| 64 | if [[ ! -z ${AAAA_DOMAIN} ]] && [[ -z $(echo ${AAAA_DOMAIN} | grep "^\([0-9a-fA-F]\{0,4\}:\)\{1,7\}[0-9a-fA-F]\{0,4\}$") ]]; then |
| 65 | AAAA_DOMAIN= |
| 66 | fi |
| 67 | if [[ ! -z ${AAAA_DOMAIN} ]]; then |
| 68 | log_f "Found AAAA record for ${DOMAIN}: ${AAAA_DOMAIN} - skipping A record check" |
| 69 | if [[ $(expand ${IPV6:-"0000:0000:0000:0000:0000:0000:0000:0000"}) == $(expand ${AAAA_DOMAIN}) ]] || [[ ${SKIP_IP_CHECK} == "y" ]] || [[ ${SNAT6_TO_SOURCE} != "n" ]]; then |
| 70 | if verify_challenge_path "${DOMAIN}" 6; then |
| 71 | log_f "Confirmed AAAA record with IP $(expand ${AAAA_DOMAIN})" |
| 72 | return 0 |
| 73 | else |
| 74 | log_f "Confirmed AAAA record with IP $(expand ${AAAA_DOMAIN}), but HTTP validation failed" |
| 75 | fi |
| 76 | else |
| 77 | log_f "Cannot match your IP $(expand ${IPV6:-"0000:0000:0000:0000:0000:0000:0000:0000"}) against hostname ${DOMAIN} (DNS returned $(expand ${AAAA_DOMAIN}))" |
| 78 | fi |
| 79 | elif [[ ! -z ${A_DOMAIN} ]]; then |
| 80 | log_f "Found A record for ${DOMAIN}: ${A_DOMAIN}" |
| 81 | if [[ ${IPV4:-ERR} == ${A_DOMAIN} ]] || [[ ${SKIP_IP_CHECK} == "y" ]] || [[ ${SNAT_TO_SOURCE} != "n" ]]; then |
| 82 | if verify_challenge_path "${DOMAIN}" 4; then |
| 83 | log_f "Confirmed A record ${A_DOMAIN}" |
| 84 | return 0 |
| 85 | else |
| 86 | log_f "Confirmed A record with IP ${A_DOMAIN}, but HTTP validation failed" |
| 87 | fi |
| 88 | else |
| 89 | log_f "Cannot match your IP ${IPV4} against hostname ${DOMAIN} (DNS returned ${A_DOMAIN})" |
| 90 | fi |
| 91 | else |
| 92 | log_f "No A or AAAA record found for hostname ${DOMAIN}" |
| 93 | fi |
| 94 | return 1 |
| 95 | } |
| 96 | |
| 97 | verify_challenge_path(){ |
| 98 | if [[ ${SKIP_HTTP_VERIFICATION} == "y" ]]; then |
| 99 | echo '(skipping check, returning 0)' |
| 100 | return 0 |
| 101 | fi |
| 102 | # verify_challenge_path URL 4|6 |
| 103 | RANDOM_N=${RANDOM}${RANDOM}${RANDOM} |
| 104 | echo ${RANDOM_N} > /var/www/acme/${RANDOM_N} |
| 105 | if [[ "$(curl --insecure -${2} -L http://${1}/.well-known/acme-challenge/${RANDOM_N} --silent)" == "${RANDOM_N}" ]]; then |
| 106 | rm /var/www/acme/${RANDOM_N} |
| 107 | return 0 |
| 108 | else |
| 109 | rm /var/www/acme/${RANDOM_N} |
| 110 | return 1 |
| 111 | fi |
| 112 | } |