]> git.ipfire.org Git - thirdparty/dehydrated.git/commitdiff
rewrote challenge validation to iterate over authorizations instead of altnames ...
authorLukas Schauer <lukas@schauer.so>
Sun, 28 Jan 2018 04:02:18 +0000 (05:02 +0100)
committerLukas Schauer <lukas@schauer.so>
Sun, 28 Jan 2018 05:13:37 +0000 (06:13 +0100)
.travis.yml [deleted file]
dehydrated
docs/hook_chain.md
test.sh [deleted file]

diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644 (file)
index b4e42e5..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-sudo: false
-language: shell
-
-os:
-  - linux
-  - osx
-
-cache:
-  directories:
-    - ngrok
-
-script:
-  - export CI="true"
-  - ./test.sh
index 4a162bc5889209145419a4822e3fdf0f1579310f..6c3af646b60bb07549060cecb9b66755ed1a7f89 100755 (executable)
@@ -407,6 +407,13 @@ get_json_array_value() {
   sed -n "${filter}"
 }
 
+# Get sub-dictionary from json
+get_json_dict_value() {
+  local filter
+  filter=$(printf 's/.*"%s": *{\([^}]*\)}.*/\\1/p' "$1")
+  sed -n "${filter}"
+}
+
 # Get integer value from json
 get_json_int_value() {
   local filter
@@ -604,75 +611,86 @@ sign_csr() {
   fi
 
   if [[ -n "${ZSH_VERSION:-}" ]]; then
-    local -A challenge_altnames challenge_uris challenge_tokens keyauths deploy_args authorization
+    local -A challenge_identifiers challenge_uris challenge_tokens authorizations keyauths deploy_args
   else
-    local -a challenge_altnames challenge_uris challenge_tokens keyauths deploy_args authorization
+    local -a challenge_identifiers challenge_uris challenge_tokens authorizations keyauths deploy_args
   fi
 
-  if  [[ ${API} -eq 2 ]]; then
-    # APIv2
+  # Initial step: Find which authorizations we're dealing with
+  if [[ ${API} -eq 2 ]]; then
+    # Request new order and store authorization URIs
     for altname in ${altnames}; do
       challenge_identifiers+="$(printf '{"type": "dns", "value": "%s"}, ' "${altname}")"
     done
     challenge_identifiers="[${challenge_identifiers%, }]"
 
-    echo " + Requesting challenges for ${altnames}..."
+    echo " + Requesting new certificate order from CA..."
     result="$(signed_request "${CA_NEW_ORDER}" '{"identifiers": '"${challenge_identifiers}"'}')"
 
-    authorizations="$(echo ${result} | get_json_array_value authorizations)"
+    order_authorizations="$(echo ${result} | get_json_array_value authorizations)"
     finalize="$(echo "${result}" | get_json_string_value finalize)"
 
     local idx=0
-    for uris in ${authorizations}; do
-      authorization[${idx}]="${uris}"
+    for uri in ${order_authorizations}; do
+      authorizations[${idx}]="$(echo "${uri}" | _sed -e 's/\"(.*)".*/\1/')"
+      idx=$((idx+1))
+    done
+    echo " + Received ${idx} authorizations URLs from the CA"
+  else
+    # Copy $altnames to $authorizations (just doing this to reduce duplicate code later on)
+    local idx=0
+    for altname in ${altnames}; do
+      authorizations[${idx}]="${altname}"
       idx=$((idx+1))
     done
-
-    unset challenge_identifiers authorizations
   fi
 
-  local idx=0; idy=-1
-  # Request challenges
-  for altname in ${altnames}; do
-    idy=$((idy+1))
-    if [[ ${API} -eq 1 ]]; then
-      # Ask the acme-server for new challenge token and extract them from the resulting json block
-      echo " + Requesting challenge for ${altname}..."
-      response="$(signed_request "${CA_NEW_AUTHZ}" '{"resource": "new-authz", "identifier": {"type": "dns", "value": "'"${altname}"'"}}' | clean_json)"
+  # Check if authorizations are valid and gather challenge information for pending authorizations
+  local idx=0
+  for authorization in ${authorizations[*]}; do
+    if [[ "${API}" -eq 2 ]]; then
+      # Receive authorization ($authorization is authz uri)
+      response="$(http_request get "$(echo "${authorization}" | _sed -e 's/\"(.*)".*/\1/')" | clean_json)"
+      identifier="$(echo "${response}" | get_json_dict_value identifier | get_json_string_value value)"
+      echo " + Handling authorization for ${identifier}"
     else
-      echo " + Handling challenge for ${altname}..."
-      uris="$(<<<"${authorization[${idy}]}" _sed -e 's/\"(.*)".*/\1/')"
-      response="$(http_request get "${uris}" | clean_json)"
+      # Request new authorization ($authorization is altname)
+      identifier="${authorization}"
+      echo " + Requesting authorization for ${identifier}..."
+      response="$(signed_request "${CA_NEW_AUTHZ}" '{"resource": "new-authz", "identifier": {"type": "dns", "value": "'"${identifier}"'"}}' | clean_json)"
     fi
 
-    challenge_status="$(printf '%s' "${response}" | rm_json_arrays | get_json_string_value status)"
-    if [ "${challenge_status}" = "valid" ] && [ ! "${PARAM_FORCE:-no}" = "yes" ]; then
-       echo " + Already validated!"
-       continue
+    # Check if authorization has already been validated
+    if [ "$(echo "${response}" | sed -E 's/"challenges": \[\{.*\}\]//' | get_json_string_value status)" = "valid" ] && [ ! "${PARAM_FORCE:-no}" = "yes" ]; then
+      echo " + Found valid authorization for ${identifier}"
+      continue
     fi
 
-    challenges="$(printf '%s\n' "${response}" | sed -n 's/.*\("challenges":[^\[]*\[[^]]*]\).*/\1/p')"
-    challenge="$(<<<"${challenges}" _sed -e 's/^[^\[]+\[(.+)\]$/\1/' -e 's/\}(, (\{)|(\]))/}\'$'\n''\2/g' | grep \""${CHALLENGETYPE}"\")"
-
-    challenge_token="$(printf '%s' "${challenge}" | get_json_string_value token | _sed 's/[^A-Za-z0-9_\-]/_/g')"
-    if [[ ${API} -eq 1 ]]; then
-      challenge_uri="$(printf '%s' "${challenge}" | get_json_string_value uri)"
-    else
-      challenge_uri="$(printf '%s' "${challenge}" | get_json_string_value url)"
+    # Find challenge in authorization
+    challenges="$(echo "${response}" | _sed 's/.*"challenges": \[(\{.*\})\].*/\1/')"
+    challenge="$(<<<"${challenges}" _sed -e 's/^[^\[]+\[(.+)\]$/\1/' -e 's/\}(, (\{)|(\]))/}\'$'\n''\2/g' | grep \""${CHALLENGETYPE}"\" || true)"
+    if [ -z "${challenge}" ]; then
+      allowed_validations="$(grep -Eo '"type": "[^"]+"' <<< "${challenges}" | grep -Eo ' "[^"]+"' | _sed -e 's/"//g' -e 's/^ //g')"
+      _exiterr "Validating this certificate is not possible using ${CHALLENGETYPE}. Possible validation methods are: ${allowed_validations}"
     fi
 
-    if [[ -z "${challenge_token}" ]] || [[ -z "${challenge_uri}" ]]; then
-      _exiterr "Can't retrieve challenges (${response})"
+    # Gather challenge information
+    challenge_identifier[${idx}]="${identifier}"
+    challenge_tokens[${idx}]="$(echo "${challenge}" | get_json_string_value token)"
+    if [[ ${API} -eq 2 ]]; then
+      challenge_uris[${idx}]="$(echo "${challenge}" | get_json_string_value url)"
+    else
+      challenge_uris[${idx}]="$(echo "${challenge}" | get_json_string_value uri)"
     fi
 
-    # Challenge response consists of the challenge token and the thumbprint of our public certificate
-    keyauth="${challenge_token}.${thumbprint}"
+    # Prepare challenge tokens and deployment parameters
+    keyauth="${challenge_tokens[${idx}]}.${thumbprint}"
 
     case "${CHALLENGETYPE}" in
       "http-01")
         # Store challenge response in well-known location and make world-readable (so that a webserver can access it)
-        printf '%s' "${keyauth}" > "${WELLKNOWN}/${challenge_token}"
-        chmod a+r "${WELLKNOWN}/${challenge_token}"
+        printf '%s' "${keyauth}" > "${WELLKNOWN}/${challenge_tokens[${idx}]}"
+        chmod a+r "${WELLKNOWN}/${challenge_tokens[${idx}]}"
         keyauth_hook="${keyauth}"
         ;;
       "dns-01")
