diff --git a/dyntls.sh b/dyntls.sh index b4a597a..e0299c6 100644 --- a/dyntls.sh +++ b/dyntls.sh @@ -843,14 +843,23 @@ _ProvideCertDomainService() { MyIsError=0 - # Split list by '&' into individual domain-service mappings - IFS=$'\n' DomainServiceAry=$(echo "$DYNTLS_DOMAINSERVICE_LIST" | sed 's/&/\n/g') - for ServiceEntry in $DomainServiceAry; do + # Normalize DYNTLS_DOMAINSERVICE_LIST: '&' separated -> one mapping per line + DomainServiceRaw=$(printf '%s\n' "$DYNTLS_DOMAINSERVICE_LIST" | tr '&' '\n') - # Split one mapping into fields - IFS=":" set -- $ServiceEntry - Domain="$1"; PkiDir="$2"; UserGroup="$3"; FilePerm="$4" - Service="$5"; ServiceOwner="$6"; RestartFlag="$7"; RestartMode="$8"; DisplayName="$9" + # Nothing to do if no mappings are configured + if [ -z "$DomainServiceRaw" ]; then + _log "No domain-service mappings configured." 1 + _log "Leave the function '_ProvideCertDomainService()'..." 1 + return 0 + fi + + # Process each mapping line-by-line, split into colon-separated fields + while IFS=':' read -r \ + Domain PkiDir UserGroup FilePerm Service ServiceOwner RestartFlag RestartMode DisplayName || \ + [ -n "$Domain" ] + do + # Skip empty lines + [ -z "$Domain" ] && continue # Decide whether PkiDir is relative (default) or absolute (contains '/') case "$PkiDir" in @@ -858,104 +867,188 @@ _ProvideCertDomainService() { # Absolute path (or in general a path containing '/') EffectivePkiDir="$PkiDir" ;; + "") + _log "Skipping mapping with empty pkiDir for domain=$Domain" 2 + MyIsError=1 + continue + ;; *) # Relative service name > located below DYNTLS_PKI EffectivePkiDir="$DYNTLS_PKI/$PkiDir" ;; esac - # Convert legacy user.group notation to user:group for chown to avoid deprecation warnings - UG_USER=${UserGroup%%.*} - UG_GROUP=${UserGroup#*.} - UserGroup=$UG_USER:$UG_GROUP + # Convert legacy user.group notation to user:group + case "$UserGroup" in + *.*) + UG_USER=${UserGroup%%.*} + UG_GROUP=${UserGroup#*.} + + # Legacy special case: "user." -> "user:user" + [ -n "$UG_GROUP" ] || UG_GROUP=$UG_USER + + UserGroup="$UG_USER:$UG_GROUP" + ;; + esac + + # Validate required ownership field + case "$UserGroup" in + *:*) + ;; + "") + _log "Skipping mapping with empty userGroup for domain=$Domain" 4 + MyIsError=1 + continue + ;; + *) + _log "Skipping mapping with invalid userGroup '$UserGroup' for domain=$Domain" 4 + MyIsError=1 + continue + ;; + esac + + # Validate file permission field + case "$FilePerm" in + [0-7][0-7][0-7]|[0-7][0-7][0-7][0-7]) + ;; + *) + _log "Skipping mapping with invalid file permission '$FilePerm' for domain=$Domain" 4 + MyIsError=1 + continue + ;; + esac + + # Normalize restart flag + case "$RestartFlag" in + 1) + ;; + ""|0) + RestartFlag=0 + ;; + *) + _log "Invalid restart flag '$RestartFlag' for domain=$Domain, forcing 0" 3 + RestartFlag=0 + ;; + esac + + # Fallback display name for logs if missing + [ -n "$DisplayName" ] || DisplayName=$Service _log "Processing mapping: domain=$Domain, pkiDir=$PkiDir, userGroup=$UserGroup, perm=$FilePerm, service=$Service, owner=$ServiceOwner, restart=$RestartFlag, mode=$RestartMode, name=$DisplayName" 1 # Match against current hostname - if [ "$Domain" = "$DYNTLS_MEMBER_HOSTNAME" ]; then - _log "$DisplayName-Domain recognized! Try to provide the certificate to $DisplayName..." 2 + [ "$Domain" = "$DYNTLS_MEMBER_HOSTNAME" ] || continue - # Ensure required directories exist - _log "Domain '$Domain' matches CN. Preparing service directory '$EffectivePkiDir'..." 1 - mkdir -p "$EffectivePkiDir/certs" "$EffectivePkiDir/private" + _log "$DisplayName-Domain recognized! Try to provide the certificate to $DisplayName..." 2 + _log "Domain '$Domain' matches CN. Preparing service directory '$EffectivePkiDir'..." 1 - # Set ownership/permissions on directories - _log "Adjust the owner of '$EffectivePkiDir/'..." 1 - find "$EffectivePkiDir" -type d | xargs -r chown "$UserGroup" - find "$EffectivePkiDir" -type d | xargs -r chmod 750 + # Ensure required directories exist + mkdir -p "$EffectivePkiDir/certs" "$EffectivePkiDir/private" || { + _log "Failed to create service PKI directories under '$EffectivePkiDir'." 4 + MyIsError=1 + continue + } - # Provide base server key if missing - _log "Check whether the server base key '$EffectivePkiDir/private/$DYNTLS_PKI_SERVER_BASEKEY_FILE' exists..." 1 - if [ ! -f "$EffectivePkiDir/private/$DYNTLS_PKI_SERVER_BASEKEY_FILE" ]; then - _log "$DYNTLS_PKI_SERVER_BASEKEY doesn't exist. Copy it..." 1 - cp "$DYNTLS_PKI_SERVER_BASEKEY" "$EffectivePkiDir/private/" - fi + # Set ownership/permissions on directories + _log "Adjust the owner of '$EffectivePkiDir/'..." 1 + find "$EffectivePkiDir" -type d -exec chown "$UserGroup" {} \; || { + _log "Failed to adjust directory ownership under '$EffectivePkiDir'." 4 + MyIsError=1 + } + find "$EffectivePkiDir" -type d -exec chmod 750 {} \; || { + _log "Failed to adjust directory permissions under '$EffectivePkiDir'." 4 + MyIsError=1 + } - # 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/" + # Provide base server key if missing + _log "Check whether the server base key '$EffectivePkiDir/private/$DYNTLS_PKI_SERVER_BASEKEY_FILE' exists..." 1 + if [ ! -f "$EffectivePkiDir/private/$DYNTLS_PKI_SERVER_BASEKEY_FILE" ]; then + _log "$DYNTLS_PKI_SERVER_BASEKEY doesn't exist. Copy it..." 1 + cp "$DYNTLS_PKI_SERVER_BASEKEY" "$EffectivePkiDir/private/" || { + _log "Failed to copy base server key to '$EffectivePkiDir/private/'." 4 + MyIsError=1 + continue + } + fi - cp "$DYNTLS_DOMAIN_TARGET_CERT" \ - "$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/" + # Copy issued certificates into service PKI area + _log "Copy the domain certificate, 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_HTTP_CERT_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_CHAIN_SUFFIX" \ + "$DYNTLS_PKI_HTTP_CERT_DIR/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_FULLCHAIN_SUFFIX" \ + "$EffectivePkiDir/certs/" || { + _log "Failed to copy certificate files to '$EffectivePkiDir/certs/'." 4 + MyIsError=1 + continue + } - # Provide server key: either symlink to base or copy unique key - KeyPath="$EffectivePkiDir/private/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_KEY_SUFFIX" + # Provide server key: either symlink to base or copy unique key + KeyPath="$EffectivePkiDir/private/$DYNTLS_MEMBER_HOSTNAME.$DYNTLS_PKI_KEY_SUFFIX" - if [ "$DYNTLS_PKI_KEY_LNS" -eq 1 ]; then - _log "Using relative symlink for service key: $KeyPath -> $DYNTLS_PKI_SERVER_BASEKEY_FILE" 1 - ln -sf "$DYNTLS_PKI_SERVER_BASEKEY_FILE" "$KeyPath" - else - _log "Using dedicated service key copy at: $KeyPath (source: $EffectivePkiDir/private/$DYNTLS_PKI_SERVER_BASEKEY_FILE)" 1 - cp "$EffectivePkiDir/private/$DYNTLS_PKI_SERVER_BASEKEY_FILE" "$KeyPath" - fi + if [ "$DYNTLS_PKI_KEY_LNS" -eq 1 ]; then + _log "Using relative symlink for service key: $KeyPath -> $DYNTLS_PKI_SERVER_BASEKEY_FILE" 1 + ln -sf "$DYNTLS_PKI_SERVER_BASEKEY_FILE" "$KeyPath" || { + _log "Failed to create service key symlink '$KeyPath'." 4 + MyIsError=1 + continue + } + else + _log "Using dedicated service key copy at: $KeyPath (source: $EffectivePkiDir/private/$DYNTLS_PKI_SERVER_BASEKEY_FILE)" 1 + cp "$EffectivePkiDir/private/$DYNTLS_PKI_SERVER_BASEKEY_FILE" "$KeyPath" || { + _log "Failed to copy service key to '$KeyPath'." 4 + MyIsError=1 + continue + } + fi - # Adjust ownership and permissions on .pem files - _log "Adjust the ownership of '$EffectivePkiDir/certs/*.pem'..." 1 - find "$EffectivePkiDir/certs" -type f -name '*.pem' | xargs -r chown "$UserGroup" - _log "Adjust the ownership of '$EffectivePkiDir/private/*.pem'..." 1 - find "$EffectivePkiDir/private" -type f -name '*.pem' | xargs -r chown "$UserGroup" - _log "Adjust the permissions of '$EffectivePkiDir/certs/*.pem'..." 1 - find "$EffectivePkiDir/certs" -type f -name '*.pem' | xargs -r chmod "$FilePerm" - _log "Adjust the permissions of '$EffectivePkiDir/private/*.pem'..." 1 - find "$EffectivePkiDir/private" -type f -name '*.pem' | xargs -r chmod 440 + # Adjust ownership and permissions on .pem files + find "$EffectivePkiDir/certs" -type f -name '*.pem' -exec chown "$UserGroup" {} \; || { + _log "Failed to adjust certificate ownership under '$EffectivePkiDir/certs/'." 4 + MyIsError=1 + } + find "$EffectivePkiDir/private" -type f -name '*.pem' -exec chown "$UserGroup" {} \; || { + _log "Failed to adjust key ownership under '$EffectivePkiDir/private/'." 4 + MyIsError=1 + } + find "$EffectivePkiDir/certs" -type f -name '*.pem' -exec chmod "$FilePerm" {} \; || { + _log "Failed to adjust certificate permissions under '$EffectivePkiDir/certs/'." 4 + MyIsError=1 + } + find "$EffectivePkiDir/private" -type f -name '*.pem' -exec chmod 440 {} \; || { + _log "Failed to adjust key permissions under '$EffectivePkiDir/private/'." 4 + MyIsError=1 + } - _log "Certificates and keys provided for service '$DisplayName'." 2 + _log "Certificates and keys provided for service '$DisplayName'." 2 - # Restart or reload the service if configured - if [ "$RestartFlag" -eq 1 ] && [ -n "$Service" ]; then - # fallback to restart if mode is missing or invalid - [ "$RestartMode" = "restart" ] || [ "$RestartMode" = "reload" ] || RestartMode="restart" + # Restart or reload the service if configured + if [ "$RestartFlag" -eq 1 ] && [ -n "$Service" ]; then + [ "$RestartMode" = "restart" ] || [ "$RestartMode" = "reload" ] || RestartMode="restart" - if [ -n "$ServiceOwner" ] && [ "$ServiceOwner" != "root" ]; then - _log "Reloading service '$Service' as non-root user '$ServiceOwner'..." 1 - if sudo -u "$ServiceOwner" XDG_RUNTIME_DIR="/run/user/$(id -u $ServiceOwner)" \ - systemctl --user "$RestartMode" "$Service.service"; then - _log "Service '$DisplayName' successfully $RestartMode-ed as '$ServiceOwner'." 2 - else - _log "Error while $RestartMode-ing service '$DisplayName'." 4 - MyIsError=1 - fi + if [ -n "$ServiceOwner" ] && [ "$ServiceOwner" != "root" ]; then + _log "Reloading service '$Service' as non-root user '$ServiceOwner'..." 1 + if sudo -u "$ServiceOwner" XDG_RUNTIME_DIR="/run/user/$(id -u "$ServiceOwner")" \ + systemctl --user "$RestartMode" "$Service.service"; then + _log "Service '$DisplayName' successfully $RestartMode-ed as '$ServiceOwner'." 2 else - _log "Reloading service '$Service' as root..." 1 - if systemctl "$RestartMode" "$Service.service"; then - _log "Service '$DisplayName' successfully $RestartMode-ed as root." 2 - else - _log "Error while $RestartMode-ing service '$DisplayName'." 4 - MyIsError=1 - fi + _log "Error while $RestartMode-ing service '$DisplayName'." 4 + MyIsError=1 fi else - _log "No restart required for service '$DisplayName'." 2 + _log "Reloading service '$Service' as root..." 1 + if systemctl "$RestartMode" "$Service.service"; then + _log "Service '$DisplayName' successfully $RestartMode-ed as root." 2 + else + _log "Error while $RestartMode-ing service '$DisplayName'." 4 + MyIsError=1 + fi fi + else + _log "No restart required for service '$DisplayName'." 2 fi - done + done <