Skip to content

Commit 03d60ff

Browse files
authored
Merge pull request #534 from srvrco/test-multiple-domain
Pre-release 2.21
2 parents a5393a5 + 804c71f commit 03d60ff

16 files changed

+415
-95
lines changed

.github/workflows/run-all-tests.yml

+9-1
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,16 @@ jobs:
2121
- uses: actions/checkout@v1
2222
- name: Build the docker-compose stack
2323
run: docker-compose up -d --build
24-
- name: Run test suite on centos6
24+
- name: Run test suite on CentOS6
2525
run: test/run-test.sh centos6
26+
test-centos7:
27+
runs-on: ubuntu-latest
28+
steps:
29+
- uses: actions/checkout@v1
30+
- name: Build the docker-compose stack
31+
run: docker-compose up -d --build
32+
- name: Run test suite on CentOS7
33+
run: test/run-test.sh centos7
2634
test-centos7-duckdns:
2735
runs-on: ubuntu-latest
2836
steps:

dns_scripts/dns_add_challtestsrv

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
fulldomain="${1}"
55
token="${2}"
66

7-
curl -X POST -d "{\"host\":\"_acme-challenge.${fulldomain}.\", \"value\": \"${token}\"}" http://10.30.50.3:8055/set-txt
7+
curl --silent -X POST -d "{\"host\":\"_acme-challenge.${fulldomain}.\", \"value\": \"${token}\"}" http://10.30.50.3:8055/set-txt

getssl

+170-76
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,13 @@
214214
# 2020-02-13 Fix bug with copying to all locations when creating RSA and ECDSA certs (2.20)
215215
# 2020-02-22 Change sign_string to use openssl asn1parse (better fix for #424)
216216
# 2020-02-23 Add dig to config check for systems without drill (ubuntu)
217+
# 2020-03-11 Use dig +trace to find primary name server and improve dig parsing of CNAME
218+
# 2020-03-12 Fix bug with DNS validation and multiple domains (#524)
219+
# 2020-03-24 Find primary ns using all dns utils (dig, host, nslookup) (2.21)
217220
# ----------------------------------------------------------------------------------------
218221

219222
PROGNAME=${0##*/}
220-
VERSION="2.20"
223+
VERSION="2.21"
221224

222225
# defaults
223226
ACCOUNT_KEY_LENGTH=4096
@@ -233,7 +236,7 @@ CSR_SUBJECT="/"
233236
CURL_USERAGENT="${PROGNAME}/${VERSION}"
234237
DEACTIVATE_AUTH="false"
235238
DEFAULT_REVOKE_CA="https://acme-v02.api.letsencrypt.org"
236-
DNS_EXTRA_WAIT=""
239+
DNS_EXTRA_WAIT=60
237240
DNS_WAIT=10
238241
DOMAIN_KEY_LENGTH=4096
239242
DUAL_RSA_ECDSA="false"
@@ -749,13 +752,37 @@ create_order() {
749752
OrderLink=$(echo "$responseHeaders" | grep -i location | awk '{print $2}'| tr -d '\r\n ')
750753
debug "Order link $OrderLink"
751754
FinalizeLink=$(json_get "$response" "finalize")
752-
dn=0
753-
for d in $alldomains; do
754-
# get authorizations link
755-
AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "$d" "authorizations" "x")
756-
debug "authorizations link for $d - ${AuthLink[$dn]}"
757-
((dn++))
758-
done
755+
756+
if [[ $API -eq 1 ]]; then
757+
dn=0
758+
for d in $alldomains; do
759+
# get authorizations link
760+
AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "$d" "authorizations" "x")
761+
debug "authorizations link for $d - ${AuthLink[$dn]}"
762+
((dn++))
763+
done
764+
else
765+
# Authorization links are unsorted, so fetch the authorization link, find the domain, save response in the correct array position
766+
AuthLinks=$(json_get "$response" "authorizations")
767+
AuthLinkResponse=()
768+
AuthLinkResponseHeader=()
769+
for l in $AuthLinks; do
770+
debug "Requesting authorizations link for $l"
771+
send_signed_request "$l" ""
772+
# Get domain from response
773+
authdomain=$(json_get "$response" "identifier" "value")
774+
# find array position (This is O(n2) but that doubt we'll see performance issues)
775+
dn=0
776+
for d in $alldomains; do
777+
if [ "$d" == "$authdomain" ]; then
778+
debug "Saving authorization response for $authdomain for domain alldomains[$dn]"
779+
AuthLinkResponse[$dn]=$response
780+
AuthLinkResponseHeader[$dn]=$responseHeaders
781+
fi
782+
((dn++))
783+
done
784+
done
785+
fi
759786
}
760787