@@ -680,89 +698,86 @@ sign_csr() {
         keyauth_hook="$(printf '%s' "${keyauth}" | "${OPENSSL}" dgst -sha256 -binary | urlbase64)"
         ;;
     esac
-
-    challenge_altnames[${idx}]="${altname}"
-    challenge_uris[${idx}]="${challenge_uri}"
     keyauths[${idx}]="${keyauth}"
-    challenge_tokens[${idx}]="${challenge_token}"
-    # Note: assumes args will never have spaces!
-    deploy_args[${idx}]="${altname} ${challenge_token} ${keyauth_hook}"
+    deploy_args[${idx}]="${identifier} ${challenge_tokens[${idx}]} ${keyauth_hook}"
+
     idx=$((idx+1))
   done
-  challenge_count="${idx}"
+  local num_pending_challenges=${idx}
+  echo " + ${num_pending_challenges} pending challenge(s)"
 
-  # Wait for hook script to deploy the challenges if used
-  if [[ ${challenge_count} -ne 0 ]]; then
+  # Detect duplicate challenge identifiers
+  if [ "${HOOK_CHAIN}" = "yes" ] && [ -n "$(tr ' ' '\n' <<< "${challenge_identifier[*]}" | sort | uniq -d)" ]; then
+    echo "!! Disabling HOOK_CHAIN for this certificate (see https://dehydrated.de/docs/hook_chain.md#problem-with-wildcard-certificates for more information)"
+    HOOK_CHAIN=no
+  fi
+
+  # Deploy challenge tokens using chained hook
+  if [[ ${num_pending_challenges} -ne 0 ]]; then
     # shellcheck disable=SC2068
-    [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]] && "${HOOK}" "deploy_challenge" ${deploy_args[@]}
+    if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]]; then
+      echo " + Deploying challenge tokens..."
+      "${HOOK}" "deploy_challenge" ${deploy_args[@]}
+    fi
   fi
 
