diff --git a/.github/workflows/run-all-tests.yml b/.github/workflows/run-all-tests.yml index e6d80ed..1ff795d 100644 --- a/.github/workflows/run-all-tests.yml +++ b/.github/workflows/run-all-tests.yml @@ -21,8 +21,16 @@ jobs: - uses: actions/checkout@v1 - name: Build the docker-compose stack run: docker-compose up -d --build - - name: Run test suite on centos6 + - name: Run test suite on CentOS6 run: test/run-test.sh centos6 + test-centos7: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Build the docker-compose stack + run: docker-compose up -d --build + - name: Run test suite on CentOS7 + run: test/run-test.sh centos7 test-centos7-duckdns: runs-on: ubuntu-latest steps: diff --git a/dns_scripts/dns_add_challtestsrv b/dns_scripts/dns_add_challtestsrv index 601bcfc..98444b5 100755 --- a/dns_scripts/dns_add_challtestsrv +++ b/dns_scripts/dns_add_challtestsrv @@ -4,4 +4,4 @@ fulldomain="${1}" token="${2}" -curl -X POST -d "{\"host\":\"_acme-challenge.${fulldomain}.\", \"value\": \"${token}\"}" http://10.30.50.3:8055/set-txt +curl --silent -X POST -d "{\"host\":\"_acme-challenge.${fulldomain}.\", \"value\": \"${token}\"}" http://10.30.50.3:8055/set-txt diff --git a/getssl b/getssl index b870e68..c6fe0e9 100755 --- a/getssl +++ b/getssl @@ -214,7 +214,10 @@ # 2020-02-13 Fix bug with copying to all locations when creating RSA and ECDSA certs (2.20) # 2020-02-22 Change sign_string to use openssl asn1parse (better fix for #424) # 2020-02-23 Add dig to config check for systems without drill (ubuntu) -# 2020-03-23 Fix staging server URL in domain template +# 2020-03-11 Use dig +trace to find primary name server and improve dig parsing of CNAME +# 2020-03-12 Fix bug with DNS validation and multiple domains (#524) +# 2020-03-24 Find primary ns using all dns utils (dig, host, nslookup) +# 2020-03-23 Fix staging server URL in domain template (2.21) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} @@ -234,7 +237,7 @@ CSR_SUBJECT="/" CURL_USERAGENT="${PROGNAME}/${VERSION}" DEACTIVATE_AUTH="false" DEFAULT_REVOKE_CA="https://acme-v02.api.letsencrypt.org" -DNS_EXTRA_WAIT="" +DNS_EXTRA_WAIT=60 DNS_WAIT=10 DOMAIN_KEY_LENGTH=4096 DUAL_RSA_ECDSA="false" @@ -750,13 +753,37 @@ create_order() { OrderLink=$(echo "$responseHeaders" | grep -i location | awk '{print $2}'| tr -d '\r\n ') debug "Order link $OrderLink" FinalizeLink=$(json_get "$response" "finalize") - dn=0 - for d in $alldomains; do - # get authorizations link - AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "$d" "authorizations" "x") - debug "authorizations link for $d - ${AuthLink[$dn]}" - ((dn++)) - done + + if [[ $API -eq 1 ]]; then + dn=0 + for d in $alldomains; do + # get authorizations link + AuthLink[$dn]=$(json_get "$response" "identifiers" "value" "$d" "authorizations" "x") + debug "authorizations link for $d - ${AuthLink[$dn]}" + ((dn++)) + done + else + # Authorization links are unsorted, so fetch the authorization link, find the domain, save response in the correct array position + AuthLinks=$(json_get "$response" "authorizations") + AuthLinkResponse=() + AuthLinkResponseHeader=() + for l in $AuthLinks; do + debug "Requesting authorizations link for $l" + send_signed_request "$l" "" + # Get domain from response + authdomain=$(json_get "$response" "identifier" "value") + # find array position (This is O(n2) but that doubt we'll see performance issues) + dn=0 + for d in $alldomains; do + if [ "$d" == "$authdomain" ]; then + debug "Saving authorization response for $authdomain for domain alldomains[$dn]" + AuthLinkResponse[$dn]=$response + AuthLinkResponseHeader[$dn]=$responseHeaders + fi + ((dn++)) + done + done + fi } date_epoc() { # convert the date into epoch time @@ -801,6 +828,29 @@ error_exit() { # give error message on error exit exit 1 } +find_dns_utils() { + HAS_NSLOOKUP=false + HAS_DIG_OR_DRILL="" + HAS_HOST=false + if [[ -n "$(command -v nslookup)" ]]; then + debug "HAS NSLOOKUP=true" + HAS_NSLOOKUP=true + fi + + if [[ -n "$(command -v drill)" ]]; then + debug "HAS DIG_OR_DRILL=drill" + HAS_DIG_OR_DRILL="drill" + elif [[ -n "$(command -v dig)" ]]; then + debug "HAS DIG_OR_DRILL=dig" + HAS_DIG_OR_DRILL="dig" + fi + + if [[ -n "$(command -v host)" ]]; then + debug "HAS HOST=true" + HAS_HOST=true + fi +} + fulfill_challenges() { dn=0 for d in $alldomains; do @@ -823,7 +873,9 @@ for d in $alldomains; do error_exit "new-authz error: $response" fi else - send_signed_request "${AuthLink[$dn]}" "" + response=${AuthLinkResponse[$dn]} + responseHeaders=${AuthLinkResponseHeader[$dn]} + response_status=$(json_get "$response" status) fi if [[ $response_status == "valid" ]]; then @@ -841,16 +893,14 @@ for d in $alldomains; do if [[ $VALIDATE_VIA_DNS == "true" ]]; then # set up the correct DNS token for verification if [[ $API -eq 1 ]]; then # get the dns component of the ACME response - # get the token from the dns component + # get the token and uri from the dns component token=$(json_get "$response" "token" "dns-01") - # get the uri from the dns component uri=$(json_get "$response" "uri" "dns-01") debug uri "$uri" else # APIv2 debug "authlink response = $response" - # get the token from the http-01 component + # get the token and uri from the dns-01 component token=$(json_get "$response" "challenges" "type" "dns-01" "token") - # get the uri from the http component uri=$(json_get "$response" "challenges" "type" "dns-01" "url") debug uri "$uri" fi @@ -901,7 +951,6 @@ for d in $alldomains; do uri=$(json_get "$response" "uri" "http-01") debug uri "$uri" else # APIv2 - send_signed_request "${AuthLink[$dn]}" "" debug "authlink response = $response" # get the token from the http-01 component token=$(json_get "$response" "challenges" "type" "http-01" "token") @@ -998,8 +1047,9 @@ if [[ $VALIDATE_VIA_DNS == "true" ]]; then | grep ^_acme -A2\ | grep '"'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then + debug "$DNS_CHECK_FUNC" TXT "_acme-challenge.${d}" "@${ns}" check_result=$($DNS_CHECK_FUNC TXT "_acme-challenge.${d}" "@${ns}" \ - | grep 'IN TXT'|awk -F'"' '{ print $2}') + | grep 'IN\WTXT'|awk -F'"' '{ print $2}') elif [[ "$DNS_CHECK_FUNC" == "host" ]]; then check_result=$($DNS_CHECK_FUNC -t TXT "_acme-challenge.${d}" "${ns}" \ | grep 'descriptive text'|awk -F'"' '{ print $2}') @@ -1054,10 +1104,11 @@ fi } get_auth_dns() { # get the authoritative dns server for a domain (sets primary_ns ) - gad_d="$1" # domain name + orig_gad_d="$1" # domain name gad_s="$PUBLIC_DNS_SERVER" # start with PUBLIC_DNS_SERVER if [[ "$os" == "cygwin" ]]; then + gad_d="$orig_gad_d" all_auth_dns_servers=$(nslookup -type=soa "${d}" ${PUBLIC_DNS_SERVER} 2>/dev/null \ | grep "primary name server" \ | awk '{print $NF}') @@ -1068,85 +1119,125 @@ get_auth_dns() { # get the authoritative dns server for a domain (sets primary_n return fi - if [[ "$DNS_CHECK_FUNC" == "drill" ]] || [[ "$DNS_CHECK_FUNC" == "dig" ]]; then - if [[ -z "$gad_s" ]]; then #checking for CNAMEs - res=$($DNS_CHECK_FUNC CNAME "$gad_d"| grep "^$gad_d") - else - res=$($DNS_CHECK_FUNC CNAME "$gad_d" "@$gad_s"| grep "^$gad_d") - fi - if [[ -n "$res" ]]; then # domain is a CNAME so get main domain - gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g') - fi - if [[ -z "$gad_s" ]]; then #checking for CNAMEs - res=$($DNS_CHECK_FUNC NS "$gad_d"| grep "^$gad_d") + if [[ -n "$HAS_DIG_OR_DRILL" ]]; then + gad_d="$orig_gad_d" + debug Using "$HAS_DIG_OR_DRILL SOA +trace +nocomments $gad_d @$gad_s" to find primary nameserver + # Use SOA +trace to find the name server + if [[ -z "$gad_s" ]]; then + res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" 2>/dev/null | grep "IN\WNS\W" | tail -1) else - res=$($DNS_CHECK_FUNC NS "$gad_d" "@$gad_s"| grep "^$gad_d") + res=$($HAS_DIG_OR_DRILL SOA +trace +nocomments "$gad_d" "@$gad_s" 2>/dev/null | grep "IN\WNS\W" | tail -1) fi + + # fallback to existing code if [[ -z "$res" ]]; then - error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" - else - all_auth_dns_servers=$(echo "$res" | awk '$4 ~ "NS" {print $5}' | sed 's/\.$//g'|tr '\n' ' ') + debug Checking for CNAME using "$HAS_DIG_OR_DRILL CNAME $gad_d @$gad_s" + if [[ -z "$gad_s" ]]; then #checking for CNAMEs (need grep as dig 9.11 sometimes returns everything not just CNAME entries) + res=$($HAS_DIG_OR_DRILL CNAME "$gad_d"| grep "^$gad_d" | grep CNAME) + else + res=$($HAS_DIG_OR_DRILL CNAME "$gad_d" "@$gad_s"| grep "^$gad_d" | grep CNAME) + fi + if [[ -n "$res" ]]; then # domain is a CNAME so get main domain + gad_d=$(echo "$res"| awk '{print $5}' |sed 's/\.$//g') + debug Domain is a CNAME, actual domain is "$gad_d" + fi + # If gad_d is an A record then this returns the SOA for the root domain, e.g. without the www + # dig NS ubuntu.getssl.text + # > getssl.test. IN SOA ns1.duckdns.org + # If gad_d is a CNAME record then this returns the NS for the domain pointed to by $gad_d + # dig NS www.getssl.text + # > www.getssl.test. IN CNAME getssl.test + # > getssl.test. IN NS ns1.duckdns.org + debug Using "$HAS_DIG_OR_DRILL NS $gad_d @$gad_s" to find primary nameserver + if [[ -z "$gad_s" ]]; then + res=$($HAS_DIG_OR_DRILL NS "$gad_d"| grep -E "IN\W(NS|SOA)\W" | tail -1) + else + res=$($HAS_DIG_OR_DRILL NS "$gad_d" "@$gad_s"| grep -E "IN\W(NS|SOA)\W" | tail -1) + fi fi - if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then - primary_ns="$all_auth_dns_servers" - else - primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + if [[ -n "$res" ]]; then + all_auth_dns_servers=$(echo "$res" | awk '$4 ~ "NS" {print $5}' | sed 's/\.$//g'|tr '\n' ' ') + if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then + primary_ns="$all_auth_dns_servers" + else + primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + fi + return fi - return fi - if [[ "$DNS_CHECK_FUNC" == "host" ]]; then + if [[ "$HAS_HOST" == true ]]; then + gad_d="$orig_gad_d" + debug Using "host -t NS" to find primary name server for "$gad_d" if [[ -z "$gad_s" ]]; then - res=$($DNS_CHECK_FUNC -t NS "$gad_d"| grep "name server") + res=$(host -t NS "$gad_d"| grep "name server") else - res=$($DNS_CHECK_FUNC -t NS "$gad_d" "$gad_s"| grep "name server") + res=$(host -t NS "$gad_d" "$gad_s"| grep "name server") fi - if [[ -z "$res" ]]; then - error_exit "couldn't find primary DNS server - please set AUTH_DNS_SERVER in config" - else + if [[ -n "$res" ]]; then all_auth_dns_servers=$(echo "$res" | awk '{print $4}' | sed 's/\.$//g'|tr '\n' ' ') + if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then + primary_ns="$all_auth_dns_servers" + else + primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + fi + return fi - if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then - primary_ns="$all_auth_dns_servers" - else - primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') - fi - return fi - res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) + if [[ "$HAS_NSLOOKUP" == true ]]; then + gad_d="$orig_gad_d" + debug Using "nslookup -debug -type=soa -type=ns $gad_d $gad_s" to find primary name server + res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) + + if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then + # this is a Non-authoritative server, need to check for an authoritative one. + gad_s=$(echo "$res" | awk '$2 ~ "nameserver" {print $4; exit }' |sed 's/\.$//g') + if [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then + # if domain name doesn't exist, then find auth servers for next level up + gad_s=$(echo "$res" | awk '$1 ~ "origin" {print $3; exit }') + gad_d=$(echo "$res" | awk '$1 ~ "->" {print $2; exit}') + # handle scenario where awk returns nothing + if [[ -z "$gad_d" ]]; then + gad_d="$orig_gad_d" + fi + fi - if [[ "$(echo "$res" | grep -c "Non-authoritative")" -gt 0 ]]; then - # this is a Non-authoritative server, need to check for an authoritative one. - gad_s=$(echo "$res" | awk '$2 ~ "nameserver" {print $4; exit }' |sed 's/\.$//g') - if [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then - # if domain name doesn't exist, then find auth servers for next level up - gad_s=$(echo "$res" | awk '$1 ~ "origin" {print $3; exit }') - gad_d=$(echo "$res" | awk '$1 ~ "->" {print $2; exit}') + # shellcheck disable=SC2086 + res=$(nslookup -debug -type=soa -type=ns "$gad_d" ${gad_s}) fi - fi - if [[ -z "$gad_s" ]]; then - res=$(nslookup -debug -type=soa -type=ns "$gad_d") - else - res=$(nslookup -debug -type=soa -type=ns "$gad_d" "${gad_s}") - fi + if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then + gad_d=$(echo "$res" | awk ' $2 ~ "canonical" {print $5; exit }' |sed 's/\.$//g') + elif [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then + gad_s=$(echo "$res" | awk ' $1 ~ "origin" {print $3; exit }') + gad_d=$(echo "$res"| awk '$1 ~ "->" {print $2; exit}') + # handle scenario where awk returns nothing + if [[ -z "$gad_d" ]]; then + gad_d="$orig_gad_d" + fi + fi - if [[ "$(echo "$res" | grep -c "canonical name")" -gt 0 ]]; then - gad_d=$(echo "$res" | awk ' $2 ~ "canonical" {print $5; exit }' |sed 's/\.$//g') - elif [[ "$(echo "$res" | grep -c "an't find")" -gt 0 ]]; then - gad_s=$(echo "$res" | awk ' $1 ~ "origin" {print $3; exit }') - gad_d=$(echo "$res"| awk '$1 ~ "->" {print $2; exit}') - fi + # shellcheck disable=SC2086 + # not quoting gad_s fixes the nslookup: couldn't get address for '': not found warning (#332) + all_auth_dns_servers=$(nslookup -debug -type=soa -type=ns "$gad_d" $gad_s \ + | awk '$1 ~ "nameserver" {print $3}' \ + | sed 's/\.$//g'| tr '\n' ' ') - all_auth_dns_servers=$(nslookup -type=soa -type=ns "$gad_d" "$gad_s" \ - | awk ' $2 ~ "nameserver" {print $4}' \ - | sed 's/\.$//g'| tr '\n' ' ') - if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then - primary_ns="$all_auth_dns_servers" - else - primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + if [[ -n "$all_auth_dns_servers" ]]; then + if [[ $CHECK_ALL_AUTH_DNS == "true" ]]; then + primary_ns="$all_auth_dns_servers" + else + primary_ns=$(echo "$all_auth_dns_servers" | awk '{print $1}') + fi + return + fi fi + + # nslookup on alpine/ubuntu containers doesn't support -debug, print a warning in this case + # This means getssl cannot check that the DNS record has been updated on the primary name server + info "Warning: Couldn't find primary DNS server - please set PUBLIC_DNS_SERVER or AUTH_DNS_SERVER in config" + info "This means getssl cannot check the DNS entry has been updated" } get_certificate() { # get certificate for csr, if all domains validated. @@ -2248,6 +2339,9 @@ set_server_type # check config for typical errors. check_config +# check what dns utils are installed +find_dns_utils + if [[ -e "$DOMAIN_DIR/FORCE_RENEWAL" ]]; then rm -f "$DOMAIN_DIR/FORCE_RENEWAL" || error_exit "problem deleting file $DOMAIN_DIR/FORCE_RENEWAL" _FORCE_RENEW=1 diff --git a/test/2-simple-dns01-nslookup.bats b/test/2-simple-dns01-nslookup.bats index f92d817..482be2a 100644 --- a/test/2-simple-dns01-nslookup.bats +++ b/test/2-simple-dns01-nslookup.bats @@ -8,12 +8,16 @@ load '/getssl/test/test_helper.bash' # This is run for every test setup() { export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt - mv /usr/bin/dig /usr/bin/dig.getssl.bak + if [ -f /usr/bin/dig ]; then + mv /usr/bin/dig /usr/bin/dig.getssl.bak + fi } teardown() { - mv /usr/bin/dig.getssl.bak /usr/bin/dig + if [ -f /usr/bin/dig.getssl.bak ]; then + mv /usr/bin/dig.getssl.bak /usr/bin/dig + fi } @@ -30,5 +34,6 @@ teardown() { assert_output --partial "nslookup" refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' # don't fail for :error:badNonce - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + # don't check for "Warnings:" as there might be a warning message if nslookup doesn't support -debug (alpine/ubuntu) + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' } diff --git a/test/7-duckdns-dns01.bats b/test/7-duckdns-dns01-dig.bats similarity index 85% rename from test/7-duckdns-dns01.bats rename to test/7-duckdns-dns01-dig.bats index 0c680ea..c6d8f56 100644 --- a/test/7-duckdns-dns01.bats +++ b/test/7-duckdns-dns01-dig.bats @@ -6,7 +6,7 @@ load '/getssl/test/test_helper.bash' -@test "Create new certificate using staging server and DuckDNS" { +@test "Create new certificate using staging server, dig and DuckDNS" { if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi @@ -21,7 +21,7 @@ load '/getssl/test/test_helper.bash' refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' } -@test "Force renewal of certificate using staging server and DuckDNS" { +@test "Force renewal of certificate using staging server, dig and DuckDNS" { if [ -z "$STAGING" ]; then skip "Running internal tests, skipping external test" fi diff --git a/test/7-duckdns-dns01-nslookup.bats b/test/7-duckdns-dns01-nslookup.bats new file mode 100644 index 0000000..81c921e --- /dev/null +++ b/test/7-duckdns-dns01-nslookup.bats @@ -0,0 +1,49 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + if [ -f /usr/bin/dig ]; then + mv /usr/bin/dig /usr/bin/dig.getssl.bak + fi +} + + +teardown() { + if [ -f /usr/bin/dig.getssl.bak ]; then + mv /usr/bin/dig.getssl.bak /usr/bin/dig + fi +} + + +@test "Create new certificate using staging server, nslookup and DuckDNS" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + CONFIG_FILE="getssl-duckdns01.cfg" + + setup_environment + init_getssl + create_certificate + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings +} + + +@test "Force renewal of certificate using staging server, nslookup and DuckDNS" { + if [ -z "$STAGING" ]; then + skip "Running internal tests, skipping external test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings + cleanup_environment +} diff --git a/test/8-duckdns-ecdsa.bats b/test/8-duckdns-ecdsa.bats index dfe84fe..2e10512 100644 --- a/test/8-duckdns-ecdsa.bats +++ b/test/8-duckdns-ecdsa.bats @@ -20,7 +20,7 @@ load '/getssl/test/test_helper.bash' assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings } @@ -32,7 +32,7 @@ load '/getssl/test/test_helper.bash' assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' # ignore nslookup warnings cleanup_environment } @@ -50,7 +50,7 @@ load '/getssl/test/test_helper.bash' assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' } @@ -62,7 +62,7 @@ load '/getssl/test/test_helper.bash' assert_success refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' refute_output --regexp '[^:][Ee][Rr][Rr][Oo][Rr][^:]' - refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg][^:]' cleanup_environment } diff --git a/test/9-multiple-domains-dns01.bats b/test/9-multiple-domains-dns01.bats new file mode 100644 index 0000000..2a9344f --- /dev/null +++ b/test/9-multiple-domains-dns01.bats @@ -0,0 +1,66 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# This is run for every test +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt +} + + +@test "Create certificates for multi-level domains using DNS-01 verification" { + # This tests we can create a certificate for .getssl.test and getssl.test (in SANS) + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-multiple-domains-dns01.cfg" + setup_environment + + # Add top level domain from SANS to DNS + curl --silent -X POST -d '{"host":"getssl.test", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + + init_getssl + create_certificate + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} + + +@test "Force renewal of multi-level domains using DNS-01" { + # This tests we can renew a certificate for .getssl.test and getssl.test (in SANS) + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + run ${CODE_DIR}/getssl -f $GETSSL_HOST + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' + # Remove all the dns aliases + cleanup_environment + curl --silent -X POST -d '{"host":"getssl.tst"}' http://10.30.50.3:8055/clear-a +} + +@test "Test IGNORE_DIRECTORY_DOMAIN using DNS-01 verification" { + # This tests we can create a certificate for getssl.test and .getssl.test (*both* in SANS) + if [ -n "$STAGING" ]; then + skip "Using staging server, skipping internal test" + fi + CONFIG_FILE="getssl-ignore-directory-domain.cfg" + setup_environment + + # Add top level domain from SANS to DNS + curl --silent -X POST -d '{"host":"getssl.test", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + + init_getssl + create_certificate + assert_success + refute_output --regexp '[Ff][Aa][Ii][Ll][Ee][Dd]' + refute_output --regexp '[Ee][Rr][Rr][Oo][Rr]' + refute_output --regexp '[Ww][Aa][Rr][Nn][Ii][Nn][Gg]' +} diff --git a/test/Dockerfile-centos7 b/test/Dockerfile-centos7 new file mode 100644 index 0000000..8a34bc5 --- /dev/null +++ b/test/Dockerfile-centos7 @@ -0,0 +1,20 @@ +FROM centos:centos7 + +# Note this image uses drill, does not have dig or nslookup installed + +# Update and install required software +RUN yum -y update +RUN yum -y install epel-release +RUN yum -y install git curl ldns wget which nginx + +WORKDIR /root +RUN mkdir /etc/nginx/pki +RUN mkdir /etc/nginx/pki/private +COPY ./test/test-config/nginx-ubuntu-no-ssl /etc/nginx/conf.d/default.conf +COPY ./test/test-config/nginx-centos7.conf /etc/nginx/nginx.conf + +# BATS (Bash Automated Testings) +RUN git clone https://github.com/bats-core/bats-core.git /bats-core +RUN git clone https://github.com/jasonkarns/bats-support /bats-support +RUN git clone https://github.com/jasonkarns/bats-assert-1 /bats-assert +RUN /bats-core/install.sh /usr/local diff --git a/test/debug-test.sh b/test/debug-test.sh index 890366b..ac94b53 100755 --- a/test/debug-test.sh +++ b/test/debug-test.sh @@ -10,7 +10,7 @@ if [ $# -eq 2 ]; then fi #shellcheck disable=SC1091 -source /getssl/test/test_helper.bash +source /getssl/test/test_helper.bash 3>&1 CONFIG_FILE=$1 if [ ! -e "$CONFIG_FILE" ]; then diff --git a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg index 543c201..8f29088 100644 --- a/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg +++ b/test/test-config/getssl-dns01-dual-rsa-ecdsa.cfg @@ -7,7 +7,8 @@ CA="https://pebble:14000/dir" VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_challtestsrv" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_challtestsrv" -AUTH_DNS_SERVER=10.30.50.3 +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" DUAL_RSA_ECDSA="true" ACCOUNT_KEY_TYPE="prime256v1" diff --git a/test/test-config/getssl-dns01.cfg b/test/test-config/getssl-dns01.cfg index 7e26b98..0b816b1 100644 --- a/test/test-config/getssl-dns01.cfg +++ b/test/test-config/getssl-dns01.cfg @@ -7,7 +7,8 @@ CA="https://pebble:14000/dir" VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_challtestsrv" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_challtestsrv" -AUTH_DNS_SERVER=10.30.50.3 +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" # Additional domains - this could be multiple domains / subdomains in a comma separated list SANS="" diff --git a/test/test-config/getssl-duckdns01.cfg b/test/test-config/getssl-duckdns01.cfg index 517aaeb..8c12ee1 100644 --- a/test/test-config/getssl-duckdns01.cfg +++ b/test/test-config/getssl-duckdns01.cfg @@ -5,8 +5,8 @@ CA="https://acme-staging-v02.api.letsencrypt.org/directory" VALIDATE_VIA_DNS=true DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_duckdns" DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_duckdns" -AUTH_DNS_SERVER=1.1.1.1 -CHECK_ALL_AUTH_DNS=false +PUBLIC_DNS_SERVER=ns2.duckdns.org +CHECK_ALL_AUTH_DNS=true DNS_EXTRA_WAIT=60 ACCOUNT_KEY_TYPE="rsa" diff --git a/test/test-config/getssl-ignore-directory-domain.cfg b/test/test-config/getssl-ignore-directory-domain.cfg new file mode 100644 index 0000000..9777891 --- /dev/null +++ b/test/test-config/getssl-ignore-directory-domain.cfg @@ -0,0 +1,39 @@ +# Uncomment and modify any variables you need +# see https://github.com/srvrco/getssl/wiki/Config-variables for details +# see https://github.com/srvrco/getssl/wiki/Example-config-files for example configs +# +CA="https://pebble:14000/dir" + +VALIDATE_VIA_DNS=true +DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_challtestsrv" +DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_challtestsrv" +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" + +# Ignore directory domain (i.e. the domain passed on the command line), and just use the domains in the SANS list +IGNORE_DIRECTORY_DOMAIN="true" +SANS="getssl.test,$GETSSL_HOST" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +ACL=( + '/var/www/html/.well-known/acme-challenge' + '/var/www/html/.well-known/acme-challenge' +) + +#Set USE_SINGLE_ACL="true" to use a single ACL for all checks +USE_SINGLE_ACL="false" + +# Location for all your certs, these can either be on the server (full path name) +# or using ssh /sftp as for the ACL +DOMAIN_CERT_LOCATION="/etc/nginx/pki/server.crt" +DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.key" +CA_CERT_LOCATION="/etc/nginx/pki/chain.crt" +DOMAIN_CHAIN_LOCATION="" # this is the domain cert and CA cert +DOMAIN_PEM_LOCATION="" # this is the domain_key, domain cert and CA cert + +# The command needed to reload apache / nginx or whatever you use +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test-config/getssl-multiple-domains-dns01.cfg b/test/test-config/getssl-multiple-domains-dns01.cfg new file mode 100644 index 0000000..8754677 --- /dev/null +++ b/test/test-config/getssl-multiple-domains-dns01.cfg @@ -0,0 +1,38 @@ +# Uncomment and modify any variables you need +# see https://github.com/srvrco/getssl/wiki/Config-variables for details +# see https://github.com/srvrco/getssl/wiki/Example-config-files for example configs +# +CA="https://pebble:14000/dir" + +VALIDATE_VIA_DNS=true +DNS_ADD_COMMAND="/getssl/dns_scripts/dns_add_challtestsrv" +DNS_DEL_COMMAND="/getssl/dns_scripts/dns_del_challtestsrv" +PUBLIC_DNS_SERVER=10.30.50.3 +DNS_EXTRA_WAIT="" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="getssl.test" + +# Acme Challenge Location. The first line for the domain, the following ones for each additional domain. +ACL=( + '/var/www/html/.well-known/acme-challenge' + '/var/www/html/.well-known/acme-challenge' +) + +#Set USE_SINGLE_ACL="true" to use a single ACL for all checks +USE_SINGLE_ACL="false" + +# Location for all your certs, these can either be on the server (full path name) +# or using ssh /sftp as for the ACL +DOMAIN_CERT_LOCATION="/etc/nginx/pki/server.crt" +DOMAIN_KEY_LOCATION="/etc/nginx/pki/private/server.key" +CA_CERT_LOCATION="/etc/nginx/pki/chain.crt" +DOMAIN_CHAIN_LOCATION="" # this is the domain cert and CA cert +DOMAIN_PEM_LOCATION="" # this is the domain_key, domain cert and CA cert + +# The command needed to reload apache / nginx or whatever you use +RELOAD_CMD="cp /getssl/test/test-config/nginx-ubuntu-ssl ${NGINX_CONFIG} && /getssl/test/restart-nginx" + +# Define the server type and confirm correct certificate is installed +SERVER_TYPE="https" +CHECK_REMOTE="true" diff --git a/test/test_helper.bash b/test/test_helper.bash index 3ffcf51..f311b18 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -58,15 +58,14 @@ fi # Find IP address if [[ -n "$(command -v ip)" ]]; then - IP=$(ip address) + GETSSL_IP=$(ip address | awk '/10.30.50/ { print $2 }' | awk -F/ '{ print $1 }') elif [[ -n "$(command -v hostname)" ]]; then - IP=$(hostname -I) + GETSSL_IP=$(hostname -I | sed -e 's/[[:space:]]*$//') else echo "Cannot find IP address" exit 1 fi -GETSSL_IP=$(echo "$IP" | awk '/10.30.50/ { print $2 }' | awk -F/ '{ print $1 }') export GETSSL_IP if [ ! -f ${INSTALL_DIR}/pebble.minica.pem ]; then