761788
date_epoc() { # convert the date into epoch time
@@ -800,6 +827,29 @@ error_exit() { # give error message on error exit
800827
exit 1
801828
}
802829

830+
find_dns_utils() {
831+
HAS_NSLOOKUP=false
832+
HAS_DIG_OR_DRILL=""
833+
HAS_HOST=false
834+
if [[ -n "$(command -v nslookup)" ]]; then
835+
debug "HAS NSLOOKUP=true"
836+
HAS_NSLOOKUP=true
837+
fi
838+
839+
if [[ -n "$(command -v drill)" ]]; then
840+
debug "HAS DIG_OR_DRILL=drill"
841+
HAS_DIG_OR_DRILL="drill"
842+
elif [[ -n "$(command -v dig)" ]]; then
843+
debug "HAS DIG_OR_DRILL=dig"
844+
HAS_DIG_OR_DRILL="dig"
845+
fi
846+
847+
if [[ -n "$(command -v host)" ]]; then
848+
debug "HAS HOST=true"
849+
HAS_HOST=true
850+
fi
851+
}
852+
803853
fulfill_challenges() {
804854
dn=0
805855
for d in $alldomains; do
@@ -822,7 +872,9 @@ for d in $alldomains; do
822872
error_exit "new-authz error: $response"
823873
fi
824874
else
825-
send_signed_request "${AuthLink[$dn]}" ""
875+
response=${AuthLinkResponse[$dn]}
876+
responseHeaders=${AuthLinkResponseHeader[$dn]}
877+
response_status=$(json_get "$response" status)
826878
fi
827879

828880
if [[ $response_status == "valid" ]]; then
@@ -840,16 +892,14 @@ for d in $alldomains; do
840892
if [[ $VALIDATE_VIA_DNS == "true" ]]; then # set up the correct DNS token for verification
841893
if [[ $API -eq 1 ]]; then
842894
# get the dns component of the ACME response
843-
# get the token from the dns component
895+
# get the token and uri from the dns component
844896
token=$(json_get "$response" "token" "dns-01")
845-
# get the uri from the dns component
846897
uri=$(json_get "$response" "uri" "dns-01")
847898
debug uri "$uri"
848899
else # APIv2
849900
debug "authlink response = $response"
850-
# get the token from the http-01 component
901+
# get the token and uri from the dns-01 component
851902
token=$(json_get "$response" "challenges" "type" "dns-01" "token")
852-
# get the uri from the http component
853903
uri=$(json_get "$response" "challenges" "type" "dns-01" "url")
854904
debug uri "$uri"
855905
fi
@@ -900,7 +950,6 @@ for d in $alldomains; do
900950
uri=$(json_get "$response" "uri" "http-01")
901951
debug uri "$uri"
902952
else # APIv2
903-
send_signed_request "${AuthLink[$dn]}" ""
904953
debug "authlink response = $response"
905954
# get the token from the http-01 component
906955
token=$(json_get "$response" "challenges" "type" "http-01" "token")
@@ -997,8 +1046,9 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then
9971046
| grep ^_acme -A2\
9981047
| grep '"'|awk -F'"' '{ print $2}')
9991048
elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then
1049+
debug "$DNS_CHECK_FUNC" TXT "_acme-challenge.${d}" "@${ns}"
10001050
check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${d}" "@${ns}" \
1001-
| grep 'IN TXT'|awk -F'"' '{ print $2}')
1051+
| grep 'IN\WTXT'|awk -F'"' '{ print $2}')
10021052
elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then
10031053
check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${d}" "${ns}" \
10041054
| grep 'descriptive text'|awk -F'"' '{ print $2}')
@@ -1053,10 +1103,11 @@ fi
10531103
}
10541104

