Separate permission settings for certificate and key files Add specific permissions for key files Maintain consistent error handling for all file operations
292 lines
8.8 KiB
Bash
Executable file
292 lines
8.8 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
# Exit on any error, treat unset variables as errors, and propagate errors in pipelines
|
|
set -euo pipefail
|
|
|
|
load_env() {
|
|
local paths=(
|
|
"$(pwd)/.env"
|
|
"$(dirname "$(realpath "$0")")/.env"
|
|
"$(dirname "$(realpath "$0")")/.env.$(basename "$(realpath "$0")")"
|
|
"$(dirname "$(readlink "$0")")/.env.$(basename "$0")"
|
|
"/opt/skyfritt-tools-env/.env.$(basename "$0")"
|
|
)
|
|
|
|
for path in "${paths[@]}"; do
|
|
if [[ -f "$path" ]]; then
|
|
source "$path" && return 0
|
|
fi
|
|
done
|
|
|
|
echo "Error: .env file not found in any location" >&2
|
|
exit 1
|
|
}
|
|
|
|
load_env || exit 1
|
|
|
|
AUTO_MODE="false"
|
|
FORCE_UPDATE="false"
|
|
SERVICE_SUPPORTS="${SERVICE_SUPPORTS:-reload}"
|
|
FULLCHAIN_PEM="${FULLCHAIN_PEM:-true}"
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--silent)
|
|
AUTO_MODE="true"
|
|
shift
|
|
;;
|
|
--force)
|
|
FORCE_UPDATE="true"
|
|
shift
|
|
;;
|
|
--disable-pem)
|
|
FULLCHAIN_PEM="false"
|
|
shift
|
|
;;
|
|
--restart)
|
|
SERVICE_SUPPORTS="restart"
|
|
shift
|
|
;;
|
|
*)
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
GREEN='\033[0;32m'
|
|
RED='\033[0;31m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
TEMP_DIR=$(mktemp -d)
|
|
trap 'rm -rf "$TEMP_DIR"' EXIT
|
|
|
|
check_requirements() {
|
|
local required_commands=("curl" "jq" "openssl")
|
|
for cmd in "${required_commands[@]}"; do
|
|
if ! command -v "$cmd" &> /dev/null; then
|
|
echo -e "${RED}Error: Required command '$cmd' is not installed.${NC}"
|
|
exit 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
validate_api_key() {
|
|
local api_key=$1
|
|
[[ $api_key =~ ^[A-Za-z0-9_-]{32,}$ ]]
|
|
}
|
|
|
|
setup_directories() {
|
|
local dirs=("$CERT_PATH" "$KEY_PATH" "$TEMP_PATH")
|
|
for dir in "${dirs[@]}"; do
|
|
if ! mkdir -p "$dir"; then
|
|
echo -e "${RED}Error: Failed to create directory: $dir${NC}"
|
|
exit 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
download_and_verify_cert() {
|
|
local domain=$1
|
|
local cert_api_key=$2
|
|
local key_api_key=$3
|
|
local temp_cert="$TEMP_DIR/$domain.crt"
|
|
local temp_key="$TEMP_DIR/$domain.key"
|
|
local temp_pem="$TEMP_DIR/$domain.pem"
|
|
|
|
echo -e "${BLUE}Processing certificate for $domain${NC}"
|
|
|
|
# Download certificate
|
|
if ! curl -s -fL -o "$temp_cert" -H "X-API-Key: $cert_api_key" \
|
|
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/certificates/$domain"; then
|
|
echo -e "${RED}Failed to download certificate for $domain${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Download private key
|
|
if ! curl -s -fL -o "$temp_key" -H "X-API-Key: $key_api_key" \
|
|
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatekeys/$domain"; then
|
|
echo -e "${RED}Failed to download private key for $domain${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Download fullchain PEM file
|
|
if ! curl -s -fL -o "$temp_pem" -H "X-API-Key: $cert_api_key.$key_api_key" \
|
|
"https://$CERTWARDEN_SERVER/certwarden/api/v1/download/privatecertchains/$domain"; then
|
|
echo -e "${RED}Failed to download fullchain PEM file for $domain${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Verify files are not empty
|
|
if [ ! -s "$temp_cert" ] || [ ! -s "$temp_key" ] || [ ! -s "$temp_pem" ]; then
|
|
echo -e "${RED}Downloaded files are empty for $domain${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Validate certificate and key match
|
|
local cert_fingerprint
|
|
cert_fingerprint=$(openssl x509 -in "$temp_cert" -noout -pubkey |
|
|
openssl pkey -pubin -outform DER 2>/dev/null |
|
|
openssl dgst -sha256)
|
|
local key_fingerprint
|
|
key_fingerprint=$(openssl pkey -in "$temp_key" -pubout -outform DER 2>/dev/null |
|
|
openssl dgst -sha256)
|
|
local pem_fingerprint
|
|
pem_fingerprint=$(openssl x509 -in "$temp_pem" -noout -pubkey |
|
|
openssl pkey -pubin -outform DER 2>/dev/null |
|
|
openssl dgst -sha256)
|
|
|
|
if [ "$cert_fingerprint" != "$key_fingerprint" ]; then
|
|
echo -e "${RED}Certificate and key do not match for $domain${NC}"
|
|
return 1
|
|
fi
|
|
|
|
if [[ "$cert_fingerprint" != "$pem_fingerprint" ]]; then
|
|
echo -e "${RED}Certificate and PEM file do not match for $domain${NC}"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
install_certificate() {
|
|
local domain=$1
|
|
local final_cert="$CERT_PATH/$domain.crt"
|
|
local final_key="$KEY_PATH/$domain.key"
|
|
local final_pem="$KEY_PATH/$domain.pem"
|
|
local temp_cert="$TEMP_DIR/$domain.crt"
|
|
local temp_key="$TEMP_DIR/$domain.key"
|
|
local temp_pem="$TEMP_DIR/$domain.pem"
|
|
local needs_reload=0
|
|
|
|
# Check if certificate needs updating
|
|
if [ "$FORCE_UPDATE" = "true" ]; then
|
|
needs_reload=1
|
|
elif [ "$FULLCHAIN_PEM" = "true" ] && [ -f "$final_pem" ]; then
|
|
if ! cmp -s "$final_pem" "$temp_pem"; then
|
|
needs_reload=1
|
|
fi
|
|
elif [ -f "$final_cert" ]; then
|
|
if ! cmp -s "$final_cert" "$temp_cert"; then
|
|
needs_reload=1
|
|
fi
|
|
else
|
|
needs_reload=1
|
|
fi
|
|
|
|
# Install new certificate and key
|
|
if [ $needs_reload -eq 1 ]; then
|
|
if ! cp -f "$temp_cert" "$final_cert" || ! cp -f "$temp_key" "$final_key"; then
|
|
echo -e "${RED}Failed to install certificate files for $domain${NC}"
|
|
return 1
|
|
fi
|
|
|
|
# Set permissions and ownership for cert and key separately
|
|
if ! chown "$CERT_OWNER:$CERT_GROUP" "$final_cert" || \
|
|
! chmod "$CERT_PERMISSIONS" "$final_cert"; then
|
|
echo -e "${RED}Failed to set permissions for $final_cert${NC}"
|
|
return 1
|
|
fi
|
|
if ! chown "$CERT_OWNER:$CERT_GROUP" "$final_key" || \
|
|
! chmod "$KEY_PERMISSIONS" "$final_key"; then
|
|
echo -e "${RED}Failed to set permissions for $final_key${NC}"
|
|
return 1
|
|
fi
|
|
|
|
if [ "$FULLCHAIN_PEM" = "true" ]; then
|
|
if ! cp -f "$temp_pem" "$final_pem"; then
|
|
echo -e "${RED}Failed to install PEM file for $domain${NC}"
|
|
return 1
|
|
fi
|
|
if ! chown "$CERT_OWNER:$CERT_GROUP" "$final_pem" || \
|
|
! chmod "$KEY_PERMISSIONS" "$final_pem"; then
|
|
echo -e "${RED}Failed to set permissions for $final_pem${NC}"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
echo -e "${GREEN}Certificate updated for $domain${NC}"
|
|
return 0
|
|
fi
|
|
|
|
echo -e "${BLUE}Certificate already up to date for $domain${NC}"
|
|
return 2
|
|
}
|
|
|
|
process_certificates() {
|
|
local service_reloaded=0
|
|
local certs_array
|
|
certs_array=$(echo "$CERTIFICATES" | jq -c '.[]')
|
|
|
|
while IFS= read -r cert; do
|
|
local domain cert_api_key key_api_key
|
|
domain=$(echo "$cert" | jq -r '.domain')
|
|
cert_api_key=$(echo "$cert" | jq -r '.cert_api_key')
|
|
key_api_key=$(echo "$cert" | jq -r '.key_api_key')
|
|
|
|
if download_and_verify_cert "$domain" "$cert_api_key" "$key_api_key"; then
|
|
if install_certificate "$domain"; then
|
|
service_reloaded=1
|
|
fi
|
|
fi
|
|
done <<< "$certs_array"
|
|
|
|
# Reload service if needed
|
|
if [ $service_reloaded -eq 1 ]; then
|
|
if [[ $SERVICE_SUPPORTS = reload ]]; then
|
|
echo -e "${BLUE}Reloading $SERVICE_NAME service...${NC}"
|
|
if systemctl reload "$SERVICE_NAME"; then
|
|
echo -e "${GREEN}Service reloaded successfully${NC}"
|
|
else
|
|
echo -e "${RED}Failed to reload service${NC}"
|
|
return 1
|
|
fi
|
|
elif [[ $SERVICE_SUPPORTS = restart ]]; then
|
|
echo -e "${BLUE}Restarting $SERVICE_NAME service...${NC}"
|
|
if systemctl restart "$SERVICE_NAME"; then
|
|
echo -e "${GREEN}Service restarted successfully${NC}"
|
|
else
|
|
echo -e "${RED}Failed to restart service${NC}"
|
|
return 1
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
check_cert_expiration() {
|
|
echo -e "\n${BLUE}Certificate Expiration Dates:${NC}"
|
|
for cert in "$CERT_PATH"/*.crt; do
|
|
if [ -f "$cert" ]; then
|
|
echo -n "$(basename "$cert"): "
|
|
openssl x509 -enddate -noout -in "$cert"
|
|
fi
|
|
done
|
|
}
|
|
|
|
main_menu() {
|
|
if [ "$AUTO_MODE" = "true" ] || [ "$FORCE_UPDATE" = "true" ]; then
|
|
process_certificates
|
|
exit 0
|
|
fi
|
|
|
|
echo -e "\n${BLUE}Certificate Management Menu${NC}"
|
|
echo "1. Process all certificates"
|
|
echo "2. List installed certificates"
|
|
echo "3. Check certificate expiration"
|
|
echo "4. Force update all certificates"
|
|
|
|
read -r -p "Select an option (1-4): " choice
|
|
|
|
case $choice in
|
|
1) FORCE_UPDATE="false"; process_certificates ;;
|
|
2) ls -l "$CERT_PATH"/*.crt 2>/dev/null || echo "No certificates found" ;;
|
|
3) check_cert_expiration ;;
|
|
4) FORCE_UPDATE="true"; process_certificates; FORCE_UPDATE="false" ;;
|
|
*) echo -e "${RED}Invalid option${NC}" ;;
|
|
esac
|
|
}
|
|
|
|
# Script initialization
|
|
check_requirements
|
|
setup_directories
|
|
main_menu
|