]> git.ipfire.org Git - thirdparty/dehydrated.git/commitdiff
added support for requesting preferred-chain instead of default chain
authorLukas Schauer <lukas@schauer.so>
Fri, 13 Nov 2020 19:36:51 +0000 (20:36 +0100)
committerLukas Schauer <lukas@schauer.so>
Fri, 13 Nov 2020 19:36:51 +0000 (20:36 +0100)
CHANGELOG
README.md
dehydrated
docs/examples/config
docs/per-certificate-config.md

index d686453c86f1c8866b8f994e9a4c22230f520d33..7a06c40cbc9c20c82d201f7e53f3f7029592bc0d 100644 (file)
--- 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
index fe85e66fdcd7546bc2ffcdaaa91b04ef2c212777..119b3ebd07a053c86d7a186b0ae5f14478efbc45 100644 (file)
--- 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
index 06fb348c16fa31c328e3ae5c267952b93a1459f6..cff579c2d546b0455cbd4095490491c2e21b00a6 100755 (executable)
@@ -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)
index e148b18fd580c6612e3ff8f61d9e27520df4c07f..660934470f8883f6a5600855bdabb7c92f43317c 100644 (file)
 
 # ACME API version (default: auto)
 #API=auto
+
+# Preferred issuer chain (default: <unset> -> uses default chain)
+#PREFERRED_CHAIN=
index 457a41a4904817750e2539ba7a8c091899057490..9c3176a6b092cf3d8d7046446498c01024c533c6 100644 (file)
@@ -17,6 +17,7 @@ Currently supported options:
 - WELLKNOWN
 - OPENSSL_CNF
 - RENEW_DAYS
+- PREFERRED_CHAIN
 
 ## DOMAINS_D