-  # Respond to challenges
-  reqstatus="valid"
-  idx=0
-  if [ ${challenge_count} -ne 0 ]; then
-    for altname in "${challenge_altnames[@]:0}"; do
-      challenge_token="${challenge_tokens[${idx}]}"
-      keyauth="${keyauths[${idx}]}"
+  # Validate pending challenges
+  local idx=0
+  while [ ${idx} -lt ${num_pending_challenges} ]; do
+    echo " + Responding to challenge for ${challenge_identifier[${idx}]} authorization..."
 
-      # Wait for hook script to deploy the challenge if used
-      # shellcheck disable=SC2086
-      [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && "${HOOK}" "deploy_challenge" ${deploy_args[${idx}]}
+    # Run hook script to deploy the challenge token
+    if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]]; then
+      "${HOOK}" "deploy_challenge" ${deploy_args[${idx}]}
+    fi
 
-      # Ask the acme-server to verify our challenge and wait until it is no longer pending
-      echo " + Responding to challenge for ${altname}..."
-      if [[ ${API} -eq 1 ]]; then
-        result="$(signed_request "${challenge_uris[${idx}]}" '{"resource": "challenge", "keyAuthorization": "'"${keyauth}"'"}' | clean_json)"
-      else
-        result="$(signed_request "${challenge_uris[${idx}]}" '{"keyAuthorization": "'"${keyauth}"'"}' | clean_json)"
-      fi
+    # Ask the acme-server to verify our challenge and wait until it is no longer pending
+    if [[ ${API} -eq 1 ]]; then
+      result="$(signed_request "${challenge_uris[${idx}]}" '{"resource": "challenge", "keyAuthorization": "'"${keyauths[${idx}]}"'"}' | clean_json)"
+    else
+      result="$(signed_request "${challenge_uris[${idx}]}" '{"keyAuthorization": "'"${keyauths[${idx}]}"'"}' | clean_json)"
+    fi
 
