From: Lukas Schauer Date: Fri, 13 Nov 2020 19:36:51 +0000 (+0100) Subject: added support for requesting preferred-chain instead of default chain X-Git-Tag: v0.7.0~17 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7dfde364a3c960e4ec4b3c32026929ba2a39b040;p=thirdparty%2Fdehydrated.git added support for requesting preferred-chain instead of default chain --- diff --git a/CHANGELOG b/CHANGELOG index d686453..7a06c40 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ This file contains a log of major changes in dehydrated - Support for external account bindings - Special support for ZeroSSL - Support presets for some CAs instead of requiring URLs +- Allow requesting preferred chain (`--preferred-chain`) ## Fixed - No more silent failures on broken hook-scripts diff --git a/README.md b/README.md index fe85e66..119b3eb 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ Commands: --cron (-c) Sign/renew non-existent/changed/expiring certificates. --signcsr (-s) path/to/csr.pem Sign a given CSR, output CRT on stdout (advanced usage) --revoke (-r) path/to/cert.pem Revoke specified certificate + --deactivate Deactivate account --cleanup (-gc) Move unused certificate files to archive directory --help (-h) Show help text --env (-e) Output configuration variables for use in other scripts @@ -77,6 +78,7 @@ Parameters: --privkey (-p) path/to/key.pem Use specified private key instead of account key (useful for revocation) --config (-f) path/to/config Use specified config file --hook (-k) path/to/hook.sh Use specified script for hooks + --preferred-chain issuer-cn Use alternative certificate chain identified by issuer CN --out (-o) certs/directory Output certificates into the specified directory --alpn alpn-certs/directory Output alpn verification certificates into the specified directory --challenge (-t) http-01|dns-01 Which challenge should be used? Currently http-01 and dns-01 are supported diff --git a/dehydrated b/dehydrated index 06fb348..cff579c 100755 --- a/dehydrated +++ b/dehydrated @@ -258,6 +258,7 @@ store_configvars() { __KEYSIZE="${KEYSIZE}" __CHALLENGETYPE="${CHALLENGETYPE}" __HOOK="${HOOK}" + __PREFERRED_CHAIN="${PREFERRED_CHAIN}" __WELLKNOWN="${WELLKNOWN}" __HOOK_CHAIN="${HOOK_CHAIN}" __OPENSSL_CNF="${OPENSSL_CNF}" @@ -272,6 +273,7 @@ reset_configvars() { KEYSIZE="${__KEYSIZE}" CHALLENGETYPE="${__CHALLENGETYPE}" HOOK="${__HOOK}" + PREFERRED_CHAIN="${__PREFERRED_CHAIN}" WELLKNOWN="${__WELLKNOWN}" HOOK_CHAIN="${__HOOK_CHAIN}" OPENSSL_CNF="${__OPENSSL_CNF}" @@ -336,6 +338,7 @@ load_config() { DOMAINS_D= DOMAINS_TXT= HOOK= + PREFERRED_CHAIN= HOOK_CHAIN="no" RENEW_DAYS="30" KEYSIZE="4096" @@ -500,6 +503,7 @@ load_config() { [[ -n "${PARAM_NO_LOCK:-}" ]] && LOCKFILE="" [[ -n "${PARAM_HOOK:-}" ]] && HOOK="${PARAM_HOOK}" + [[ -n "${PARAM_PREFERRED_CHAIN:-}" ]] && PREFERRED_CHAIN="${PARAM_PREFERRED_CHAIN}" [[ -n "${PARAM_CERTDIR:-}" ]] && CERTDIR="${PARAM_CERTDIR}" [[ -n "${PARAM_ALPNCERTDIR:-}" ]] && ALPNCERTDIR="${PARAM_ALPNCERTDIR}" [[ -n "${PARAM_CHALLENGETYPE:-}" ]] && CHALLENGETYPE="${PARAM_CHALLENGETYPE}" @@ -924,6 +928,15 @@ extract_altnames() { fi } +# Get last subject CN in certificate chain +get_last_cn() { + cn="$("${OPENSSL}" verify -CAfile <(echo "${1}") -show_chain <(echo "${1}") | tail -n 1 | _sed -e 's/.* CN ?= ?([^/,]*).*/\1/')" + if [ -z "${cn}" ]; then + _exiterr "Error while fetching CN from certificate chain" + fi + printf "${cn}" +} + # Create certificate for domain(s) and outputs it FD 3 sign_csr() { csr="${1}" # the CSR itself (not a file) @@ -1153,8 +1166,35 @@ sign_csr() { esac result="$(signed_request "${order_location}" "" | jsonsh)" done + + resheaders="$(_mktemp)" certificate="$(echo "${result}" | get_json_string_value certificate)" - crt="$(signed_request "${certificate}" "")" + crt="$(signed_request "${certificate}" "" 4>"${resheaders}")" + + if [ -n "${PREFERRED_CHAIN:-}" ]; then + foundaltchain=0 + altcn="$(get_last_cn "${crt}")" + if [ "${altcn}" = "${PREFERRED_CHAIN}" ]; then + foundaltchain=1 + fi + if [ "${foundaltchain}" = "0" ]; then + while read altcrturl; do + if [ "${foundaltchain}" = "0" ]; then + altcrt="$(signed_request "${altcrturl}" "")" + altcn="$(get_last_cn "${altcrt}")" + if [ "${altcn}" = "${PREFERRED_CHAIN}" ]; then + foundaltchain=1 + crt="${altcrt}" + fi + fi + done <<< "$(grep -Ei '^link:' "${resheaders}" | grep -Ei 'rel="alternate"' | cut -d'<' -f2 | cut -d'>' -f1)" + fi + if [ "${foundaltchain}" = "0" ]; then + _exiterr "Alternative chain with CN = ${PREFERRED_CHAIN} not found" + fi + echo " + Using preferred chain with CN = ${altcn}" + fi + rm -f "${resheaders}" fi # Try to load the certificate to detect corruption @@ -1564,7 +1604,7 @@ command_sign_domains() { config_var="$(echo "${cfgline:1}" | cut -d'=' -f1)" config_value="$(echo "${cfgline:1}" | cut -d'=' -f2-)" case "${config_var}" in - KEY_ALGO|OCSP_MUST_STAPLE|PRIVATE_KEY_RENEW|PRIVATE_KEY_ROLLOVER|KEYSIZE|CHALLENGETYPE|HOOK|WELLKNOWN|HOOK_CHAIN|OPENSSL_CNF|RENEW_DAYS) + KEY_ALGO|OCSP_MUST_STAPLE|PRIVATE_KEY_RENEW|PRIVATE_KEY_ROLLOVER|KEYSIZE|CHALLENGETYPE|HOOK|PREFERRED_CHAIN|WELLKNOWN|HOOK_CHAIN|OPENSSL_CNF|RENEW_DAYS) echo " + ${config_var} = ${config_value}" declare -- "${config_var}=${config_value}" ;; @@ -2068,6 +2108,14 @@ main() { PARAM_HOOK="${1}" ;; + # PARAM_Usage: --preferred-chain issuer-cn + # PARAM_Description: Use alternative certificate chain identified by issuer CN + --preferred-chain) + shift 1 + check_parameters "${1:-}" + PARAM_PREFERRED_CHAIN="${1}" + ;; + # PARAM_Usage: --out (-o) certs/directory # PARAM_Description: Output certificates into the specified directory --out|-o) diff --git a/docs/examples/config b/docs/examples/config index e148b18..6609344 100644 --- a/docs/examples/config +++ b/docs/examples/config @@ -127,3 +127,6 @@ # ACME API version (default: auto) #API=auto + +# Preferred issuer chain (default: -> uses default chain) +#PREFERRED_CHAIN= diff --git a/docs/per-certificate-config.md b/docs/per-certificate-config.md index 457a41a..9c3176a 100644 --- a/docs/per-certificate-config.md +++ b/docs/per-certificate-config.md @@ -17,6 +17,7 @@ Currently supported options: - WELLKNOWN - OPENSSL_CNF - RENEW_DAYS +- PREFERRED_CHAIN ## DOMAINS_D