#!/bin/bash # Configuration Management System for Custom PHP Framework # Template management, validation, and secure credential handling # Domain: michaelschiemer.de | Email: kontakt@michaelschiemer.de | PHP: 8.4 set -euo pipefail # Configuration manager constants DEPLOYMENT_DIR="${DEPLOYMENT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../" && pwd)}" CONFIG_TEMPLATES_DIR="${DEPLOYMENT_DIR}/applications/environments" CONFIG_CREDENTIALS_DIR="${DEPLOYMENT_DIR}/.credentials" CONFIG_BACKUP_DIR="${DEPLOYMENT_DIR}/.backups" # Colors GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # Logging log() { echo -e "${GREEN}[CONFIG] ✅ $1${NC}"; } warn() { echo -e "${YELLOW}[CONFIG] ⚠️ $1${NC}"; } error() { echo -e "${RED}[CONFIG] ❌ $1${NC}"; } info() { echo -e "${BLUE}[CONFIG] ℹ️ $1${NC}"; } # Initialize configuration directories init_config_directories() { mkdir -p "$CONFIG_CREDENTIALS_DIR" "$CONFIG_BACKUP_DIR" chmod 700 "$CONFIG_CREDENTIALS_DIR" "$CONFIG_BACKUP_DIR" } # Generate secure password generate_password() { local length=${1:-25} local type=${2:-"alphanumeric"} case $type in "alphanumeric") openssl rand -base64 $((length * 3 / 4)) | tr -d "=+/" | cut -c1-"$length" ;; "base64") openssl rand -base64 "$length" ;; "hex") openssl rand -hex $((length / 2)) ;; "strong") # Strong password with special characters openssl rand -base64 "$length" | tr -d "=+/" | head -c"$length" ;; *) error "Unknown password type: $type" return 1 ;; esac } # Generate all required credentials for an environment generate_environment_credentials() { local environment=$1 local creds_file="${CONFIG_CREDENTIALS_DIR}/${environment}.env" info "Generating secure credentials for $environment environment..." # Backup existing credentials if they exist if [[ -f "$creds_file" ]]; then local backup_file="${CONFIG_BACKUP_DIR}/${environment}.env.backup.$(date +%Y%m%d_%H%M%S)" cp "$creds_file" "$backup_file" warn "Existing credentials backed up to: $backup_file" fi # Generate credentials based on environment cat > "$creds_file" << EOF # Generated Credentials for $environment Environment # Created: $(date) # Custom PHP Framework Deployment # Database Credentials DB_PASSWORD=$(generate_password 25 alphanumeric) DB_ROOT_PASSWORD=$(generate_password 25 alphanumeric) # Redis Credentials REDIS_PASSWORD=$(generate_password 25 alphanumeric) # Application Security APP_KEY=$(generate_password 32 base64) CSRF_SECRET=$(generate_password 32 hex) # Session Security SESSION_SECRET=$(generate_password 32 base64) # API Security API_SECRET=$(generate_password 32 hex) JWT_SECRET=$(generate_password 32 base64) # External API Credentials SHOPIFY_WEBHOOK_SECRET=$(generate_password 64 hex) MAIL_API_KEY=$(generate_password 32 alphanumeric) EOF # Environment-specific credentials if [[ "$environment" == "production" ]]; then cat >> "$creds_file" << EOF # Production-specific Credentials GRAFANA_ADMIN_PASSWORD=$(generate_password 16 strong) MONITORING_API_KEY=$(generate_password 32 hex) BACKUP_ENCRYPTION_KEY=$(generate_password 32 base64) SSL_PASSPHRASE=$(generate_password 20 strong) EOF fi # Set secure permissions chmod 600 "$creds_file" success "Credentials generated: $creds_file" return 0 } # Load credentials from file load_credentials() { local environment=$1 local creds_file="${CONFIG_CREDENTIALS_DIR}/${environment}.env" if [[ ! -f "$creds_file" ]]; then error "Credentials file not found: $creds_file" return 1 fi # Source the credentials file set -a # Export all variables source "$creds_file" set +a info "Credentials loaded for $environment environment" } # Validate environment template validate_template() { local template_file=$1 if [[ ! -f "$template_file" ]]; then error "Template file not found: $template_file" return 1 fi info "Validating template: $(basename "$template_file")" # Check for required placeholders local required_placeholders=( "*** REQUIRED ***" "DOMAIN_NAME=" "APP_ENV=" "DB_PASSWORD=" "APP_KEY=" ) local missing_placeholders=() for placeholder in "${required_placeholders[@]}"; do if ! grep -q "$placeholder" "$template_file"; then missing_placeholders+=("$placeholder") fi done if [[ ${#missing_placeholders[@]} -gt 0 ]]; then error "Template missing required placeholders:" printf ' - %s\n' "${missing_placeholders[@]}" return 1 fi success "Template validation passed" return 0 } # Apply configuration to template apply_configuration() { local environment=$1 local domain=$2 local email=$3 local additional_vars="${4:-}" local template_file="${CONFIG_TEMPLATES_DIR}/${environment}.env.template" local output_file="${CONFIG_TEMPLATES_DIR}/.env.${environment}" local creds_file="${CONFIG_CREDENTIALS_DIR}/${environment}.env" info "Creating $environment configuration from template..." # Validate template first if ! validate_template "$template_file"; then return 1 fi # Backup existing config if [[ -f "$output_file" ]]; then local backup_file="${CONFIG_BACKUP_DIR}/.env.${environment}.backup.$(date +%Y%m%d_%H%M%S)" cp "$output_file" "$backup_file" warn "Existing config backed up to: $backup_file" fi # Copy template to output cp "$template_file" "$output_file" # Apply basic configuration sed -i "s|DOMAIN_NAME=.*|DOMAIN_NAME=${domain}|g" "$output_file" sed -i "s|MAIL_FROM_ADDRESS=.*|MAIL_FROM_ADDRESS=${email}|g" "$output_file" sed -i "s|your-domain\.com|${domain}|g" "$output_file" sed -i "s|your-email@example\.com|${email}|g" "$output_file" # Apply environment-specific settings case $environment in "production") sed -i 's|APP_DEBUG=.*|APP_DEBUG=false|g' "$output_file" sed -i 's|LOG_LEVEL=.*|LOG_LEVEL=warning|g' "$output_file" sed -i 's|APP_ENV=.*|APP_ENV=production|g' "$output_file" ;; "staging") sed -i 's|APP_DEBUG=.*|APP_DEBUG=false|g' "$output_file" sed -i 's|LOG_LEVEL=.*|LOG_LEVEL=info|g' "$output_file" sed -i 's|APP_ENV=.*|APP_ENV=staging|g' "$output_file" ;; "development") sed -i 's|APP_DEBUG=.*|APP_DEBUG=true|g' "$output_file" sed -i 's|LOG_LEVEL=.*|LOG_LEVEL=debug|g' "$output_file" sed -i 's|APP_ENV=.*|APP_ENV=development|g' "$output_file" ;; esac # Apply credentials if available if [[ -f "$creds_file" ]]; then info "Applying generated credentials..." # Load credentials while IFS='=' read -r key value; do # Skip comments and empty lines [[ $key =~ ^#.*$ ]] || [[ -z $key ]] && continue # Remove any existing quotes value=$(echo "$value" | sed 's/^["'\'']*//;s/["'\'']*$//') # Apply to config if grep -q "^${key}=" "$output_file"; then sed -i "s|^${key}=.*|${key}=${value}|g" "$output_file" elif grep -q "${key}=\*\*\* REQUIRED \*\*\*" "$output_file"; then sed -i "s|${key}=\*\*\* REQUIRED \*\*\*|${key}=${value}|g" "$output_file" fi done < <(grep -v '^#' "$creds_file" | grep '=' || true) fi # Apply additional variables if provided if [[ -n "$additional_vars" ]]; then info "Applying additional configuration variables..." # Parse additional_vars as key=value pairs echo "$additional_vars" | tr ' ' '\n' | while IFS='=' read -r key value; do if [[ -n "$key" && -n "$value" ]]; then sed -i "s|^${key}=.*|${key}=${value}|g" "$output_file" fi done fi # Set secure permissions chmod 600 "$output_file" success "Configuration created: $output_file" return 0 } # Validate configuration file validate_configuration() { local config_file=$1 if [[ ! -f "$config_file" ]]; then error "Configuration file not found: $config_file" return 1 fi info "Validating configuration: $(basename "$config_file")" # Check for remaining placeholders local remaining_placeholders remaining_placeholders=$(grep "*** REQUIRED ***" "$config_file" || true) if [[ -n "$remaining_placeholders" ]]; then error "Configuration contains unfilled placeholders:" echo "$remaining_placeholders" return 1 fi # Check for critical configuration local required_vars=( "APP_ENV" "DOMAIN_NAME" "DB_PASSWORD" "APP_KEY" ) local missing_vars=() for var in "${required_vars[@]}"; do if ! grep -q "^${var}=" "$config_file"; then missing_vars+=("$var") fi done if [[ ${#missing_vars[@]} -gt 0 ]]; then error "Configuration missing required variables:" printf ' - %s\n' "${missing_vars[@]}" return 1 fi # Load and validate specific values local app_env app_env=$(grep "^APP_ENV=" "$config_file" | cut -d'=' -f2 | tr -d '"') if [[ "$app_env" == "production" ]]; then # Additional production validation local debug_mode debug_mode=$(grep "^APP_DEBUG=" "$config_file" | cut -d'=' -f2 | tr -d '"') if [[ "$debug_mode" == "true" ]]; then warn "Debug mode is enabled in production configuration" fi # Check password strength local db_password db_password=$(grep "^DB_PASSWORD=" "$config_file" | cut -d'=' -f2 | tr -d '"') if [[ ${#db_password} -lt 16 ]]; then error "Database password too short for production (minimum 16 characters)" return 1 fi fi success "Configuration validation passed" return 0 } # List available configurations list_configurations() { info "Available configurations:" for env_file in "${CONFIG_TEMPLATES_DIR}/.env."*; do if [[ -f "$env_file" && ! "$env_file" =~ \.template$ && ! "$env_file" =~ \.backup ]]; then local env_name env_name=$(basename "$env_file" | sed 's/\.env\.//') local app_env domain app_env=$(grep "^APP_ENV=" "$env_file" | cut -d'=' -f2 | tr -d '"' || echo "unknown") domain=$(grep "^DOMAIN_NAME=" "$env_file" | cut -d'=' -f2 | tr -d '"' || echo "unknown") echo " • $env_name: $app_env environment for $domain" fi done info "Available credentials:" for creds_file in "${CONFIG_CREDENTIALS_DIR}"/*.env; do if [[ -f "$creds_file" ]]; then local env_name env_name=$(basename "$creds_file" .env) local created created=$(stat -c %y "$creds_file" 2>/dev/null | cut -d' ' -f1 || echo "unknown") echo " • $env_name: created $created" fi done } # Rotate credentials rotate_credentials() { local environment=$1 warn "Rotating credentials for $environment environment..." warn "This will generate new passwords and invalidate existing ones!" printf "Continue with credential rotation? [y/N]: " read -r confirm if [[ ! $confirm =~ ^[Yy]$ ]]; then info "Credential rotation cancelled" return 0 fi generate_environment_credentials "$environment" success "Credentials rotated for $environment environment" warn "You must redeploy the application for changes to take effect!" } # Backup configurations backup_configurations() { local backup_name="config_backup_$(date +%Y%m%d_%H%M%S)" local backup_path="${CONFIG_BACKUP_DIR}/${backup_name}" info "Creating configuration backup: $backup_name" mkdir -p "$backup_path" # Backup environment files cp -r "$CONFIG_TEMPLATES_DIR" "${backup_path}/environments" # Backup credentials (if they exist) if [[ -d "$CONFIG_CREDENTIALS_DIR" && "$(ls -A "$CONFIG_CREDENTIALS_DIR" 2>/dev/null)" ]]; then cp -r "$CONFIG_CREDENTIALS_DIR" "${backup_path}/credentials" fi # Create backup manifest cat > "${backup_path}/manifest.txt" << EOF Configuration Backup: $backup_name Created: $(date) Custom PHP Framework Configuration Management Contents: - Environment configurations: environments/ - Secure credentials: credentials/ (if any) Restore with: cp -r ${backup_path}/environments/* ${CONFIG_TEMPLATES_DIR}/ cp -r ${backup_path}/credentials/* ${CONFIG_CREDENTIALS_DIR}/ EOF success "Configuration backup created: $backup_path" } # Show configuration info show_config_info() { local environment=$1 local config_file="${CONFIG_TEMPLATES_DIR}/.env.${environment}" if [[ ! -f "$config_file" ]]; then error "Configuration not found: $environment" return 1 fi info "Configuration information for $environment:" # Extract key information local domain app_env app_debug domain=$(grep "^DOMAIN_NAME=" "$config_file" | cut -d'=' -f2 | tr -d '"' || echo "unknown") app_env=$(grep "^APP_ENV=" "$config_file" | cut -d'=' -f2 | tr -d '"' || echo "unknown") app_debug=$(grep "^APP_DEBUG=" "$config_file" | cut -d'=' -f2 | tr -d '"' || echo "unknown") echo " Domain: $domain" echo " Environment: $app_env" echo " Debug mode: $app_debug" echo " File: $config_file" echo " Size: $(stat -c%s "$config_file" 2>/dev/null || echo "unknown") bytes" echo " Modified: $(stat -c%y "$config_file" 2>/dev/null | cut -d' ' -f1 || echo "unknown")" # Check for credentials local creds_file="${CONFIG_CREDENTIALS_DIR}/${environment}.env" if [[ -f "$creds_file" ]]; then echo " Credentials: Available ($(stat -c%y "$creds_file" 2>/dev/null | cut -d' ' -f1))" else echo " Credentials: Not generated" fi } # Command-line interface if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then # Initialize init_config_directories case "${1:-help}" in "generate-credentials") generate_environment_credentials "${2:-production}" ;; "apply-config") apply_configuration "$2" "$3" "$4" "${5:-}" ;; "validate") validate_configuration "${CONFIG_TEMPLATES_DIR}/.env.${2:-production}" ;; "list") list_configurations ;; "rotate") rotate_credentials "${2:-production}" ;; "backup") backup_configurations ;; "info") show_config_info "${2:-production}" ;; "help"|*) cat << EOF ${CYAN}Configuration Manager for Custom PHP Framework${NC} ${YELLOW}Usage:${NC} $0 [options] ${YELLOW}Commands:${NC} generate-credentials Generate secure credentials for environment apply-config [vars] Create config from template validate Validate configuration file list List available configurations rotate Rotate credentials for environment backup Backup all configurations info Show configuration information help Show this help message ${YELLOW}Examples:${NC} $0 generate-credentials production $0 apply-config production michaelschiemer.de kontakt@michaelschiemer.de $0 validate production $0 list $0 backup EOF ;; esac fi