#!/bin/sh # ############################################################################### # # edh-keygen.sh # # Diffie-Hellman Key Generation and Service Management Script # # This script generates Diffie-Hellman parameter files for various key sizes, # manages their permissions and can synchronize keys to custom locations # with specified ownership and permissions. It supports service restarts # for both root and non-root systemd users and is designed for integration # with automated cron jobs. # # Configuration is read from a .conf or .local file, supporting per-service # customization including: # - Service name and owner # - DH key size # - Sync path for DH key # - User.group for destination # - File permissions for the key # # Authors: Ivo Noack aka Insonic # Stephan Düsterhaupt # # Copyright (c) 2016-2025 CB-601 - the open tec Elevator # License: MIT # # Project Home: https://dev.town-square.de/cb601/edh-keygen # ############################################################################### # MIT License # # Copyright (c) 2025 CB-601 - the open tec Elevator # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense and/or sell # copies of the Software and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # Default values (in case not set in config) tls_tmp_path='/etc/pki/tls/tmp' tls_private_path='/etc/pki/tls/private/' # Determine config file: prefer edh-keygen.local, else use edh-keygen.conf script_dir=$(dirname "$0") if [ -f "$script_dir/edh-keygen.local" ]; then my_service_conf="$script_dir/edh-keygen.local" else my_service_conf="$script_dir/edh-keygen.conf" fi # Check config file if [ ! -f "$my_service_conf" ]; then echo "Service config file $my_service_conf not found!" 1>&2 exit 1 fi # Read global settings from config file while IFS= read -r line || [ -n "$line" ]; do case "$line" in ''|\#*) continue ;; # Skip empty lines and comments tls_tmp_path=*) tls_tmp_path="${line#tls_tmp_path=}" ;; tls_private_path=*) tls_private_path="${line#tls_private_path=}" ;; key_sizes=*) key_sizes="${line#key_sizes=}" ;; *) break ;; # Stop at first non-global line (service lines start here) esac done < "$my_service_conf" # Create path for 'tmp' in '/etc/pki/tls' if [ ! -d "$tls_tmp_path" ]; then mkdir -p "$tls_tmp_path" fi umask 022 # If key_sizes is set in the config, use it; otherwise, extract from service lines or set a default if [ -z "$key_sizes" ]; then key_sizes=$(awk -F: 'NF >= 3 && $3 ~ /^[0-9]+$/ { print $3 }' "$my_service_conf" | sort -nu) [ -z "$key_sizes" ] && key_sizes="2048 4096" fi # Generate DH params for bits in $key_sizes; do echo "Generating DH parameters for $bits bits..." openssl dhparam -out "$tls_tmp_path/dh_${bits}.pem" "$bits" done # Set permissions find "$tls_tmp_path" -type f -name "*.pem" -exec chmod 644 {} \; # Sync certs from temporary to private folder rsync -a "$tls_tmp_path/"*.pem "$tls_private_path" # Delete the temporary files rm -rf "$tls_tmp_path" # Read and process service list while IFS= read -r line || [ -n "$line" ]; do # Skip empty lines and lines starting with # case "$line" in ''|\#*) continue ;; esac # Extract service, owner and sync parameters service=$(printf "%s" "$line" | awk -F: '{print $1}') owner=$(printf "%s" "$line" | awk -F: '{print $2}') key_size=$(printf "%s" "$line" | awk -F: '{print $3}') sync_path=$(printf "%s" "$line" | awk -F: '{print $4}') user_group=$(printf "%s" "$line" | awk -F: '{print $5}') permissions=$(printf "%s" "$line" | awk -F: '{print $6}') echo "$service.service (owner: $owner)..." # Check service status (must run as root) if [ "$owner" = "root" ]; then mySubState=$(systemctl show -p SubState --value "$service.service" 2>/dev/null) else uid=$(id -u "$owner" 2>/dev/null) if [ -n "$uid" ]; then mySubState=$(sudo -u "$owner" XDG_RUNTIME_DIR="/run/user/$uid" /usr/bin/systemctl --user show -p SubState --value "$service.service" 2>/dev/null) else echo "User $owner not found! Skipping $service." 1>&2 continue fi fi if [ "$mySubState" = "running" ]; then echo "$service.service is running, restarting as $owner..." if [ "$owner" = "root" ]; then /usr/bin/systemctl restart "$service.service" else sudo -u "$owner" XDG_RUNTIME_DIR="/run/user/$uid" /usr/bin/systemctl --user restart "$service" fi echo "$service.service restarted." fi # Handle DH key sync if parameters exist if [ -n "$key_size" ] && [ -n "$sync_path" ] && [ -n "$user_group" ] && [ -n "$permissions" ]; then if ! echo "$user_group" | grep -q "."; then echo "Error: user_group must be 'user.group' for $service. Skipping sync." 1>&2 continue fi dh_file="$tls_private_path/dh_${key_size}.pem" if [ ! -f "$dh_file" ]; then echo "DH key $dh_file not found. Skipping sync for $service." 1>&2 continue fi # Create directory if missing if [ ! -d "$sync_path" ]; then mkdir -p "$sync_path" || { echo "Failed to create $sync_path for $service." 1>&2 continue } chown "$user_group" "$sync_path" chmod 750 "$sync_path" echo "Created directory $sync_path for $service." fi # Copy DH key and set permissions cp "$dh_file" "$sync_path/" || { echo "Failed to copy DH key to $sync_path for $service." 1>&2 continue } chown "$user_group" "$sync_path/dh_${key_size}.pem" chmod "$permissions" "$sync_path/dh_${key_size}.pem" echo "Synced DH key (${key_size}-bit) to $sync_path for $service." fi done < "$my_service_conf" exit 0