From 74f4bbeac9ebe819f8f90f539e7944c3994ae10f Mon Sep 17 00:00:00 2001 From: Tim Kimber Date: Fri, 14 Feb 2020 07:18:26 +0000 Subject: [PATCH] Fix bug #505 with DUAL_RSA_ECDSA and multiple locations --- getssl | 37 +++++++++++------ test/4-more-than-10-hosts.bats | 4 +- test/6-dual-rsa-ecdsa-copy-2-locations.bats | 40 +++++++++++++++++++ test/debug-test.sh | 14 ++++++- test/run-all-tests.cmd | 5 +++ ...tssl-http01-dual-rsa-ecdsa-2-locations.cfg | 32 +++++++++++++++ test/test_helper.bash | 6 +-- 7 files changed, 119 insertions(+), 19 deletions(-) create mode 100644 test/6-dual-rsa-ecdsa-copy-2-locations.bats create mode 100644 test/run-all-tests.cmd create mode 100644 test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg diff --git a/getssl b/getssl index 2de9612..dbcd867 100755 --- a/getssl +++ b/getssl @@ -211,10 +211,11 @@ # 2020-02-12 Fix for DUAL_RSA_ECDSA not working with ACMEv2 (#334, #474, #502) # 2020-02-12 Fix #424 - Sporadic "error in EC signing couldn't get R from ..." (2.18) # 2020-02-12 Fix "Registration key already in use" (2.19) +# 2020-02-13 Fix bug with copying to all locations when creating RSA and ECDSA certs (2.20) # ---------------------------------------------------------------------------------------- PROGNAME=${0##*/} -VERSION="2.19" +VERSION="2.20" # defaults ACCOUNT_KEY_LENGTH=4096 @@ -549,8 +550,12 @@ copy_file_to_location() { # copies a file, using scp, sftp or ftp if required. cert=$1 # descriptive name, just used for display from=$2 # current file location to=$3 # location to move file to. + suffix=$4 # (optional) optional suffix for DUAL_RSA_ECDSA, i.e. save to private.key becomes save to private.ec.key IFS=\; read -r -a copy_locations <<<"$3" for to in "${copy_locations[@]}"; do + if [[ -n "$suffix" ]]; then + to="${to%.*}.${suffix}.${to##*.}" + fi info "copying $cert to $to" if [[ "${to:0:4}" == "ssh:" ]] ; then debug "using scp -q $SCP_OPTS $from ${to:4}" @@ -1823,6 +1828,9 @@ sign_string() { # sign a string with a given key and algorithm and return urlbas elif [[ "${signed:4:4}" == "0231" ]]; then #sha384 which needs trimming R=$(echo "$signed" | cut -c 11-106) part2=$(echo "$signed" | cut -c 107-) + elif [[ "${signed:6:4}" == "0240" ]]; then #sha512 which needs padding + R=$(echo -n 00;echo "$signed" | cut -c 9-138) + part2=$(echo "$signed" | cut -c 141-) elif [[ "${signed:6:4}" == "0241" ]]; then #sha512 which needs padding R=$(echo -n 00;echo "$signed" | cut -c 11-140) part2=$(echo "$signed" | cut -c 141-) @@ -1846,6 +1854,8 @@ sign_string() { # sign a string with a given key and algorithm and return urlbas S=$(echo "$part2" | cut -c 5-100) elif [[ "${part2:0:4}" == "0231" ]]; then #sha384 S=$(echo "$part2" | cut -c 7-102) + elif [[ "${part2:0:4}" == "0240" ]]; then #sha512 with padding + S=$(echo -n 00;echo "$part2" | cut -c 5-) elif [[ "${part2:0:4}" == "0241" ]]; then #sha512 with padding S=$(echo -n 00;echo "$part2" | cut -c 5-) elif [[ "${part2:0:4}" == "0242" ]]; then #sha512 @@ -2545,17 +2555,20 @@ if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then if [[ -n "$DOMAIN_CERT_LOCATION" ]]; then copy_file_to_location "ec domain certificate" \ "${CERT_FILE%.*}.ec.crt" \ - "${DOMAIN_CERT_LOCATION%.*}.ec.crt" + "${DOMAIN_CERT_LOCATION}" \ + "ec" fi if [[ -n "$DOMAIN_KEY_LOCATION" ]]; then - copy_file_to_location "ec private key" \ - "$DOMAIN_DIR/${DOMAIN}.ec.key" \ - "${DOMAIN_KEY_LOCATION%.*}.ec.key" + copy_file_to_location "ec private key" \ + "$DOMAIN_DIR/${DOMAIN}.ec.key" \ + "${DOMAIN_KEY_LOCATION}" \ + "ec" fi if [[ -n "$CA_CERT_LOCATION" ]]; then - copy_file_to_location "ec CA certificate" \ - "${CA_CERT%.*}.ec.crt" \ - "${CA_CERT_LOCATION%.*}.ec.crt" + copy_file_to_location "ec CA certificate" \ + "${CA_CERT%.*}.ec.crt" \ + "${CA_CERT_LOCATION%.*}.crt" \ + "ec" fi fi @@ -2570,7 +2583,7 @@ if [[ -n "$DOMAIN_CHAIN_LOCATION" ]]; then copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then cat "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_chain.pem.ec" - copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem.ec" "${to_location%.*}.ec.${to_location##*.}" + copy_file_to_location "full chain" "$TEMP_DIR/${DOMAIN}_chain.pem.ec" "${to_location}" "ec" fi fi # if DOMAIN_KEY_CERT_LOCATION is not blank, then create and copy file. @@ -2583,8 +2596,8 @@ if [[ -n "$DOMAIN_KEY_CERT_LOCATION" ]]; then cat "$DOMAIN_DIR/${DOMAIN}.key" "$CERT_FILE" > "$TEMP_DIR/${DOMAIN}_K_C.pem" copy_file_to_location "private key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then - cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" - copy_file_to_location "private ec key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" "${to_location%.*}.ec.${to_location##*.}" + cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" + copy_file_to_location "private ec key and domain cert pem" "$TEMP_DIR/${DOMAIN}_K_C.pem.ec" "${to_location}" "ec" fi fi # if DOMAIN_PEM_LOCATION is not blank, then create and copy file. @@ -2598,7 +2611,7 @@ if [[ -n "$DOMAIN_PEM_LOCATION" ]]; then copy_file_to_location "full key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem" "$to_location" if [[ "$DUAL_RSA_ECDSA" == "true" ]]; then cat "$DOMAIN_DIR/${DOMAIN}.ec.key" "${CERT_FILE%.*}.ec.crt" "${CA_CERT%.*}.ec.crt" > "$TEMP_DIR/${DOMAIN}.pem.ec" - copy_file_to_location "full ec key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem.ec" "${to_location%.*}.ec.${to_location##*.}" + copy_file_to_location "full ec key, cert and chain pem" "$TEMP_DIR/${DOMAIN}.pem.ec" "${to_location}" "ec" fi fi # end of copying certs. diff --git a/test/4-more-than-10-hosts.bats b/test/4-more-than-10-hosts.bats index 01e364d..ff61d52 100644 --- a/test/4-more-than-10-hosts.bats +++ b/test/4-more-than-10-hosts.bats @@ -17,7 +17,7 @@ setup() { # Add 11 hosts to DNS (also need to be added as aliases in docker-compose.yml) for prefix in a b c d e f g h i j k; do - curl -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a done init_getssl @@ -39,6 +39,6 @@ setup() { # Remove all the dns aliases cleanup_environment for prefix in a b c d e f g h i j k; do - curl -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a + curl --silent -X POST -d '{"host":"'$prefix.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a done } diff --git a/test/6-dual-rsa-ecdsa-copy-2-locations.bats b/test/6-dual-rsa-ecdsa-copy-2-locations.bats new file mode 100644 index 0000000..4e64043 --- /dev/null +++ b/test/6-dual-rsa-ecdsa-copy-2-locations.bats @@ -0,0 +1,40 @@ +#! /usr/bin/env bats + +load '/bats-support/load.bash' +load '/bats-assert/load.bash' +load '/getssl/test/test_helper.bash' + + +# These are run for every test, not once per file +setup() { + export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt + curl --silent -X POST -d '{"host":"'a.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/add-a +} + + +teardown() { + curl --silent -X POST -d '{"host":"'a.$GETSSL_HOST'", "addresses":["'$GETSSL_IP'"]}' http://10.30.50.3:8055/del-a +} + + +@test "Create dual certificates and copy RSA and ECDSA chain and key to two locations" { + CONFIG_FILE="getssl-http01-dual-rsa-ecdsa-2-locations.cfg" + setup_environment + mkdir -p /root/a.${GETSSL_HOST} + + init_getssl + create_certificate + assert_success + + # Check that the RSA chain and key have been copied to both locations + assert [ -e "/etc/nginx/pki/domain-chain.crt" ] + assert [ -e "/root/a.${GETSSL_HOST}/domain-chain.crt" ] + assert [ -e "/etc/nginx/pki/private/server.key" ] + assert [ -e "/root/a.${GETSSL_HOST}/server.key" ] + + # Check that the ECDSA chain and key have been copied to both locations + assert [ -e "/etc/nginx/pki/domain-chain.ec.crt" ] + assert [ -e "/root/a.${GETSSL_HOST}/domain-chain.ec.crt" ] + assert [ -e "/etc/nginx/pki/private/server.ec.key" ] + assert [ -e "/root/a.${GETSSL_HOST}/server.ec.key" ] +} diff --git a/test/debug-test.sh b/test/debug-test.sh index 23d1983..ab00666 100644 --- a/test/debug-test.sh +++ b/test/debug-test.sh @@ -3,12 +3,22 @@ # This runs getssl outside of the BATS framework for debugging, etc, against pebble # Usage: /getssl/test/debug-test.sh getssl-http01.cfg +DEBUG="" +if [ $# -eq 2 ]; then + DEBUG=$1 + shift +fi + CONFIG_FILE=$1 +if [ ! -e "$CONFIG_FILE" ]; then + CONFIG_FILE=${CODE_DIR}/test/test-config/${CONFIG_FILE} +fi source /getssl/test/test_helper.bash setup_environment 3>&1 export CURL_CA_BUNDLE=/root/pebble-ca-bundle.crt "${CODE_DIR}/getssl" -c "$GETSSL_HOST" 3>&1 -cp "${CODE_DIR}/test/test-config/${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" -"${CODE_DIR}/getssl" -f "$GETSSL_HOST" 3>&1 +cp "${CONFIG_FILE}" "${INSTALL_DIR}/.getssl/${GETSSL_HOST}/getssl.cfg" +# shellcheck disable=SC2086 +"${CODE_DIR}/getssl" ${DEBUG} -f "$GETSSL_HOST" 3>&1 diff --git a/test/run-all-tests.cmd b/test/run-all-tests.cmd new file mode 100644 index 0000000..16c6fd5 --- /dev/null +++ b/test/run-all-tests.cmd @@ -0,0 +1,5 @@ +docker exec -it getssl-alpine bats /getssl/test +docker exec -it getssl-centos6 bats /getssl/test +docker exec -it getssl-debian bats /getssl/test +docker exec -it getssl-ubuntu bats /getssl/test +docker exec -it getssl-ubuntu18 bats /getssl/test diff --git a/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg b/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg new file mode 100644 index 0000000..80533ce --- /dev/null +++ b/test/test-config/getssl-http01-dual-rsa-ecdsa-2-locations.cfg @@ -0,0 +1,32 @@ +# Test that more than one location can be specified for CERT and KEY locations and that the +# files are copied to both locations when both RSA and ECDSA certificates are created +# +CA="https://pebble:14000/dir" + +DUAL_RSA_ECDSA="true" +ACCOUNT_KEY_TYPE="prime256v1" +PRIVATE_KEY_ALG="prime256v1" + +# Additional domains - this could be multiple domains / subdomains in a comma separated list +SANS="a.${GETSSL_HOST}" + +# Acme Challenge Location. +ACL=('/var/www/html/.well-known/acme-challenge') + +#Set USE_SINGLE_ACL="true" to use a single ACL for all checks +USE_SINGLE_ACL="true" + +# 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;/root/a.${GETSSL_HOST}/server.key" +CA_CERT_LOCATION="/etc/nginx/pki/chain.crt" +DOMAIN_CHAIN_LOCATION="/etc/nginx/pki/domain-chain.crt;/root/a.${GETSSL_HOST}/domain-chain.crt" # 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 b33ee0b..554d60a 100644 --- a/test/test_helper.bash +++ b/test/test_helper.bash @@ -9,7 +9,7 @@ setup_environment() { fi if [ ! -f ${INSTALL_DIR}/pebble.minica.pem ]; then - wget --no-clobber https://raw.githubusercontent.com/letsencrypt/pebble/master/test/certs/pebble.minica.pem 2>&1 + wget --quiet --no-clobber https://raw.githubusercontent.com/letsencrypt/pebble/master/test/certs/pebble.minica.pem 2>&1 CERT_FILE=/etc/ssl/certs/ca-certificates.crt if [ ! -f $CERT_FILE ]; then CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt @@ -17,14 +17,14 @@ setup_environment() { cat $CERT_FILE ${INSTALL_DIR}/pebble.minica.pem > ${INSTALL_DIR}/pebble-ca-bundle.crt fi - curl -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a + curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/add-a cp ${CODE_DIR}/test/test-config/nginx-ubuntu-no-ssl "${NGINX_CONFIG}" /getssl/test/restart-nginx } cleanup_environment() { - curl -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/del-a + curl --silent -X POST -d '{"host":"'"$GETSSL_HOST"'", "addresses":["'"$GETSSL_IP"'"]}' http://10.30.50.3:8055/del-a }