From 85959c25f7d5f9c19a1229d4c19ba6dcaec9b0d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20D=C3=BCsterhaupt?= Date: Sun, 29 Mar 2026 11:17:30 +0200 Subject: [PATCH] Build chain and fullchain using local root CA and ACME intermediates --- dyntls.sh | 215 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 135 insertions(+), 80 deletions(-) diff --git a/dyntls.sh b/dyntls.sh index a409c6d..e6c7227 100644 --- a/dyntls.sh +++ b/dyntls.sh @@ -62,9 +62,9 @@ # Outputs text to stdout. _usage() { _log "Enter the function '_usage()'..." 1 - + # command help: - + # init-pki # update [ cmd-opts ] # gen-req [ cmd-opts ] @@ -225,17 +225,17 @@ notice() { # SDuesterhaupt: 2025-09-14 - Create a secure temporary file/directory # # Wrapper for mktemp to create temporary files or directories in the session temp directory. -# Ensures that DYNTLS_TEMP_DIR_session is initialized and usable. +# Ensures that DYNTLS_LE_TMP_DIR_session is initialized and usable. # # No parameters. # Outputs path of created temporary file/directory to stdout. # Returns 0 on success or 1 on failure. dyntls_mktemp() { - [ -n "$DYNTLS_TEMP_DIR_session" ] || _die "DYNTLS_TEMP_DIR_session not initialized!" 1 - [ -d "$DYNTLS_TEMP_DIR_session" ] || mkdir -p "$DYNTLS_TEMP_DIR_session" \ - || _die "Could not create temporary directory '$DYNTLS_TEMP_DIR_session'" 1 + [ -n "$DYNTLS_LE_TMP_DIR_session" ] || _die "DYNTLS_LE_TMP_DIR_session not initialized!" 5 + [ -d "$DYNTLS_LE_TMP_DIR_session" ] || mkdir -p "$DYNTLS_LE_TMP_DIR_session" \ + || _die "Could not create temporary directory '$DYNTLS_LE_TMP_DIR_session'" 5 - template="$DYNTLS_TEMP_DIR_session/tmp.XXXXXX" + template="$DYNTLS_LE_TMP_DIR_session/tmp.XXXXXX" tempfile=$(mktemp "$template") || return 1 # Workaround für win32 mktemp @@ -257,7 +257,7 @@ dyntls_mktemp() { # No parameters or output. cleanup() { # Entferne temporäres Sitzungsverzeichnis falls vorhanden - [ -n "$DYNTLS_TEMP_DIR_session" ] && rm -rf "$DYNTLS_TEMP_DIR_session" + [ -n "$DYNTLS_LE_TMP_DIR_session" ] && rm -rf "$DYNTLS_LE_TMP_DIR_session" # Terminal-Zustand wiederherstellen (stty echo 2>/dev/null) || { (set -o echo 2>/dev/null) && set -o echo; } @@ -278,12 +278,12 @@ cleanup() { _vars_source_check() { _log "Enter the function '_vars_source_check()'..." 1 - [ -n "$DYNTLS_PKI" ] || _die "DYNTLS_PKI env-var undefined" 1 - [ -n "$DYNTLS_PKI_HTTP_DIR" ] || _die "DYNTLS_PKI_HTTP_DIR env-var undefined" 1 - [ -n "$DYNTLS_PKI_HTTP_CERT_DIR" ] || _die "DYNTLS_PKI_HTTP_CERT_DIR env-var undefined" 1 - [ -n "$DYNTLS_PKI_HTTP_KEY_DIR" ] || _die "DYNTLS_PKI_HTTP_KEY_DIR env-var undefined" 1 - [ -n "$DYNTLS_HTTPD_DEFAULT_DIR" ] || _die "DYNTLS_HTTPD_DEFAULT_DIR env-var undefined" 1 - [ -n "$DYNTLS_ENCRYPT_TOKEN_DIR" ] || _die "DYNTLS_ENCRYPT_TOKEN_DIR env-var undefined" 1 + [ -n "$DYNTLS_PKI" ] || _die "DYNTLS_PKI env-var undefined" 5 + [ -n "$DYNTLS_PKI_HTTP_DIR" ] || _die "DYNTLS_PKI_HTTP_DIR env-var undefined" 5 + [ -n "$DYNTLS_PKI_HTTP_CERT_DIR" ] || _die "DYNTLS_PKI_HTTP_CERT_DIR env-var undefined" 5 + [ -n "$DYNTLS_PKI_HTTP_KEY_DIR" ] || _die "DYNTLS_PKI_HTTP_KEY_DIR env-var undefined" 5 + [ -n "$DYNTLS_HTTPD_DEFAULT_DIR" ] || _die "DYNTLS_HTTPD_DEFAULT_DIR env-var undefined" 5 + [ -n "$DYNTLS_ENCRYPT_TOKEN_DIR" ] || _die "DYNTLS_ENCRYPT_TOKEN_DIR env-var undefined" 5 _log "Leave the function '_vars_source_check()'..." 1 @@ -305,13 +305,16 @@ _verify_pki_init() { _vars_source_check - [ -d "$DYNTLS_PKI" ] || _die "DYNTLS_PKI does not exist. $help_note" 1 - [ -d "$DYNTLS_PKI_HTTP_DIR" ] || _die "DYNTLS_PKI_HTTP_DIR missing. $help_note" 1 - [ -d "$DYNTLS_PKI_HTTP_CERT_DIR" ] || _die "DYNTLS_PKI_HTTP_CERT_DIR missing. $help_note" 1 - [ -d "$DYNTLS_PKI_HTTP_KEY_DIR" ] || _die "DYNTLS_PKI_HTTP_KEY_DIR missing. $help_note" 1 - [ -d "$DYNTLS_HTTPD_DEFAULT_DIR" ] || _die "DYNTLS_HTTPD_DEFAULT_DIR missing. $help_note" 1 - [ -d "$DYNTLS_ENCRYPT_TOKEN_DIR" ] || _die "DYNTLS_ENCRYPT_TOKEN_DIR missing. $help_note" 1 - [ -f "$DYNTLS_PKI_SERVER_BASEKEY" ] || _die "DYNTLS_PKI_SERVER_BASEKEY missing. $help_note" 1 + [ -d "$DYNTLS_PKI" ] || _die "DYNTLS_PKI does not exist. $help_note" 5 + [ -d "$DYNTLS_PKI_HTTP_DIR" ] || _die "DYNTLS_PKI_HTTP_DIR missing. $help_note" 5 + [ -d "$DYNTLS_PKI_HTTP_CERT_DIR" ] || _die "DYNTLS_PKI_HTTP_CERT_DIR missing. $help_note" 5 + [ -d "$DYNTLS_PKI_HTTP_KEY_DIR" ] || _die "DYNTLS_PKI_HTTP_KEY_DIR missing. $help_note" 5 + [ -d "$DYNTLS_HTTPD_DEFAULT_DIR" ] || _die "DYNTLS_HTTPD_DEFAULT_DIR missing. $help_note" 5 + [ -d "$DYNTLS_ENCRYPT_TOKEN_DIR" ] || _die "DYNTLS_ENCRYPT_TOKEN_DIR missing. $help_note" 5 + [ -f "$DYNTLS_PKI_SERVER_BASEKEY" ] || _die "DYNTLS_PKI_SERVER_BASEKEY missing. $help_note" 5 + + [ -f "$DYNTLS_LE_CERT_DIR/$DYNTLS_LE_ROOT_CERT_FILE" ] \ + || _die "Root CA missing: $DYNTLS_LE_CERT_DIR/$DYNTLS_LE_ROOT_CERT_FILE. $help_note" 5 _log "Leave the function '_verify_pki_init()'..." 1 @@ -331,23 +334,42 @@ _init_pki() { _log "Enter the function '_init_pki()'..." 1 [ -d "$DYNTLS_PKI_HTTP_CERT_DIR" ] || mkdir -p "$DYNTLS_PKI_HTTP_CERT_DIR" \ - || _die "Failed to create $DYNTLS_PKI_HTTP_CERT_DIR" 1 + || _die "Failed to create $DYNTLS_PKI_HTTP_CERT_DIR" 5 [ -d "$DYNTLS_PKI_HTTP_KEY_DIR" ] || mkdir -p "$DYNTLS_PKI_HTTP_KEY_DIR" \ - || _die "Failed to create $DYNTLS_PKI_HTTP_KEY_DIR" 1 + || _die "Failed to create $DYNTLS_PKI_HTTP_KEY_DIR" 5 if [ ! -f "$DYNTLS_PKI_SERVER_BASEKEY" ] && [ "$DYNTLS_PKI_KEY_LNS" -ne 0 ]; then _log "Create base server key, length $DYNTLS_PKI_KEY_SIZE bit" 1 #MyLEError=$(eval "$DYNTLS_OPENSSL genrsa -out $DYNTLS_PKI_SERVER_BASEKEY $DYNTLS_PKI_KEY_SIZE 2>&1") "$DYNTLS_OPENSSL" genrsa -out "$DYNTLS_PKI_SERVER_BASEKEY" "$DYNTLS_PKI_KEY_SIZE" 2>/dev/null \ - || _die "Failed to generate base server key" 1 + || _die "Failed to generate base server key" 5 fi + # Adjust the permission(s) find "$DYNTLS_HTTPD_DEFAULT_DIR" -type d -exec chown "$DYNTLS_HTTPD_DEFAULT_OWNER" {} \; -exec chmod 755 {} \; find "$DYNTLS_PKI_HTTP_CERT_DIR" -type f -exec chmod 644 {} \; find "$DYNTLS_PKI_HTTP_KEY_DIR" -type f -exec chmod 440 {} \; [ -d "$DYNTLS_ENCRYPT_TOKEN_DIR" ] || mkdir -p "$DYNTLS_ENCRYPT_TOKEN_DIR" \ - || _die "Failed to create $DYNTLS_ENCRYPT_TOKEN_DIR" 1 + || _die "Failed to create $DYNTLS_ENCRYPT_TOKEN_DIR" 5 + + # Download Root Certificate(s) + [ -d "$DYNTLS_LE_CERT_DIR" ] || mkdir -p "$DYNTLS_LE_CERT_DIR" \ + || _die "Failed to create certs directory '$DYNTLS_LE_CERT_DIR'" 5 + + # Adjust the permission(s) + chmod 750 "$DYNTLS_LE_CERT_DIR" + + if command -v wget >/dev/null 2>&1; then + _log "Download LE root from $DYNTLS_LE_ROOT_CERT_URL into $DYNTLS_LE_CERT_DIR" 1 + if wget -q "$DYNTLS_LE_ROOT_CERT_URL" -O "$DYNTLS_LE_CERT_DIR/$DYNTLS_LE_ROOT_CERT_FILE"; then + chmod 400 "$DYNTLS_LE_CERT_DIR/$DYNTLS_LE_ROOT_CERT_FILE" + else + _die "Failed to download LE root cert" 5 + fi + else + _die "wget not available" 5 + fi notice "init-pki complete; you may now create a certificate. Your PKI dirs are: $DYNTLS_PKI_HTTP_CERT_DIR, $DYNTLS_PKI_HTTP_KEY_DIR, $DYNTLS_ENCRYPT_TOKEN_DIR" @@ -371,7 +393,7 @@ _init_log() { if mkdir -p "$DYNTLS_LOG_DIR" 2>/dev/null; then printf "Log directory '%s' created.\n" "$DYNTLS_LOG_DIR" else - _die "Could not create log directory '$DYNTLS_LOG_DIR'" 1 + _die "Could not create log directory '$DYNTLS_LOG_DIR'" 5 fi fi @@ -403,12 +425,15 @@ _vars_setup() { . "$vars" notice "Note: using dynTLS configuration from: $vars" fi - + # Set defaults, preferring existing env-vars if present set_var DYNTLS "$prog_dir" + set_var DYNTLS_LE_CERT_DIR "$DYNTLS/certs" + set_var DYNTLS_LE_TMP_DIR "$DYNTLS/tmp" set_var DYNTLS_LE_PROGRAM "letsencrypt_master.sh" + set_var DYNTLS_LE_ROOT_CERT_URL "https://letsencrypt.org/certs/isrgrootx1.pem" + set_var DYNTLS_LE_ROOT_CERT_FILE "isrgrootx1.pem" set_var DYNTLS_OPENSSL openssl - set_var DYNTLS_TMP "$DYNTLS/tmp" #set_var DYNTLS_DN cn_only #set_var DYNTLS_REQ_COUNTRY "US" #set_var DYNTLS_REQ_PROVINCE "California" @@ -426,22 +451,23 @@ _vars_setup() { set_var DYNTLS_PKI_HTTP_CERT_BACKUP_DIR "$DYNTLS_PKI_HTTP_CERT_DIR/backup" set_var DYNTLS_PKI_HTTP_KEY_DIR "$DYNTLS_PKI_HTTP_DIR/private" set_var DYNTLS_PKI_CERT_SUFFIX "cert.pem" + set_var DYNTLS_PKI_TMP_CHAIN_SUFFIX "cert.pem_chain" + set_var DYNTLS_PKI_CHAIN_SUFFIX "chain.pem" set_var DYNTLS_PKI_FULLCHAIN_SUFFIX "fullchain.pem" set_var DYNTLS_PKI_KEY_SUFFIX "key.pem" - set_var DYNTLS_PKI_LECA_CHAIN_FILE "LE_CA.chain.pem" - set_var DYNTLS_PKI_LECA_CHAIN "$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_PKI_LECA_CHAIN_FILE" - set_var DYNTLS_PKI_LECA_R12_CHAIN_FILE "LE_CA-R12.chain.pem" - set_var DYNTLS_PKI_LECA_R12_CHAIN "$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_PKI_LECA_R12_CHAIN_FILE" - set_var DYNTLS_PKI_LECA_R13_CHAIN_FILE "LE_CA-R13.chain.pem" - set_var DYNTLS_PKI_LECA_R13_CHAIN "$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_PKI_LECA_R13_CHAIN_FILE" + #set_var DYNTLS_PKI_LECA_CHAIN_FILE "LE_CA.chain.pem" + #set_var DYNTLS_PKI_LECA_CHAIN "$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_PKI_LECA_CHAIN_FILE" + #set_var DYNTLS_PKI_LECA_R12_CHAIN_FILE "LE_CA-R12.chain.pem" + #set_var DYNTLS_PKI_LECA_R12_CHAIN "$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_PKI_LECA_R12_CHAIN_FILE" + #set_var DYNTLS_PKI_LECA_R13_CHAIN_FILE "LE_CA-R13.chain.pem" + #set_var DYNTLS_PKI_LECA_R13_CHAIN "$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_PKI_LECA_R13_CHAIN_FILE" set_var DYNTLS_PKI_CERT_EXPIRE 30 # Let's Encrypt default: 30 days set_var DYNTLS_PKI_KEY_FORCE_RENEW 0 - set_var DYNTLS_TEMP_DIR "$DYNTLS_TMP" set_var DYNTLS_BACKUP_EXPIRATION 0 set_var DYNTLS_LOG_DIR "/var/log/dyntls" set_var DYNTLS_LOG_FILE "$DYNTLS_LOG_DIR/dyntls.log" - set_var DYNTLS_LOG_LEVEL "3" + set_var DYNTLS_LOG_LEVEL 3 set_var DYNTLS_ENCRYPT_ACCOUNTKEY "$DYNTLS/private/letsencrypt_account.key" set_var DYNTLS_PKI_SERVER_BASEKEY_FILE "base.$DYNTLS_PKI_KEY_SUFFIX" @@ -454,29 +480,29 @@ _vars_setup() { set_var DYNTLS_DNS_TSIG "tsig.key" set_var DYNTLS_DNS_ZONE "" - set_var DYNTLS_HTTPD_DEFAULT_OWNER "apache." + set_var DYNTLS_HTTPD_DEFAULT_OWNER "apache:apache" set_var DYNTLS_RELOAD_WEBSERVER "false" set_var DYNTLS_SEND_MAIL "false" - set_list DYNTLS_DOMAIN_LIST "example365.tld:sub1.example365.tld:sub2.example365.tld" "1" - set_list DYNTLS_DOMAINSERVICE_LIST "mail02.example365.tld:postfix:root.root:444:postfix:1:restart:Postfix" "1" + set_list DYNTLS_DOMAIN_LIST "example365.tld:sub1.example365.tld:sub2.example365.tld" 1 + set_list DYNTLS_DOMAINSERVICE_LIST "mail02.example365.tld:postfix:root.root:444:postfix:1:restart:Postfix" 1 set_var DYNTLS_PRODUCTIVE 0 - + set_list DYNTLS_CMD_PRE_LIST "" set_list DYNTLS_CMD_POST_LIST "" - # Assign value to $DYNTLS_TEMP_DIR_session and work around Windows mktemp bug when parent dir is missing - if [ -z "$DYNTLS_TEMP_DIR_session" ]; then - if [ -d "$DYNTLS_TEMP_DIR" ]; then - DYNTLS_TEMP_DIR_session="$(mktemp -du "$DYNTLS_TEMP_DIR/dyn-tls-$$.XXXXXX")" + # Assign value to $DYNTLS_LE_TMP_DIR_session and work around Windows mktemp bug when parent dir is missing + if [ -z "$DYNTLS_LE_TMP_DIR_session" ]; then + if [ -d "$DYNTLS_LE_TMP_DIR" ]; then + DYNTLS_LE_TMP_DIR_session="$(mktemp -du "$DYNTLS_LE_TMP_DIR/dyn-tls-$$.XXXXXX")" else # If the directory does not exist then we have not run init-pki - mkdir -p "$DYNTLS_TEMP_DIR" || _die "Cannot create $DYNTLS_TEMP_DIR (permission?)" "1" - DYNTLS_TEMP_DIR_session="$(mktemp -du "$DYNTLS_TEMP_DIR/dyn-tls-$$.XXXXXX")" - rm -rf "$DYNTLS_TEMP_DIR" + mkdir -p "$DYNTLS_LE_TMP_DIR" || _die "Cannot create $DYNTLS_LE_TMP_DIR (permission?)" 5 + DYNTLS_LE_TMP_DIR_session="$(mktemp -du "$DYNTLS_LE_TMP_DIR/dyn-tls-$$.XXXXXX")" + rm -rf "$DYNTLS_LE_TMP_DIR" fi fi @@ -653,7 +679,7 @@ _Hostname() case "$MyDomainPart" in tld) # Top Level Domain: - # Must be alphabetic only, length 2–5 + # Must be alphabetic only, length 2-5 if printf '%s\n' "$DYNTLS_MEMBER_HOSTNAME" | grep -Eq '^[A-Za-z]{2,5}$'; then _log "Expression '$DYNTLS_MEMBER_HOSTNAME' is a valid TLD." 1 MyReturnFlag=0 @@ -707,7 +733,7 @@ _Hostname() fi ;; *) - _die "Invalid action '$MyDomainPart' in function _Hostname()." 1 + _die "Invalid action '$MyDomainPart' in function _Hostname()." 5 ;; esac ;; @@ -727,12 +753,12 @@ _Hostname() printf '%s\n' "$MySubdomain" ;; *) - _die "Invalid action '$MyDomainPart' in function _Hostname()." 1 + _die "Invalid action '$MyDomainPart' in function _Hostname()." 5 ;; esac ;; *) - _die "Invalid method '$MyMethod' in function _Hostname()." 1 + _die "Invalid method '$MyMethod' in function _Hostname()." 5 ;; esac @@ -867,10 +893,15 @@ _ProvideCertDomainService() { # Copy issued certificates into service PKI area _log "Copy the domain certificate, LE_CA chain and full chain from $DYNTLS_PKI_HTTP_CERT_DIR to the PKI service dir '$EffectivePkiDir/certs/'..." 1 + #cp "$DYNTLS_DOMAIN_TARGET_CERT" \ + # "$DYNTLS_PKI_LECA_CHAIN" \ + # "$DYNTLS_PKI_LECA_R12_CHAIN" \ + # "$DYNTLS_PKI_LECA_R13_CHAIN" \ + # "$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_FULLCHAIN_SUFFIX" \ + # "$EffectivePkiDir/certs/" + cp "$DYNTLS_DOMAIN_TARGET_CERT" \ - "$DYNTLS_PKI_LECA_CHAIN" \ - "$DYNTLS_PKI_LECA_R12_CHAIN" \ - "$DYNTLS_PKI_LECA_R13_CHAIN" \ + "$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_CHAIN_SUFFIX" \ "$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_FULLCHAIN_SUFFIX" \ "$EffectivePkiDir/certs/" @@ -952,8 +983,8 @@ _create_cert() { ######################################## # 1. Temporary output for the new cert ######################################## - mkdir -p "$DYNTLS_TMP" - out_file_tmp="$DYNTLS_TMP/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_CERT_SUFFIX" + mkdir -p "$DYNTLS_LE_TMP_DIR" + out_file_tmp="$DYNTLS_LE_TMP_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_CERT_SUFFIX" _log "Temporary certificate file: $out_file_tmp" 1 ######################################## @@ -1028,7 +1059,7 @@ _create_cert() { elif [ -f "$out_file_tmp" ]; then _log "Temporary certificate file exists, starting validation." 1 if openssl x509 -checkend $(($DYNTLS_PKI_CERT_EXPIRE*86400)) -noout -in "$out_file_tmp"; then - _log "The verification of the new certificate was successful. The certificate seems to be valid and it is moved to its destination folder." "2" + _log "The verification of the new certificate was successful. The certificate seems to be valid and it is moved to its destination folder." 2 _log "New certificate meets minimum validity window (${DYNTLS_PKI_CERT_EXPIRE} days)." 1 if [ "$DYNTLS_PRODUCTIVE" -eq 1 ]; then # Backup old cert @@ -1036,7 +1067,7 @@ _create_cert() { _log "Using backup directory: $BackupDir" 1 mkdir -p "$BackupDir" && chmod 750 "$BackupDir" && chown root:root "$BackupDir" [ -f "$DYNTLS_DOMAIN_TARGET_CERT" ] && { - _log "Backup the expired certificate '$DYNTLS_DOMAIN_TARGET_CERT' to '$BackupDir/'." "2" + _log "Backup the expired certificate '$DYNTLS_DOMAIN_TARGET_CERT' to '$BackupDir/'." 2 cp "$DYNTLS_DOMAIN_TARGET_CERT" "$BackupDir/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_CERT_SUFFIX-$(date +%y%m%d)" } @@ -1062,19 +1093,42 @@ _create_cert() { _log "Moving issued certificate to target: $DYNTLS_DOMAIN_TARGET_CERT" 2 mv "$out_file_tmp" "$DYNTLS_DOMAIN_TARGET_CERT" - # Create fullchain file choosing correct chain (R12 vs R13) - issuer_CN=$(openssl x509 -noout -issuer -in "$DYNTLS_DOMAIN_TARGET_CERT" | sed -n 's/^issuer=.*CN=//p') - _log "Detected issuer CN for chain selection: $issuer_CN" 1 - chainFile="$DYNTLS_PKI_LECA_R13_CHAIN" + # Remove temporary file(s) + _log "Removing ACME chain file: $DYNTLS_LE_TMP_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_TMP_CHAIN_SUFFIX" 1 + rm -f "$DYNTLS_LE_TMP_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_TMP_CHAIN_SUFFIX" - [ "$issuer_CN" = "R12" ] && chainFile="$DYNTLS_PKI_LECA_R12_CHAIN" - _log "Using chain file: $chainFile" 1 + # Create fullchain file choosing correct chain (R12 vs R13) + #issuer_CN=$(openssl x509 -noout -issuer -in "$DYNTLS_DOMAIN_TARGET_CERT" | sed -n 's/^issuer=.*CN=//p') + #_log "Detected issuer CN for chain selection: $issuer_CN" 1 + #chainFile="$DYNTLS_PKI_LECA_R13_CHAIN" + + #[ "$issuer_CN" = "R12" ] && chainFile="$DYNTLS_PKI_LECA_R12_CHAIN" + #_log "Using chain file: $chainFile" 1 + #fullchain_path="$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_FULLCHAIN_SUFFIX" + #_log "Creating fullchain file: $fullchain_path" 1 + #cat "$DYNTLS_DOMAIN_TARGET_CERT" "$chainFile" > "$fullchain_path" + #chmod 640 "$DYNTLS_PKI_HTTP_CERT_DIR"/*.pem* + + # Build CA chain: root + intermediates + tmp_chain="$DYNTLS_LE_TMP_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_TMP_CHAIN_SUFFIX" + root_cert="$DYNTLS_LE_CERT_DIR/$DYNTLS_LE_ROOT_CERT_FILE" + + [ -f "$tmp_chain" ] || _die "Missing intermediate chain: $tmp_chain" 5 + + chain_path="$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_CHAIN_SUFFIX" + _log "Creating CA chain file: $chain_path" 1 + cat "$tmp_chain" "$root_cert" > "$chain_path" + + # Build fullchain: CA chain + leaf cert fullchain_path="$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_FULLCHAIN_SUFFIX" _log "Creating fullchain file: $fullchain_path" 1 - cat "$DYNTLS_DOMAIN_TARGET_CERT" "$chainFile" > "$fullchain_path" - chmod 640 "$DYNTLS_PKI_HTTP_CERT_DIR"/*.pem* + cat "$DYNTLS_DOMAIN_TARGET_CERT" "$chain_path" > "$fullchain_path" - # Now copy or link the server key AFTER cert is issued + # Adjust the permission(s) + chmod 640 "$chain_path" "$fullchain_path" + #chmod 640 "$DYNTLS_PKI_HTTP_CERT_DIR"/*.pem* + + # Copy or link the server key AFTER cert is issued KeyFile="$DYNTLS_PKI_HTTP_KEY_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_KEY_SUFFIX" _log "Planned server key path: $KeyFile" 1 @@ -1094,13 +1148,14 @@ _create_cert() { else _log "Staging mode: certificate issued but not installed." 2 rm -f "$out_file_tmp" - rm -f "$DYNTLS_TMP/$DYNTLS_MEMBER_HOSTNAME.cert.pem_chain" + rm -f "$DYNTLS_LE_TMP_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_TMP_CHAIN_SUFFIX" + export DYNTLS_RELOAD_WEBSERVER="false" fi else _log "Issued certificate does not satisfy validity window; removing temporary file(s)." 4 rm -f "$out_file_tmp" - rm -f "$DYNTLS_TMP/$DYNTLS_MEMBER_HOSTNAME.cert.pem_chain" + rm -f "$DYNTLS_LE_TMP_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_TMP_CHAIN_SUFFIX" fi fi @@ -1419,7 +1474,7 @@ while :; do export DYNTLS_PKI_CERT_EXPIRE="$2" shift 2 else - _die "Missing value for --days" 1 + _die "Missing value for --days" 5 fi fi ;; @@ -1433,7 +1488,7 @@ while :; do export DYNTLS_DOMAINS="$2" shift 2 else - _die "Missing value for --hostnames/-H" 1 + _die "Missing value for --hostnames/-H" 5 fi fi # Normalize domain string @@ -1451,7 +1506,7 @@ while :; do export DYNTLS_BIND_ZONE_KEY="$2" shift 2 else - _die "Missing value for --key/-K" 1 + _die "Missing value for --key/-K" 5 fi fi ;; @@ -1484,7 +1539,7 @@ while :; do export DYNTLS_DNS_SERVER="$2" shift 2 else - _die "Missing value for --DNS/-D" 1 + _die "Missing value for --DNS/-D" 5 fi fi ;; @@ -1498,7 +1553,7 @@ while :; do export DYNTLS_DNS_TSIG="$2" shift 2 else - _die "Missing value for --tsig/-T" 1 + _die "Missing value for --tsig/-T" 5 fi fi ;; @@ -1512,7 +1567,7 @@ while :; do export DYNTLS_VARS_FILE="$2" shift 2 else - _die "Missing value for --vars" 1 + _die "Missing value for --vars" 5 fi fi ;; @@ -1555,26 +1610,26 @@ _validate_command_params() { add-cert) # Requires --hostnames / DYNTLS_DOMAINS to be set (one or more domains/SAN) if [ -z "$DYNTLS_DOMAINS" ]; then - _die "'--hostnames' (DYNTLS_DOMAINS) option is required for add-cert command." 1 + _die "'--hostnames' (DYNTLS_DOMAINS) option is required for add-cert command." 5 fi ;; check-cert) # Requires --hostnames / DYNTLS_DOMAINS (domain to check) if [ -z "$DYNTLS_DOMAINS" ]; then - _die "'--hostnames' (DYNTLS_DOMAINS) option is required for check-cert command." 1 + _die "'--hostnames' (DYNTLS_DOMAINS) option is required for check-cert command." 5 fi ;; remove-cert) # Requires --hostnames / DYNTLS_DOMAINS (domain to remove) if [ -z "$DYNTLS_DOMAINS" ]; then - _die "'--hostnames' (DYNTLS_DOMAINS) option is required for remove-cert command." 1 + _die "'--hostnames' (DYNTLS_DOMAINS) option is required for remove-cert command." 5 fi ;; update-cert) # No mandatory CLI options, but could add checks if your workflow requires them. # Example: Validate DYNTLS_DOMAIN_LIST is set: # if [ -z "$DYNTLS_DOMAIN_LIST" ]; then - # _die "'DYNTLS_DOMAIN_LIST' is required for update-cert command." 1 + # _die "'DYNTLS_DOMAIN_LIST' is required for update-cert command." 5 # fi ;; init-pki) @@ -1624,7 +1679,7 @@ case "$cmd" in exit 0 ;; *) - _die "Unknown command '$cmd'. Run without commands for usage help." "1" + _die "Unknown command '$cmd'. Run without commands for usage help." 5 ;; esac