#!/bin/bash # Setup Secrets Management # This script implements Docker secrets and environment-based configuration set -euo pipefail echo "🔐 Setting up secrets management..." # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Function to print colored output print_status() { echo -e "${GREEN}[INFO]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } print_step() { echo -e "${BLUE}[STEP]${NC} $1" } # Configuration MANAGER_HOST="omv800" SECRETS_DIR="/opt/migration/secrets" CONFIG_DIR="/opt/migration/configs" ENV_FILE="/opt/migration/.env" # 1. Create secrets directory with proper permissions print_step "Step 1: Creating secrets directory structure..." mkdir -p "$SECRETS_DIR/generated" mkdir -p "$SECRETS_DIR/templates" chmod 700 "$SECRETS_DIR" chmod 700 "$SECRETS_DIR/generated" # 2. Generate strong passwords and keys print_step "Step 2: Generating secure passwords and keys..." # Function to generate secure passwords generate_password() { openssl rand -base64 32 | tr -d "=+/" | cut -c1-25 } # Generate passwords TRAEFIK_ADMIN_PASSWORD=$(generate_password) TRAEFIK_MIGRATION_PASSWORD=$(generate_password) POSTGRES_PASSWORD=$(generate_password) REDIS_PASSWORD=$(generate_password) JWT_SECRET=$(openssl rand -base64 64 | tr -d "=+/") # Generate htpasswd hashes TRAEFIK_ADMIN_HASH=$(htpasswd -nbB admin "$TRAEFIK_ADMIN_PASSWORD" | cut -d: -f2) TRAEFIK_MIGRATION_HASH=$(htpasswd -nbB migration "$TRAEFIK_MIGRATION_PASSWORD" | cut -d: -f2) print_status "Generated secure passwords and hashes" # 3. Create environment configuration file print_step "Step 3: Creating environment configuration..." cat > "$ENV_FILE" << EOF # Migration Environment Configuration # Generated: $(date) # IMPORTANT: This file contains sensitive information - do not commit to version control # Domain Configuration DOMAIN=homelab.local EMAIL=admin@homelab.local TIMEZONE=America/New_York # Network Configuration MANAGER_HOST=omv800 MANAGER_IP=192.168.50.229 # Database Configuration POSTGRES_USER=postgres POSTGRES_DB=migration_db REDIS_USER=default # SSL Configuration SSL_KEY_SIZE=4096 SSL_COUNTRY=US SSL_STATE=State SSL_CITY=City SSL_ORG=HomeLab SSL_OU=IT # Monitoring Configuration GRAFANA_ADMIN_USER=admin PROMETHEUS_RETENTION=30d # Backup Configuration BACKUP_RETENTION_DAYS=30 BACKUP_COMPRESSION=gzip # Security Configuration SESSION_TIMEOUT=3600 MAX_LOGIN_ATTEMPTS=5 LOCKOUT_DURATION=900 # Feature Flags ENABLE_METRICS=true ENABLE_DEBUG=false ENABLE_TRACING=false EOF # Add sensitive values (these will be moved to Docker secrets) cat >> "$ENV_FILE" << EOF # Sensitive Configuration (will be moved to Docker secrets) TRAEFIK_ADMIN_PASSWORD=$TRAEFIK_ADMIN_PASSWORD TRAEFIK_MIGRATION_PASSWORD=$TRAEFIK_MIGRATION_PASSWORD POSTGRES_PASSWORD=$POSTGRES_PASSWORD REDIS_PASSWORD=$REDIS_PASSWORD JWT_SECRET=$JWT_SECRET EOF chmod 600 "$ENV_FILE" print_status "Environment configuration created: $ENV_FILE" # 4. Create Docker secrets print_step "Step 4: Creating Docker secrets..." # Create secret files echo -n "$TRAEFIK_ADMIN_PASSWORD" > "$SECRETS_DIR/generated/traefik_admin_password" echo -n "$TRAEFIK_MIGRATION_PASSWORD" > "$SECRETS_DIR/generated/traefik_migration_password" echo -n "$POSTGRES_PASSWORD" > "$SECRETS_DIR/generated/postgres_password" echo -n "$REDIS_PASSWORD" > "$SECRETS_DIR/generated/redis_password" echo -n "$JWT_SECRET" > "$SECRETS_DIR/generated/jwt_secret" # Create users file for Traefik cat > "$SECRETS_DIR/generated/traefik_users" << EOF admin:\$2y\$10\$$TRAEFIK_ADMIN_HASH migration:\$2y\$10\$$TRAEFIK_MIGRATION_HASH EOF # Set proper permissions chmod 600 "$SECRETS_DIR"/generated/* # Deploy secrets to Docker Swarm ssh "$MANAGER_HOST" "docker secret rm traefik_admin_password 2>/dev/null || true" ssh "$MANAGER_HOST" "docker secret rm traefik_migration_password 2>/dev/null || true" ssh "$MANAGER_HOST" "docker secret rm postgres_password 2>/dev/null || true" ssh "$MANAGER_HOST" "docker secret rm redis_password 2>/dev/null || true" ssh "$MANAGER_HOST" "docker secret rm jwt_secret 2>/dev/null || true" ssh "$MANAGER_HOST" "docker secret rm traefik_users 2>/dev/null || true" # Copy secrets to manager and create Docker secrets scp "$SECRETS_DIR/generated/traefik_admin_password" "$MANAGER_HOST:/tmp/" scp "$SECRETS_DIR/generated/traefik_migration_password" "$MANAGER_HOST:/tmp/" scp "$SECRETS_DIR/generated/postgres_password" "$MANAGER_HOST:/tmp/" scp "$SECRETS_DIR/generated/redis_password" "$MANAGER_HOST:/tmp/" scp "$SECRETS_DIR/generated/jwt_secret" "$MANAGER_HOST:/tmp/" scp "$SECRETS_DIR/generated/traefik_users" "$MANAGER_HOST:/tmp/" ssh "$MANAGER_HOST" "docker secret create traefik_admin_password /tmp/traefik_admin_password" ssh "$MANAGER_HOST" "docker secret create traefik_migration_password /tmp/traefik_migration_password" ssh "$MANAGER_HOST" "docker secret create postgres_password /tmp/postgres_password" ssh "$MANAGER_HOST" "docker secret create redis_password /tmp/redis_password" ssh "$MANAGER_HOST" "docker secret create jwt_secret /tmp/jwt_secret" ssh "$MANAGER_HOST" "docker secret create traefik_users /tmp/traefik_users" # Clean up temporary files on manager ssh "$MANAGER_HOST" "rm -f /tmp/traefik_admin_password /tmp/traefik_migration_password /tmp/postgres_password /tmp/redis_password /tmp/jwt_secret /tmp/traefik_users" print_status "Docker secrets created successfully" # 5. Create secure configuration templates print_step "Step 5: Creating secure configuration templates..." # Updated Traefik configuration template cat > "$SECRETS_DIR/templates/traefik-secure.yml" << 'EOF' version: '3.8' services: traefik: image: traefik:v3.0 command: # API and dashboard - --api.dashboard=true - --api.insecure=false # Docker provider - --providers.docker.swarmMode=true - --providers.docker.exposedbydefault=false - --providers.docker.network=traefik-public # Entry points - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 - --entrypoints.web.http.redirections.entrypoint.to=websecure - --entrypoints.web.http.redirections.entrypoint.scheme=https # SSL/TLS configuration - --certificatesresolvers.letsencrypt.acme.email=${EMAIL} - --certificatesresolvers.letsencrypt.acme.storage=/certificates/acme.json - --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web # Security - --global.sendanonymoususage=false - --global.checknewversion=false # Logging - --log.level=INFO - --log.format=json - --accesslog=true - --accesslog.filepath=/var/log/traefik/access.log - --accesslog.format=json # Metrics - --metrics.prometheus=true - --metrics.prometheus.addEntryPointsLabels=true - --metrics.prometheus.addServicesLabels=true # Health checks - --ping=true - --ping.entryPoint=web # File provider for static configuration - --providers.file.directory=/etc/traefik/dynamic - --providers.file.watch=true ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - traefik-certificates:/certificates - traefik-logs:/var/log/traefik - ./dynamic:/etc/traefik/dynamic:ro secrets: - traefik_users networks: - traefik-public environment: - DOMAIN=${DOMAIN} - EMAIL=${EMAIL} deploy: placement: constraints: - node.role == manager replicas: 2 resources: limits: memory: 512M cpus: '0.5' reservations: memory: 256M cpus: '0.25' labels: # Traefik dashboard with secret-based auth - "traefik.enable=true" - "traefik.http.routers.traefik-dashboard.rule=Host(`traefik.${DOMAIN}`)" - "traefik.http.routers.traefik-dashboard.entrypoints=websecure" - "traefik.http.routers.traefik-dashboard.tls.certresolver=letsencrypt" - "traefik.http.routers.traefik-dashboard.service=api@internal" - "traefik.http.routers.traefik-dashboard.middlewares=auth-secure@file" restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s secrets: traefik_users: external: true volumes: traefik-certificates: driver: local traefik-logs: driver: local networks: traefik-public: external: true EOF # Updated middleware configuration with secrets cat > "$SECRETS_DIR/templates/middleware-secure.yml" << 'EOF' # Traefik Dynamic Configuration - Secure Middleware # Uses Docker secrets for authentication http: middlewares: # Secure authentication middleware using Docker secrets auth-secure: basicAuth: usersFile: "/run/secrets/traefik_users" removeHeader: true realm: "HomeLabSecure" # Enhanced security headers security-headers-enhanced: headers: # Security headers frameDeny: true sslRedirect: true browserXssFilter: true contentTypeNosniff: true forceSTSHeader: true sslForceHost: true stsIncludeSubdomains: true stsPreload: true stsSeconds: 63072000 # 2 years customFrameOptionsValue: "SAMEORIGIN" customRequestHeaders: X-Forwarded-Proto: "https" customResponseHeaders: X-Robots-Tag: "none" X-Content-Type-Options: "nosniff" X-Frame-Options: "SAMEORIGIN" X-XSS-Protection: "1; mode=block" Referrer-Policy: "strict-origin-when-cross-origin" Permissions-Policy: "camera=(), microphone=(), geolocation=(), payment=()" Content-Security-Policy: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'" # Stricter rate limiting for production rate-limit-strict: rateLimit: burst: 20 average: 10 period: "1s" sourceCriterion: ipStrategy: depth: 1 # IP whitelist for admin interfaces ip-whitelist-strict: ipWhiteList: sourceRange: - "192.168.50.0/24" # Local network only ipStrategy: depth: 1 excludedIPs: - "127.0.0.1" EOF print_status "Secure configuration templates created" # 6. Create script to update existing configurations print_step "Step 6: Creating configuration update script..." cat > "/opt/migration/scripts/update_configurations.sh" << 'EOF' #!/bin/bash # Update existing configurations to use secrets management set -euo pipefail # Load environment variables source /opt/migration/.env echo "🔧 Updating configurations to use secrets management..." # Update Traefik deployment echo "Updating Traefik configuration..." envsubst < /opt/migration/secrets/templates/traefik-secure.yml > /opt/migration/configs/traefik/docker-compose-secure.yml # Update middleware configuration cp /opt/migration/secrets/templates/middleware-secure.yml /opt/migration/configs/traefik/dynamic/middleware-secure.yml # Create deployment script with secrets cat > /opt/migration/scripts/deploy_traefik_secure.sh << 'SCRIPT_EOF' #!/bin/bash # Deploy Traefik with secrets management set -euo pipefail source /opt/migration/.env echo "🌐 Deploying Traefik with secrets management..." cd /opt/migration/configs/traefik docker stack deploy -c docker-compose-secure.yml traefik-secure echo "✅ Traefik deployed with secrets management" SCRIPT_EOF chmod +x /opt/migration/scripts/deploy_traefik_secure.sh echo "✅ Configurations updated successfully" EOF chmod +x "/opt/migration/scripts/update_configurations.sh" # 7. Create secrets rotation script print_step "Step 7: Creating secrets rotation script..." cat > "/opt/migration/scripts/rotate_secrets.sh" << 'EOF' #!/bin/bash # Rotate Docker secrets safely set -euo pipefail echo "🔄 Rotating Docker secrets..." MANAGER_HOST="omv800" SECRETS_DIR="/opt/migration/secrets" # Function to rotate a secret rotate_secret() { local secret_name=$1 local secret_file=$2 echo "Rotating secret: $secret_name" # Generate new secret value case $secret_name in "*password*") new_value=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-25) ;; "jwt_secret") new_value=$(openssl rand -base64 64 | tr -d "=+/") ;; *) echo "Unknown secret type: $secret_name" return 1 ;; esac # Create new secret file echo -n "$new_value" > "$secret_file.new" chmod 600 "$secret_file.new" # Create new Docker secret ssh "$MANAGER_HOST" "docker secret create ${secret_name}_new /tmp/${secret_name}.new" # Update services to use new secret (this would need service-specific logic) echo "âš ī¸ Manual service update required for $secret_name" # After successful deployment, remove old secret # ssh "$MANAGER_HOST" "docker secret rm $secret_name" # ssh "$MANAGER_HOST" "docker secret create $secret_name /tmp/${secret_name}.new" echo "✅ Secret $secret_name rotated successfully" } echo "âš ī¸ Secret rotation requires manual service updates" echo "Use this script as a template for implementing zero-downtime secret rotation" EOF chmod +x "/opt/migration/scripts/rotate_secrets.sh" # 8. Create secrets backup script print_step "Step 8: Creating secrets backup script..." cat > "/opt/migration/scripts/backup_secrets.sh" << 'EOF' #!/bin/bash # Backup secrets securely set -euo pipefail echo "💾 Backing up secrets..." BACKUP_DIR="/opt/migration/backups/secrets/$(date +%Y%m%d_%H%M%S)" mkdir -p "$BACKUP_DIR" # Backup environment file (encrypted) gpg --cipher-algo AES256 --compress-algo 1 --s2k-mode 3 \ --s2k-digest-algo SHA512 --s2k-count 65536 --symmetric \ --output "$BACKUP_DIR/.env.gpg" /opt/migration/.env # Backup secret files (encrypted) tar czf - /opt/migration/secrets/generated | \ gpg --cipher-algo AES256 --compress-algo 1 --s2k-mode 3 \ --s2k-digest-algo SHA512 --s2k-count 65536 --symmetric \ --output "$BACKUP_DIR/secrets.tar.gz.gpg" # Set secure permissions chmod 700 "$BACKUP_DIR" chmod 600 "$BACKUP_DIR"/* echo "✅ Secrets backed up to: $BACKUP_DIR" echo "â„šī¸ Use GPG to decrypt: gpg --decrypt file.gpg" EOF chmod +x "/opt/migration/scripts/backup_secrets.sh" # 9. Create validation script print_step "Step 9: Creating secrets validation script..." cat > "/opt/migration/scripts/validate_secrets.sh" << 'EOF' #!/bin/bash # Validate secrets configuration set -euo pipefail echo "✅ Validating secrets configuration..." MANAGER_HOST="omv800" ENV_FILE="/opt/migration/.env" SECRETS_DIR="/opt/migration/secrets" # Check if environment file exists and is readable if [[ -r "$ENV_FILE" ]]; then echo "✅ Environment file exists and is readable" else echo "❌ Environment file missing or not readable" exit 1 fi # Check if secrets directory has correct permissions if [[ -d "$SECRETS_DIR" ]] && [[ $(stat -c %a "$SECRETS_DIR") == "700" ]]; then echo "✅ Secrets directory has correct permissions" else echo "❌ Secrets directory permissions incorrect" exit 1 fi # Check if Docker secrets exist echo "Checking Docker secrets..." secrets=( "traefik_admin_password" "traefik_migration_password" "postgres_password" "redis_password" "jwt_secret" "traefik_users" ) for secret in "${secrets[@]}"; do if ssh "$MANAGER_HOST" "docker secret ls | grep -q $secret"; then echo "✅ Docker secret exists: $secret" else echo "❌ Docker secret missing: $secret" exit 1 fi done # Validate environment variables source "$ENV_FILE" required_vars=( "DOMAIN" "EMAIL" "MANAGER_HOST" "POSTGRES_PASSWORD" ) for var in "${required_vars[@]}"; do if [[ -n "${!var}" ]]; then echo "✅ Environment variable set: $var" else echo "❌ Environment variable missing: $var" exit 1 fi done echo "✅ All secrets validation checks passed" EOF chmod +x "/opt/migration/scripts/validate_secrets.sh" # 10. Create summary print_step "Step 10: Creating setup summary..." cat > "/opt/migration/secrets_setup_summary.txt" << EOF Secrets Management Setup Summary Generated: $(date) Files Created: - Environment config: $ENV_FILE - Secrets directory: $SECRETS_DIR/ - Traefik secure template: $SECRETS_DIR/templates/traefik-secure.yml - Middleware secure template: $SECRETS_DIR/templates/middleware-secure.yml Scripts Created: - Update configurations: /opt/migration/scripts/update_configurations.sh - Rotate secrets: /opt/migration/scripts/rotate_secrets.sh - Backup secrets: /opt/migration/scripts/backup_secrets.sh - Validate secrets: /opt/migration/scripts/validate_secrets.sh Docker Secrets Created: - traefik_admin_password - traefik_migration_password - postgres_password - redis_password - jwt_secret - traefik_users Generated Credentials: - Traefik Admin User: admin - Traefik Admin Password: $TRAEFIK_ADMIN_PASSWORD - Traefik Migration User: migration - Traefik Migration Password: $TRAEFIK_MIGRATION_PASSWORD - PostgreSQL Password: $POSTGRES_PASSWORD - Redis Password: $REDIS_PASSWORD Next Steps: 1. Update .gitignore to exclude $ENV_FILE 2. Run: /opt/migration/scripts/update_configurations.sh 3. Run: /opt/migration/scripts/validate_secrets.sh 4. Deploy with: /opt/migration/scripts/deploy_traefik_secure.sh Security Notes: - All passwords are 25 characters with high entropy - Secrets are stored in Docker secrets (encrypted at rest) - Environment file has 600 permissions - Backup scripts use GPG encryption - Rotation scripts provided for regular updates EOF print_status "✅ Secrets management setup completed successfully!" print_status "📋 Summary saved to: /opt/migration/secrets_setup_summary.txt" echo "" print_status "Generated credentials (SAVE THESE SECURELY):" echo " Traefik Admin: admin / $TRAEFIK_ADMIN_PASSWORD" echo " Traefik Migration: migration / $TRAEFIK_MIGRATION_PASSWORD" echo "" print_warning "Remember to:" echo " 1. Add $ENV_FILE to .gitignore" echo " 2. Store credentials in password manager" echo " 3. Run validation: /opt/migration/scripts/validate_secrets.sh"