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
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")
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
+++ /dev/null
-#!/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