-      reqstatus="$(printf '%s\n' "${result}" | get_json_string_value status)"
+    reqstatus="$(printf '%s\n' "${result}" | get_json_string_value status)"
 
-      while [[ "${reqstatus}" = "pending" ]]; do
-        sleep 1
-        if [[ ${API} -eq 1 ]]; then
-          result="$(http_request get "${challenge_uris[${idx}]}")"
-        else
-          result="$(http_request get "${challenge_uris[${idx}]}")"
-        fi
-        reqstatus="$(printf '%s\n' "${result}" | get_json_string_value status)"
-      done
+    while [[ "${reqstatus}" = "pending" ]]; do
+      sleep 1
+      result="$(http_request get "${challenge_uris[${idx}]}")"
+      reqstatus="$(printf '%s\n' "${result}" | get_json_string_value status)"
+    done
 
-      [[ "${CHALLENGETYPE}" = "http-01" ]] && rm -f "${WELLKNOWN}/${challenge_token}"
+    [[ "${CHALLENGETYPE}" = "http-01" ]] && rm -f "${WELLKNOWN}/${challenge_tokens[${idx}]}"
 
-      # Wait for hook script to clean the challenge if used
-      if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && [[ -n "${challenge_token}" ]]; then
-        # shellcheck disable=SC2086
-        "${HOOK}" "clean_challenge" ${deploy_args[${idx}]}
-      fi
-      idx=$((idx+1))
+    # Run hook script to clean the challenge token
+    if [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]]; then
+      # shellcheck disable=SC2086
+      "${HOOK}" "clean_challenge" ${deploy_args[${idx}]}
+    fi
+    idx=$((idx+1))
 