10551105
get_auth_dns() { # get the authoritative dns server for a domain (sets primary_ns )
1056-
gad_d="$1" # domain name
1106+
orig_gad_d="$1" # domain name
10571107
gad_s="$PUBLIC_DNS_SERVER" # start with PUBLIC_DNS_SERVER
10581108

10591109
if [[ "$os" == "cygwin" ]]; then
1110+
gad_d="$orig_gad_d"
10601111
all_auth_dns_servers=$(nslookup -type=soa "${d}" ${PUBLIC_DNS_SERVER} 2>/dev/null \
10611112
| grep "primary name server" \
10621113
| awk '{print $NF}')
@@ -1067,85 +1118,125 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n
10671118
return
10681119
fi
10691120

1070-
if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then
1071-
if [[ -z "$gad_s" ]]; then #checking for CNAMEs
1072-
res=$($DNS_CHECK_FUNC CNAME "$gad_d"| grep "^$gad_d")
1073-
else
1074-
res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d")
1075-
fi
1076-
if [[ -n "$res" ]]; then # domain is a CNAME so get main domain
1077-
gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g')
1078-
fi
1079-
if [[ -z "$gad_s" ]]; then #checking for CNAMEs
1080-
res=$($DNS_CHECK_FUNC NS "$gad_d"| grep "^$gad_d")
1121+
if [[ -n "$HAS_DIG_OR_DRILL" ]]; then
1122+
gad_d="$orig_gad_d"
1123+
debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d @$gad_s" to find primary nameserver
1124+
# Use SOA +trace to find the name server
1125+
if [[ -z "$gad_s" ]]; then
1126+
res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" 2>/dev/null | grep "IN\WNS\W" | tail -1)
10811127
else
1082-
res=$($DNS_CHECK_FUNC NS "$gad_d" "@$gad_s"| grep "^$gad_d")
1128+
res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" "@$gad_s" 2>/dev/null | grep "IN\WNS\W" | tail -1)
10831129
fi
1130+
1131+
# fallback to existing code
10841132
if [[ -z "$res" ]]; then
1085-
error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config"
1086-
else
1087-
all_auth_dns_servers=$(echo "$res" | awk '$4 ~ "NS" {print $5}' | sed 's/\.$//g'|tr '\n' ' ')
1133+
debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d @$gad_s"
1134+
if [[ -z "$gad_s" ]]; then #checking for CNAMEs (need grep as dig 9.11 sometimes returns everything not just CNAME entries)
1135+
res=$($HAS_DIG_OR_DRILL CNAME "$gad_d"| grep "^$gad_d" | grep CNAME)
1136+
else
1137+
res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" "@$gad_s"| grep "^$gad_d" | grep CNAME)
1138+
fi
1139+
if [[ -n "$res" ]]; then # domain is a CNAME so get main domain
1140+
gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g')
1141+
debug Domain is a CNAME, actual domain is "$gad_d"
1142+
fi
1143+
# If gad_d is an A record then this returns the SOA for the root domain, e.g. without the www
1144+
# dig NS ubuntu.getssl.text
1145+
# > getssl.test. IN SOA ns1.duckdns.org
1146+
# If gad_d is a CNAME record then this returns the NS for the domain pointed to by $gad_d
1147+
# dig NS www.getssl.text
1148+
# > www.getssl.test. IN CNAME getssl.test
1149+
# > getssl.test. IN NS ns1.duckdns.org
1150+
debug Using "$HAS_DIG_OR_DRILL NS $gad_d @$gad_s" to find primary nameserver
1151+
if [[ -z "$gad_s" ]]; then
1152+
res=$($HAS_DIG_OR_DRILL NS "$gad_d"| grep -E "IN\W(NS|SOA)\W" | tail -1)
1153+
else
1154+
res=$($HAS_DIG_OR_DRILL NS "$gad_d" "@$gad_s"| grep -E "IN\W(NS|SOA)\W" | tail -1)
1155+
fi
10881156
fi
1089-
if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then
1090-
primary_ns="$all_auth_dns_servers"
1091-
else
1092-
primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}')
1157+
if [[ -n "$res" ]]; then
1158+
all_auth_dns_servers=$(echo "$res" | awk '$4 ~ "NS" {print $5}' | sed 's/\.$//g'|tr '\n' ' ')
1159+
if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then
1160+
primary_ns="$all_auth_dns_servers"
1161+
else
1162+
primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}')
1163+
fi
1164+
return
10931165
fi
1094-
return
10951166
fi
10961167

1097-
if [[ "$DNS_CHECK_FUNC" == "host" ]]; then
1168+
if [[ "$HAS_HOST" == true ]]; then
1169+
gad_d="$orig_gad_d"
1170+
debug Using "host -t NS" to find primary name server for "$gad_d"
10981171
if [[ -z "$gad_s" ]]; then
1099-
res=$($DNS_CHECK_FUNC -t NS "$gad_d"| grep "name server")
1172+
res=$(host -t NS "$gad_d"| grep "name server")
11001173
else
1101-
res=$($DNS_CHECK_FUNC -t NS "$gad_d" "$gad_s"| grep "name server")
1174+
res=$(host -t NS "$gad_d" "$gad_s"| grep "name server")
11021175
fi
1103-
if [[ -z "$res" ]]; then
1104-
error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config"
1105-
else
1176+
if [[ -n "$res" ]]; then
11061177
all_auth_dns_servers=$(echo "$res" | awk '{print $4}' | sed 's/\.$//g'|tr '\n' ' ')
1178+
if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then
1179+
primary_ns="$all_auth_dns_servers"
1180+
else
1181+
primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}')
1182+
fi
1183+
return
11071184
fi
1108-
if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then
1109-
primary_ns="$all_auth_dns_servers"
1110-
else
1111-
primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}')
1112-
fi
1113-
return
11141185
fi
11151186

