263 lines
6.8 KiB
Bash
Executable File
263 lines
6.8 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Production Secrets Setup Script
|
|
# Purpose: Initialize and manage production secrets with Ansible Vault
|
|
#
|
|
# Usage:
|
|
# ./scripts/setup-production-secrets.sh init # Initialize new vault
|
|
# ./scripts/setup-production-secrets.sh deploy # Deploy secrets to production
|
|
# ./scripts/setup-production-secrets.sh rotate # Rotate secrets
|
|
# ./scripts/setup-production-secrets.sh verify # Verify secrets on server
|
|
#
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
ANSIBLE_DIR="${PROJECT_ROOT}/deployment/ansible"
|
|
VAULT_FILE="${ANSIBLE_DIR}/secrets/production-vault.yml"
|
|
INVENTORY="${ANSIBLE_DIR}/inventory/production.yml"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Logging functions
|
|
log_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Check prerequisites
|
|
check_prerequisites() {
|
|
log_info "Checking prerequisites..."
|
|
|
|
if ! command -v ansible-vault &> /dev/null; then
|
|
log_error "ansible-vault not found. Please install Ansible."
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v openssl &> /dev/null; then
|
|
log_error "openssl not found. Please install OpenSSL."
|
|
exit 1
|
|
fi
|
|
|
|
log_info "Prerequisites OK"
|
|
}
|
|
|
|
# Generate secure random password
|
|
generate_password() {
|
|
local length="${1:-32}"
|
|
openssl rand -base64 "$length" | tr -d "=+/" | cut -c1-"$length"
|
|
}
|
|
|
|
# Generate base64 encoded app key
|
|
generate_app_key() {
|
|
openssl rand -base64 32
|
|
}
|
|
|
|
# Initialize vault with secure defaults
|
|
init_vault() {
|
|
log_info "Initializing production secrets vault..."
|
|
|
|
if [[ -f "$VAULT_FILE" ]]; then
|
|
log_warn "Vault file already exists: $VAULT_FILE"
|
|
read -p "Do you want to overwrite it? (yes/no): " -r
|
|
if [[ ! $REPLY =~ ^[Yy]es$ ]]; then
|
|
log_info "Aborting initialization"
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
# Generate secure secrets
|
|
log_info "Generating secure secrets..."
|
|
DB_PASSWORD=$(generate_password 32)
|
|
REDIS_PASSWORD=$(generate_password 32)
|
|
APP_KEY=$(generate_app_key)
|
|
JWT_SECRET=$(generate_password 64)
|
|
REGISTRY_PASSWORD=$(generate_password 24)
|
|
|
|
# Create vault file
|
|
cat > "$VAULT_FILE" <<EOF
|
|
---
|
|
# Production Secrets Vault
|
|
# Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
|
|
|
# Database Credentials
|
|
vault_db_name: framework_production
|
|
vault_db_user: framework_app
|
|
vault_db_password: ${DB_PASSWORD}
|
|
|
|
# Redis Credentials
|
|
vault_redis_password: ${REDIS_PASSWORD}
|
|
|
|
# Application Secrets
|
|
vault_app_key: ${APP_KEY}
|
|
vault_jwt_secret: ${JWT_SECRET}
|
|
|
|
# Docker Registry Credentials
|
|
vault_registry_url: git.michaelschiemer.de:5000
|
|
vault_registry_user: deploy
|
|
vault_registry_password: ${REGISTRY_PASSWORD}
|
|
|
|
# Security Configuration
|
|
vault_admin_allowed_ips: "127.0.0.1,::1,94.16.110.151"
|
|
|
|
# SMTP Configuration (update these manually)
|
|
vault_smtp_host: smtp.example.com
|
|
vault_smtp_port: 587
|
|
vault_smtp_user: noreply@michaelschiemer.de
|
|
vault_smtp_password: CHANGE_ME_SMTP_PASSWORD_HERE
|
|
EOF
|
|
|
|
log_info "Vault file created with generated secrets"
|
|
log_warn "IMPORTANT: Update SMTP credentials manually if needed"
|
|
|
|
# Encrypt vault
|
|
log_info "Encrypting vault file..."
|
|
ansible-vault encrypt "$VAULT_FILE"
|
|
|
|
log_info "✅ Vault initialized successfully"
|
|
log_warn "Store the vault password securely (e.g., in password manager)"
|
|
}
|
|
|
|
# Deploy secrets to production
|
|
deploy_secrets() {
|
|
log_info "Deploying secrets to production..."
|
|
|
|
if [[ ! -f "$VAULT_FILE" ]]; then
|
|
log_error "Vault file not found: $VAULT_FILE"
|
|
log_error "Run './setup-production-secrets.sh init' first"
|
|
exit 1
|
|
fi
|
|
|
|
cd "$ANSIBLE_DIR"
|
|
|
|
log_info "Running Ansible playbook..."
|
|
ansible-playbook \
|
|
-i "$INVENTORY" \
|
|
playbooks/setup-production-secrets.yml \
|
|
--ask-vault-pass
|
|
|
|
log_info "✅ Secrets deployed successfully"
|
|
}
|
|
|
|
# Rotate secrets (regenerate and redeploy)
|
|
rotate_secrets() {
|
|
log_warn "⚠️ Secret rotation will:"
|
|
log_warn " 1. Generate new passwords/keys"
|
|
log_warn " 2. Update vault file"
|
|
log_warn " 3. Deploy to production"
|
|
log_warn " 4. Restart services"
|
|
log_warn ""
|
|
read -p "Continue with rotation? (yes/no): " -r
|
|
|
|
if [[ ! $REPLY =~ ^[Yy]es$ ]]; then
|
|
log_info "Rotation cancelled"
|
|
exit 0
|
|
fi
|
|
|
|
# Backup current vault
|
|
BACKUP_FILE="${VAULT_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
|
|
log_info "Creating backup: $BACKUP_FILE"
|
|
cp "$VAULT_FILE" "$BACKUP_FILE"
|
|
|
|
# Decrypt vault
|
|
log_info "Decrypting vault..."
|
|
ansible-vault decrypt "$VAULT_FILE"
|
|
|
|
# Generate new secrets
|
|
log_info "Generating new secrets..."
|
|
DB_PASSWORD=$(generate_password 32)
|
|
REDIS_PASSWORD=$(generate_password 32)
|
|
APP_KEY=$(generate_app_key)
|
|
JWT_SECRET=$(generate_password 64)
|
|
|
|
# Update vault file (keep registry password)
|
|
sed -i "s/vault_db_password: .*/vault_db_password: ${DB_PASSWORD}/" "$VAULT_FILE"
|
|
sed -i "s/vault_redis_password: .*/vault_redis_password: ${REDIS_PASSWORD}/" "$VAULT_FILE"
|
|
sed -i "s/vault_app_key: .*/vault_app_key: ${APP_KEY}/" "$VAULT_FILE"
|
|
sed -i "s/vault_jwt_secret: .*/vault_jwt_secret: ${JWT_SECRET}/" "$VAULT_FILE"
|
|
|
|
# Re-encrypt vault
|
|
log_info "Re-encrypting vault..."
|
|
ansible-vault encrypt "$VAULT_FILE"
|
|
|
|
log_info "✅ Secrets rotated"
|
|
log_info "Backup saved to: $BACKUP_FILE"
|
|
|
|
# Deploy rotated secrets
|
|
deploy_secrets
|
|
}
|
|
|
|
# Verify secrets on server
|
|
verify_secrets() {
|
|
log_info "Verifying secrets on production server..."
|
|
|
|
cd "$ANSIBLE_DIR"
|
|
|
|
ansible production_server \
|
|
-i "$INVENTORY" \
|
|
-m shell \
|
|
-a "docker secret ls"
|
|
|
|
log_info "Checking environment file..."
|
|
ansible production_server \
|
|
-i "$INVENTORY" \
|
|
-m stat \
|
|
-a "path=/home/deploy/secrets/.env.production"
|
|
|
|
log_info "✅ Verification complete"
|
|
}
|
|
|
|
# Main command dispatcher
|
|
main() {
|
|
check_prerequisites
|
|
|
|
case "${1:-help}" in
|
|
init)
|
|
init_vault
|
|
;;
|
|
deploy)
|
|
deploy_secrets
|
|
;;
|
|
rotate)
|
|
rotate_secrets
|
|
;;
|
|
verify)
|
|
verify_secrets
|
|
;;
|
|
help|*)
|
|
cat <<EOF
|
|
Production Secrets Management
|
|
|
|
Usage: $0 <command>
|
|
|
|
Commands:
|
|
init Initialize new secrets vault with auto-generated secure values
|
|
deploy Deploy secrets from vault to production server
|
|
rotate Rotate secrets (generate new values and redeploy)
|
|
verify Verify secrets are properly deployed on server
|
|
|
|
Examples:
|
|
$0 init # First time setup
|
|
$0 deploy # Deploy after manual vault updates
|
|
$0 rotate # Monthly security rotation
|
|
$0 verify # Check deployment status
|
|
|
|
EOF
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|