-      if [[ "${reqstatus}" = "valid" ]]; then
-        echo " + Challenge is valid!"
-      else
-        [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && "${HOOK}" "invalid_challenge" "${altname}" "${result}"
-        break
-      fi
-    done
-  fi
+    if [[ "${reqstatus}" = "valid" ]]; then
+      echo " + Challenge is valid!"
+    else
+      [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" != "yes" ]] && "${HOOK}" "invalid_challenge" "${altname}" "${result}"
+      break
+    fi
+  done
 
-  # Wait for hook script to clean the challenges if used
-  # shellcheck disable=SC2068
-  if [[ ${challenge_count} -ne 0 ]]; then
+  if [[ ${num_pending_challenges} -ne 0 ]]; then
+    # Clean challenge tokens using chained hook
     [[ -n "${HOOK}" ]] && [[ "${HOOK_CHAIN}" = "yes" ]] && "${HOOK}" "clean_challenge" ${deploy_args[@]}
-  fi
 
-  if [[ "${reqstatus}" != "valid" ]]; then
-    # Clean up any remaining challenge_tokens if we stopped early
-    if [[ "${CHALLENGETYPE}" = "http-01" ]] && [[ ${challenge_count} -ne 0 ]]; then
-      while [ ${idx} -lt ${#challenge_tokens[@]} ]; do
-        rm -f "${WELLKNOWN}/${challenge_tokens[${idx}]}"
-        idx=$((idx+1))
-      done
-    fi
+    # Clean remaining challenge tokens if validation has failed
+    if [[ "${reqstatus}" != "valid" ]]; then
+      if [[ "${CHALLENGETYPE}" = "http-01" ]] && [[ ${num_pending_challenges} -ne 0 ]]; then
+        while [ ${idx} -lt ${num_pending_challenges} ]; do
+          rm -f "${WELLKNOWN}/${challenge_tokens[${idx}]}"
+          idx=$((idx+1))
+        done
+      fi
 
-    _exiterr "Challenge is invalid! (returned: ${reqstatus}) (result: ${result})"
+      _exiterr "Challenge is invalid! (returned: ${reqstatus}) (result: ${result})"
+    fi
   fi
 
   # Finally request certificate from the acme-server and store it in cert-${timestamp}.pem and link from cert.pem
index c025783bd8737b4b1d9ec54ed0bef2274d8a747e..5a3da122b99e728c1bea80c2d071e65e0eef5d84 100644 (file)
@@ -61,3 +61,14 @@ HOOK: deploy_cert lukas.im /etc/dehydrated/certs/lukas.im/privkey.pem /etc/dehyd
  + Done!
 ```
 
+# Problem with wildcard certificates
+
+For wildcard certificates the upper level domain is used for verification, e.g.
+`*.foo.example.com` will be verified at `foo.example.com`.
+
+In cases where both `foo.example.com` and `*.foo.example.com` would have to be
+validated there would be a conflict since both will have different tokens but
+both are expected to be resolved under `_acme-challenge.foo.example.com`.
+
+If dehydrated detects this kind of configuration it will automatically fall back
+to non-chaining behaviour (until the next certificate).
diff --git a/test.sh b/test.sh
deleted file mode 100755 (executable)
index e68f22e..0000000
--- a/test.sh
+++ /dev/null
@@ -1,266 +0,0 @@
-#!/usr/bin/env bash
-
-# Fail early
-set -eu -o pipefail
-
-# Check if running in CI environment
-if [[ ! "${CI:-false}" == "true" ]]; then
-  echo "ERROR: Not running in CI environment!"
-  exit 1
-fi
-
-_TEST() {
-  echo
-  echo "${1} "
-}
-_SUBTEST() {
-  echo -n " + ${1} "
-}
-_PASS() {
-  echo -e "[\u001B[32mPASS\u001B[0m]"
-}
-_FAIL() {
-  echo -e "[\u001B[31mFAIL\u001B[0m]"
-  echo
-  echo "Problem: ${@}"
-  echo
-  echo "STDOUT:"
-  cat tmplog
-  echo
-  echo "STDERR:"
-  cat errorlog
-  exit 1
-}
-_CHECK_FILE() {
-  _SUBTEST "Checking if file '${1}' exists..."
-  if [[ -e "${1}" ]]; then
-    _PASS
-  else
-    _FAIL "Missing file: ${1}"
-  fi
-}
-_CHECK_LOG() {
-  _SUBTEST "Checking if log contains '${1}'..."
-  if grep -- "${1}" tmplog > /dev/null; then
-    _PASS
-  else
-    _FAIL "Missing in log: ${1}"
-  fi
-}
-_CHECK_NOT_LOG() {
-  _SUBTEST "Checking if log doesn't contain '${1}'..."
-  if grep -- "${1}" tmplog > /dev/null; then
-    _FAIL "Found in log: ${1}"
-  else
-    _PASS
-  fi
-}
-_CHECK_ERRORLOG() {
-  _SUBTEST "Checking if errorlog is empty..."
-  if [[ -z "$(cat errorlog)" ]]; then
-    _PASS
-  else
-    _FAIL "Non-empty errorlog"
-  fi
-}
-
-# If not found (should be cached in travis) download ngrok
-if [[ ! -e "ngrok/ngrok" ]]; then
-  (
-    mkdir -p ngrok
-    cd ngrok
-    if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
-      wget -O ngrok.zip https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
-    elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then
-      wget -O ngrok.zip https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-amd64.zip
-    else
-      echo "No ngrok for ${TRAVIS_OS_NAME}"
-      exit 1
-    fi
-    unzip ngrok.zip ngrok
-    chmod +x ngrok
-  )
-fi
-
-# Run ngrok and grab temporary url from logfile
-ngrok/ngrok http 8080 --log stdout --log-format logfmt --log-level debug > tmp.log &
-ngrok/ngrok http 8080 --log stdout --log-format logfmt --log-level debug > tmp2.log &
-ngrok/ngrok http 8080 --log stdout --log-format logfmt --log-level debug > tmp3.log &
-sleep 2
-TMP_URL="$(grep -Eo "Hostname:[a-z0-9]+.ngrok.io" tmp.log | head -1 | cut -d':' -f2)"
-TMP2_URL="$(grep -Eo "Hostname:[a-z0-9]+.ngrok.io" tmp2.log | head -1 | cut -d':' -f2)"
-TMP3_URL="$(grep -Eo "Hostname:[a-z0-9]+.ngrok.io" tmp3.log | head -1 | cut -d':' -f2)"
-if [[ -z "${TMP_URL}" ]] || [[ -z "${TMP2_URL}" ]] || [[ -z "${TMP3_URL}" ]]; then
-  echo "Couldn't get an url from ngrok, not a dehydrated bug, tests can't continue."
-  exit 1
-fi
-
-# Run python webserver in .acme-challenges directory to serve challenge responses
-mkdir -p .acme-challenges/.well-known/acme-challenge
-(
-  cd .acme-challenges
-  python -m SimpleHTTPServer 8080 > /dev/null 2> /dev/null
-) &
-
-# Generate config and create empty domains.txt
-echo 'CA="https://testca.kurz.pw/directory"' > config
-echo 'CA_TERMS="https://testca.kurz.pw/terms"' >> config
-echo 'WELLKNOWN=".acme-challenges/.well-known/acme-challenge"' >> config
-echo 'RENEW_DAYS="14"' >> config
-touch domains.txt
-
-# Check if help command is working
-_TEST "Checking if help command is working..."
-./dehydrated --help > tmplog 2> errorlog || _FAIL "Script execution failed"
-_CHECK_LOG "Default command: help"
-_CHECK_LOG "--help (-h)"
-_CHECK_LOG "--domain (-d) domain.tld"
-_CHECK_ERRORLOG
-
-# Register account key without LICENSE set
-_TEST "Register account key without LICENSE set"
-./dehydrated --register > tmplog 2> errorlog && _FAIL "Script execution failed"
-_CHECK_LOG "To accept these terms"
-_CHECK_ERRORLOG
-
-# Register account key and agreeing to terms
-_TEST "Register account key without LICENSE set"
-./dehydrated --register --accept-terms > tmplog 2> errorlog || _FAIL "Script execution failed"
-_CHECK_LOG "Registering account key"
-_CHECK_FILE accounts/*/account_key.pem
-_CHECK_ERRORLOG
-
-# Delete accounts and add LICENSE to config for normal operation
-rm -rf accounts
-echo 'LICENSE="https://testca.kurz.pw/terms/v1"' >> config
-
-# Run in cron mode with empty domains.txt (should only generate private key and exit)
-_TEST "First run in cron mode, checking if private key is generated and registered"
-./dehydrated --cron > tmplog 2> errorlog || _FAIL "Script execution failed"
-_CHECK_LOG "Registering account key"
-_CHECK_FILE accounts/*/account_key.pem
-_CHECK_ERRORLOG
-
-# Temporarily move config out of the way and try signing certificate by using temporary config location
-_TEST "Try signing using temporary config location and with domain as command line parameter"
-mv config tmp_config
-./dehydrated --cron --domain "${TMP_URL}" --domain "${TMP2_URL}" --accept-terms -f tmp_config > tmplog 2> errorlog || _FAIL "Script execution failed"
-_CHECK_NOT_LOG "Checking domain name(s) of existing cert"
-_CHECK_LOG "Generating private key"
-_CHECK_LOG "Requesting challenge for ${TMP_URL}"
-_CHECK_LOG "Requesting challenge for ${TMP2_URL}"
-_CHECK_LOG "Challenge is valid!"
-_CHECK_LOG "Creating fullchain.pem"
-_CHECK_LOG "Done!"
-_CHECK_ERRORLOG
-mv tmp_config config
-
-# Add third domain to command-lime, should force renewal.
-_TEST "Run in cron mode again, this time adding third domain, should force renewal."
-./dehydrated --cron --domain "${TMP_URL}" --domain "${TMP2_URL}" --domain "${TMP3_URL}" > tmplog 2> errorlog || _FAIL "Script execution failed"
-_CHECK_LOG "Domain name(s) are not matching!"
-_CHECK_LOG "Forcing renew."
-_CHECK_LOG "Generating private key"
-_CHECK_LOG "Requesting challenge for ${TMP_URL}"
-_CHECK_LOG "Requesting challenge for ${TMP2_URL}"
-_CHECK_LOG "Requesting challenge for ${TMP3_URL}"
-_CHECK_LOG "Challenge is valid!"
-_CHECK_LOG "Creating fullchain.pem"
-_CHECK_LOG "Done!"
-_CHECK_ERRORLOG
-
-# Prepare domains.txt
-# Modify TMP3_URL to be uppercase to check for upper-lower-case mismatch bugs
-echo "${TMP_URL} ${TMP2_URL} $(tr 'a-z' 'A-Z' <<<"${TMP3_URL}")" >> domains.txt
-
-# Run in cron mode again (should find a non-expiring certificate and do nothing)
-_TEST "Run in cron mode again, this time with domain in domains.txt, should find non-expiring certificate"
-./dehydrated --cron > tmplog 2> errorlog || _FAIL "Script execution failed"
-_CHECK_LOG "Checking domain name(s) of existing cert... unchanged."
-_CHECK_LOG "Skipping renew"
-_CHECK_ERRORLOG
-
-# Disable private key renew
-echo 'PRIVATE_KEY_RENEW="no"' >> config
-
-# Run in cron mode one last time, with domain in domains.txt and force-resign (should find certificate, resign anyway, and not generate private key)
-_TEST "Run in cron mode one last time, with domain in domains.txt and force-resign"
-./dehydrated --cron --force > tmplog 2> errorlog || _FAIL "Script execution failed"
-_CHECK_LOG "Checking domain name(s) of existing cert... unchanged."
-_CHECK_LOG "Ignoring because renew was forced!"
-_CHECK_NOT_LOG "Generating private key"
-_CHECK_LOG "Requesting challenge for ${TMP_URL}"
-_CHECK_LOG "Requesting challenge for ${TMP2_URL}"
-_CHECK_LOG "Requesting challenge for ${TMP3_URL}"
-_CHECK_LOG "Already validated!"
-_CHECK_LOG "Creating fullchain.pem"
-_CHECK_LOG "Done!"
-_CHECK_ERRORLOG
-
-# Check if signcsr command is working
-_TEST "Running signcsr command"
-./dehydrated --signcsr certs/${TMP_URL}/cert.csr > tmplog 2> errorlog || _FAIL "Script execution failed"
-_CHECK_LOG "BEGIN CERTIFICATE"
-_CHECK_LOG "END CERTIFICATE"
-_CHECK_NOT_LOG "ERROR"
-
-# Check if renewal works
-_TEST "Run in cron mode again, to check if renewal works"
-echo 'RENEW_DAYS="300"' >> config
-./dehydrated --cron > tmplog 2> errorlog || _FAIL "Script execution failed"
-_CHECK_LOG "Checking domain name(s) of existing cert... unchanged."
-_CHECK_LOG "Renewing!"
-_CHECK_ERRORLOG
-
-# Check if certificate is valid in various ways
-_TEST "Verifying certificate..."
-_SUBTEST "Verifying certificate on its own..."
-openssl x509 -in "certs/${TMP_URL}/cert.pem" -noout -text > tmplog 2> errorlog && _PASS || _FAIL
-_CHECK_LOG "CN=${TMP_URL}"
-_CHECK_LOG "${TMP2_URL}"
-_SUBTEST "Verifying file with full chain..."
-openssl x509 -in "certs/${TMP_URL}/fullchain.pem" -noout -text > /dev/null 2>> errorlog && _PASS || _FAIL
-_SUBTEST "Verifying certificate against CA certificate..."
-curl -s https://testca.kurz.pw/acme/issuer-cert | openssl x509 -inform DER -outform PEM > ca.pem
-(openssl verify -verbose -CAfile "ca.pem" -purpose sslserver "certs/${TMP_URL}/fullchain.pem" 2>&1 || true) | (grep -v ': OK$' || true) >> errorlog 2>> errorlog && _PASS || _FAIL
-_CHECK_ERRORLOG
-
-# Revoke certificate using certificate key
-_TEST "Revoking certificate..."
-./dehydrated --revoke "certs/${TMP_URL}/cert.pem" --privkey "certs/${TMP_URL}/privkey.pem" > tmplog 2> errorlog || _FAIL "Script execution failed"
-REAL_CERT="$(readlink -n "certs/${TMP_URL}/cert.pem")"
-_CHECK_LOG "Revoking certs/${TMP_URL}/${REAL_CERT}"
-_CHECK_LOG "Done."
-_CHECK_FILE "certs/${TMP_URL}/${REAL_CERT}-revoked"
-_CHECK_ERRORLOG
-
-# Enable private key renew
-echo 'PRIVATE_KEY_RENEW="yes"' >> config
-echo 'PRIVATE_KEY_ROLLOVER="yes"' >> config
-
-# Check if Rolloverkey creation works
-_TEST "Testing Rolloverkeys..."
-_SUBTEST "First Run: Creating rolloverkey"
-./dehydrated --cron --domain "${TMP2_URL}" > tmplog 2> errorlog || _FAIL "Script execution failed"
-CERT_ROLL_HASH=$(openssl rsa -in certs/${TMP2_URL}/privkey.roll.pem -outform DER -pubout 2>/dev/null | openssl sha -sha256)
-_CHECK_LOG "Generating private key"
-_CHECK_LOG "Generating private rollover key"
-_SUBTEST "Second Run: Force Renew, Use rolloverkey"
-./dehydrated --cron --force --domain "${TMP2_URL}" > tmplog 2> errorlog || _FAIL "Script execution failed"
-CERT_NEW_HASH=$(openssl rsa -in certs/${TMP2_URL}/privkey.pem -outform DER -pubout 2>/dev/null | openssl sha -sha256)
-_CHECK_LOG "Generating private key"
-_CHECK_LOG "Moving Rolloverkey into position"
-_SUBTEST "Verifying Hash Rolloverkey and private key second run"
-[[ "${CERT_ROLL_HASH}" = "${CERT_NEW_HASH}" ]] && _PASS || _FAIL
-_CHECK_ERRORLOG
-
-# Test cleanup command
-_TEST "Cleaning up certificates"
-./dehydrated --cleanup > tmplog 2> errorlog || _FAIL "Script execution failed"
-_CHECK_LOG "Moving unused file to archive directory: ${TMP_URL}/cert-"
-_CHECK_LOG "Moving unused file to archive directory: ${TMP_URL}/chain-"
-_CHECK_LOG "Moving unused file to archive directory: ${TMP_URL}/fullchain-"
-_CHECK_ERRORLOG
-
-# All done
-exit 0