From f88a72eedd2eb564270a59166eab53ea8dad3a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= Date: Tue, 26 Jun 2018 23:10:24 +0200 Subject: [PATCH] [ACME] Validate AAAA, if any, before A - fail on AAAA mismatch (LE prioritization) --- data/Dockerfiles/acme/Dockerfile | 1 + data/Dockerfiles/acme/docker-entrypoint.sh | 59 ++++++++-- data/Dockerfiles/acme/expand6.sh | 131 +++++++++++++++++++++ 3 files changed, 182 insertions(+), 9 deletions(-) create mode 100755 data/Dockerfiles/acme/expand6.sh diff --git a/data/Dockerfiles/acme/Dockerfile b/data/Dockerfiles/acme/Dockerfile index 2f2cb4e8..93ef124d 100644 --- a/data/Dockerfiles/acme/Dockerfile +++ b/data/Dockerfiles/acme/Dockerfile @@ -14,5 +14,6 @@ RUN apk add --update --no-cache \ tini COPY docker-entrypoint.sh /srv/docker-entrypoint.sh +COPY expand6.sh /srv/expand6.sh CMD ["/sbin/tini", "-g", "--", "/srv/docker-entrypoint.sh"] diff --git a/data/Dockerfiles/acme/docker-entrypoint.sh b/data/Dockerfiles/acme/docker-entrypoint.sh index ced251ae..2d619ee1 100755 --- a/data/Dockerfiles/acme/docker-entrypoint.sh +++ b/data/Dockerfiles/acme/docker-entrypoint.sh @@ -2,6 +2,9 @@ set -o pipefail exec 5>&1 +# Thanks to https://github.com/cvmiller -> https://github.com/cvmiller/expand6 +source /srv/expand6.sh + log_f() { if [[ ${2} == "no_nl" ]]; then echo -n "$(date) - ${1}" @@ -69,8 +72,8 @@ get_ipv4(){ IPV4_SRCS[2]="icanhazip.com" IPV4_SRCS[3]="v4.ident.me" IPV4_SRCS[4]="ipecho.net/plain" - IPV4_SRCS[5]="mailcow.email/ip.php" - until [[ ! -z ${IPV4} ]] || [[ ${TRY} -ge 100 ]]; do + IPV4_SRCS[5]="ip4.mailcow.email" + until [[ ! -z ${IPV4} ]] || [[ ${TRY} -ge 10 ]]; do 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]?)$") [[ ! -z ${TRY} ]] && sleep 1 TRY=$((TRY+1)) @@ -78,6 +81,22 @@ get_ipv4(){ echo ${IPV4} } +get_ipv6(){ + local IPV6= + local IPV6_SRCS= + local TRY= + IPV6_SRCS[0]="ifconfig.co" + IPV6_SRCS[1]="icanhazip.com" + IPV6_SRCS[2]="v6.ident.me" + IPV6_SRCS[3]="ip6.mailcow.email" + until [[ ! -z ${IPV6} ]] || [[ ${TRY} -ge 10 ]]; do + 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\}$") + [[ ! -z ${TRY} ]] && sleep 1 + TRY=$((TRY+1)) + done + echo ${IPV6} +} + [[ ! -f ${ACME_BASE}/dhparams.pem ]] && cp ${SSL_EXAMPLE}/dhparams.pem ${ACME_BASE}/dhparams.pem if [[ -f ${ACME_BASE}/cert.pem ]] && [[ -f ${ACME_BASE}/key.pem ]]; then @@ -112,8 +131,8 @@ else fi fi +log_f "Waiting for database... " while ! mysqladmin ping --host mysql -u${DBUSER} -p${DBPASS} --silent; do - echo "Waiting for database to come up..." sleep 2 done @@ -128,7 +147,11 @@ while true; do declare -a VALIDATED_CONFIG_DOMAINS declare -a ADDITIONAL_VALIDATED_SAN IFS=',' read -r -a ADDITIONAL_SAN_ARR <<< "${ADDITIONAL_SAN}" + log_f "Detecting IP addresses... " no_nl IPV4=$(get_ipv4) + IPV6=$(get_ipv6) + log_f "OK" no_date + # Container ids may have changed CONTAINERS_RESTART=($(curl --silent http://dockerapi:8080/containers/json | jq -r '.[] | {name: .Config.Labels["com.docker.compose.service"], id: .Id}' | jq -rc 'select( .name | tostring | contains("nginx-mailcow") or contains("postfix-mailcow") or contains("dovecot-mailcow")) | .id' | tr "\n" " ")) @@ -138,7 +161,7 @@ while true; do DOMAIN_TABLE=$(mysql -h mysql-mailcow -u ${DBUSER} -p${DBPASS} ${DBNAME} -e "SHOW TABLES LIKE 'domain'" -Bs) [[ -z ${DOMAIN_TABLE} ]] && sleep 10 done - log_f "Found domain tables." no_date + log_f "OK" no_date while read domains; do SQL_DOMAIN_ARR+=("${domains}") @@ -146,7 +169,16 @@ while true; do for SQL_DOMAIN in "${SQL_DOMAIN_ARR[@]}"; do A_CONFIG=$(dig A autoconfig.${SQL_DOMAIN} +short | tail -n 1) - if [[ ! -z ${A_CONFIG} ]]; then + AAAA_CONFIG=$(dig AAAA autoconfig.${SQL_DOMAIN} +short | tail -n 1) + if [[ ! -z ${AAAA_CONFIG} ]]; then + log_f "Found AAAA record for autoconfig.${SQL_DOMAIN}: ${AAAA_CONFIG} - skipping A record check" + if [[ $(expand ${IPV6:-"0000:0000:0000:0000:0000:0000:0000:0000"}) == $(expand ${AAAA_CONFIG}) ]] || [[ ${SKIP_IP_CHECK} == "y" ]]; then + log_f "Confirmed AAAA record autoconfig.${SQL_DOMAIN}" + VALIDATED_CONFIG_DOMAINS+=("autoconfig.${SQL_DOMAIN}") + else + log_f "Cannot match your IP ${IPV6:-NO_IPV6_LINK} against hostname autoconfig.${SQL_DOMAIN} ($(expand ${AAAA_CONFIG}))" + fi + elif [[ ! -z ${A_CONFIG} ]]; then log_f "Found A record for autoconfig.${SQL_DOMAIN}: ${A_CONFIG}" if [[ ${IPV4:-ERR} == ${A_CONFIG} ]] || [[ ${SKIP_IP_CHECK} == "y" ]]; then log_f "Confirmed A record autoconfig.${SQL_DOMAIN}" @@ -155,11 +187,20 @@ while true; do log_f "Cannot match your IP ${IPV4} against hostname autoconfig.${SQL_DOMAIN} (${A_CONFIG})" fi else - log_f "No A record for autoconfig.${SQL_DOMAIN} found" + log_f "No A or AAAA record found for hostname autoconfig.${SQL_DOMAIN}" fi - A_DISCOVER=$(dig A autodiscover.${SQL_DOMAIN} +short | tail -n 1) - if [[ ! -z ${A_DISCOVER} ]]; then + A_DISCOVER=$(dig A autodiscover.${SQL_DOMAIN} +short | tail -n 1) + AAAA_DISCOVER=$(dig AAAA autodiscover.${SQL_DOMAIN} +short | tail -n 1) + if [[ ! -z ${AAAA_DISCOVER} ]]; then + log_f "Found AAAA record for autodiscover.${SQL_DOMAIN}: ${AAAA_DISCOVER} - skipping A record check" + if [[ $(expand ${IPV6:-"0000:0000:0000:0000:0000:0000:0000:0000"}) == $(expand ${AAAA_DISCOVER}) ]] || [[ ${SKIP_IP_CHECK} == "y" ]]; then + log_f "Confirmed AAAA record autodiscover.${SQL_DOMAIN}" + VALIDATED_CONFIG_DOMAINS+=("autodiscover.${SQL_DOMAIN}") + else + log_f "Cannot match your IP ${IPV6:-NO_IPV6_LINK} against hostname autodiscover.${SQL_DOMAIN} ($(expand ${AAAA_DISCOVER}))" + fi + elif [[ ! -z ${A_DISCOVER} ]]; then log_f "Found A record for autodiscover.${SQL_DOMAIN}: ${A_DISCOVER}" if [[ ${IPV4:-ERR} == ${A_DISCOVER} ]] || [[ ${SKIP_IP_CHECK} == "y" ]]; then log_f "Confirmed A record autodiscover.${SQL_DOMAIN}" @@ -168,7 +209,7 @@ while true; do log_f "Cannot match your IP ${IPV4} against hostname autodiscover.${SQL_DOMAIN} (${A_DISCOVER})" fi else - log_f "No A record for autodiscover.${SQL_DOMAIN} found" + log_f "No A or AAAA record found for hostname autodiscover.${SQL_DOMAIN}" fi done diff --git a/data/Dockerfiles/acme/expand6.sh b/data/Dockerfiles/acme/expand6.sh new file mode 100755 index 00000000..a7817228 --- /dev/null +++ b/data/Dockerfiles/acme/expand6.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +################################################################################## +# +# Copyright (C) 2017 Craig Miller +# +# See the file "LICENSE" for information on usage and redistribution +# of this file, and for a DISCLAIMER OF ALL WARRANTIES. +# Distributed under GPLv2 License +# +################################################################################## + + +# IPv6 Address Expansion functions +# +# by Craig Miller 19 Feb 2017 +# +# 16 Nov 2017 v0.93 - added CLI functionality + + +VERSION=0.93 + +empty_addr="0000:0000:0000:0000:0000:0000:0000:0000" +empty_addr_len=${#empty_addr} + +function usage { + echo " $0 - expand compressed IPv6 addresss " + echo " e.g. $0 2001:db8:1:12:123::456 " + echo " " + echo " -t self test" + echo " " + echo " By Craig Miller - Version: $VERSION" + exit 1 + } + +if [ "$1" == "-h" ]; then + #call help + usage +fi + +# +# Expands IPv6 quibble to 4 digits with leading zeros e.g. db8 -> 0db8 +# +# Returns string with expanded quibble + +function expand_quibble() { + addr=$1 + # create array of quibbles + addr_array=(${addr//:/ }) + addr_array_len=${#addr_array[@]} + # step thru quibbles + for ((i=0; i< $addr_array_len ; i++ )) + do + quibble=${addr_array[$i]} + quibble_len=${#quibble} + case $quibble_len in + 1) quibble="000$quibble";; + 2) quibble="00$quibble";; + 3) quibble="0$quibble";; + esac + addr_array[$i]=$quibble + done + # reconstruct addr from quibbles + return_str=${addr_array[*]} + return_str="${return_str// /:}" + echo $return_str +} + +# +# Expands IPv6 address :: format to full zeros +# +# Returns string with expanded address + +function expand() { + if [[ $1 == *"::"* ]]; then + # check for leading zeros on front_addr + if [[ $1 == "::"* ]]; then + front_addr=0 + else + front_addr=$(echo $1 | sed -r 's;([^ ]+)::.*;\1;') + fi + # check for trailing zeros on back_addr + if [[ $1 == *"::" ]]; then + back_addr=0 + else + back_addr=$(echo $1 | sed -r 's;.*::([^ ]+);\1;') + fi + front_addr=$(expand_quibble $front_addr) + back_addr=$(expand_quibble $back_addr) + + new_addr=$empty_addr + front_addr_len=${#front_addr} + back_addr_len=${#back_addr} + # calculate fill needed + num_zeros=$(($empty_addr_len - $front_addr_len - $back_addr_len - 1)) + + #fill_str=${empty_addr[0]:0:$num_zeros} + new_addr="$front_addr:${empty_addr[0]:0:$num_zeros}$back_addr" + + # return expanded address + echo $new_addr + else + # return input with expandd quibbles + expand_quibble $1 + fi +} + +# self test - call with '-t' parameter +if [ "$1" == "-t" ]; then + # add address examples to test + expand fd11::1d70:cf84:18ef:d056 + expand 2a01::1 + expand fe80::f203:8cff:fe3f:f041 + expand 2001:db8:123::5 + expand 2001:470:ebbd:0:f203:8cff:fe3f:f041 + # special cases + expand ::1 + expand fd32:197d:3022:1101:: + exit 1 +fi + +# allow script to be sourced (with no arguements) +if [[ $1 != "" ]]; then + # validate input is an IPv6 address + if [[ $1 == *":"* ]]; then + expand $1 + else + echo "ERROR: unregcognized IPv6 address $1" + exit 1 + fi +fi