1116-
res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s})
1187+
if [[ "$HAS_NSLOOKUP" == true ]]; then
1188+
gad_d="$orig_gad_d"
1189+
debug Using "nslookup -debug -type=soa -type=ns $gad_d $gad_s" to find primary name server
1190+
res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s})
1191+
1192+
if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then
1193+
# this is a Non-authoritative server, need to check for an authoritative one.
1194+
gad_s=$(echo "$res" | awk '$2 ~ "nameserver" {print $4; exit }' |sed 's/\.$//g')
1195+
if [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then
1196+
# if domain name doesn't exist, then find auth servers for next level up
1197+
gad_s=$(echo "$res" | awk '$1 ~ "origin" {print $3; exit }')
1198+
gad_d=$(echo "$res" | awk '$1 ~ "->" {print $2; exit}')
1199+
# handle scenario where awk returns nothing
1200+
if [[ -z "$gad_d" ]]; then
1201+
gad_d="$orig_gad_d"
1202+
fi
1203+
fi
11171204

1118-
if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then
1119-
# this is a Non-authoritative server, need to check for an authoritative one.
1120-
gad_s=$(echo "$res" | awk '$2 ~ "nameserver" {print $4; exit }' |sed 's/\.$//g')
1121-
if [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then
1122-
# if domain name doesn't exist, then find auth servers for next level up
1123-
gad_s=$(echo "$res" | awk '$1 ~ "origin" {print $3; exit }')
1124-
gad_d=$(echo "$res" | awk '$1 ~ "->" {print $2; exit}')
1205+
# shellcheck disable=SC2086
1206+
res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s})
11251207
fi
1126-
fi
11271208

1128-
if [[ -z "$gad_s" ]]; then
1129-
res=$(nslookup -debug -type=soa -type=ns "$gad_d")
1130-
else
1131-
res=$(nslookup -debug -type=soa -type=ns "$gad_d" "${gad_s}")
1132-
fi
1209+
if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then
1210+
gad_d=$(echo "$res" | awk ' $2 ~ "canonical" {print $5; exit }' |sed 's/\.$//g')
1211+
elif [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then
1212+
gad_s=$(echo "$res" | awk ' $1 ~ "origin" {print $3; exit }')
1213+
gad_d=$(echo "$res"| awk '$1 ~ "->" {print $2; exit}')
1214+
# handle scenario where awk returns nothing
1215+
if [[ -z "$gad_d" ]]; then
1216+
gad_d="$orig_gad_d"
1217+
fi
1218+
fi
11331219

1134-
if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then
1135-
gad_d=$(echo "$res" | awk ' $2 ~ "canonical" {print $5; exit }' |sed 's/\.$//g')
1136-
elif [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then
1137-
gad_s=$(echo "$res" | awk ' $1 ~ "origin" {print $3; exit }')
1138-
gad_d=$(echo "$res"| awk '$1 ~ "->" {print $2; exit}')
1139-
fi
1220+
# shellcheck disable=SC2086
1221+
# not quoting gad_s fixes the nslookup: couldn't get address for '': not found warning (#332)
1222+
all_auth_dns_servers=$(nslookup -debug -type=soa -type=ns "$gad_d" $gad_s \
1223+
| awk '$1 ~ "nameserver" {print $3}' \
1224+
| sed 's/\.$//g'| tr '\n' ' ')
11401225

1141-
all_auth_dns_servers=$(nslookup -type=soa -type=ns "$gad_d" "$gad_s" \
1142-
| awk ' $2 ~ "nameserver" {print $4}' \
1143-
| sed 's/\.$//g'| tr '\n' ' ')
1144-
if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then
1145-
primary_ns="$all_auth_dns_servers"
1146-
else
1147-
primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}')
1226+
if [[ -n "$all_auth_dns_servers" ]]; then
1227+
if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then
1228+
primary_ns="$all_auth_dns_servers"
1229+
else
1230+
primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}')
1231+
fi
1232+
return
1233+
fi
11481234
fi
1235+
1236+
# nslookup on alpine/ubuntu containers doesn't support -debug, print a warning in this case
1237+
# This means getssl cannot check that the DNS record has been updated on the primary name server
1238+
info "Warning: Couldn't find primary DNS server - please set PUBLIC_DNS_SERVER or AUTH_DNS_SERVER in config"
1239+
info "This means getssl cannot check the DNS entry has been updated"
11491240
}
11501241

11511242
get_certificate() { # get certificate for csr, if all domains validated.
@@ -2247,6 +2338,9 @@ set_server_type
22472338
# check config for typical errors.
22482339
check_config
22492340

2341+
# check what dns utils are installed
2342+
find_dns_utils
2343+
22502344
if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then
22512345
rm -f "$DOMAIN_DIR/FORCE_RENEWAL" || error_exit "problem deleting file $DOMAIN_DIR/FORCE_RENEWAL"
22522346
_FORCE_RENEW=1

0 commit comments

Comments
 (0)