198 lines
5.9 KiB
Bash
198 lines
5.9 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
#
|
||
|
|
# Credential Rotation Script
|
||
|
|
#
|
||
|
|
# Per DoD/MilSpec requirements (NIST SP 800-53: SC-12, IA-5)
|
||
|
|
# This script assists with rotating credentials across the system
|
||
|
|
#
|
||
|
|
# Usage:
|
||
|
|
# ./scripts/rotate-credentials.sh [credential-type]
|
||
|
|
#
|
||
|
|
# Credential types:
|
||
|
|
# - jwt: JWT signing secret
|
||
|
|
# - db: Database password
|
||
|
|
# - keycloak: Keycloak client secret
|
||
|
|
# - proxmox: Proxmox API tokens
|
||
|
|
# - all: Rotate all credentials
|
||
|
|
#
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
# 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"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Generate a secure random secret
|
||
|
|
generate_secret() {
|
||
|
|
local length=${1:-64}
|
||
|
|
openssl rand -base64 $length | tr -d '\n' | head -c $length
|
||
|
|
}
|
||
|
|
|
||
|
|
# Rotate JWT secret
|
||
|
|
rotate_jwt_secret() {
|
||
|
|
log_info "Rotating JWT secret..."
|
||
|
|
|
||
|
|
local new_secret=$(generate_secret 64)
|
||
|
|
|
||
|
|
# Update in environment or secret management system
|
||
|
|
if [ -f .env ]; then
|
||
|
|
if grep -q "^JWT_SECRET=" .env; then
|
||
|
|
sed -i.bak "s/^JWT_SECRET=.*/JWT_SECRET=${new_secret}/" .env
|
||
|
|
log_info "Updated JWT_SECRET in .env (backup created: .env.bak)"
|
||
|
|
else
|
||
|
|
echo "JWT_SECRET=${new_secret}" >> .env
|
||
|
|
log_info "Added JWT_SECRET to .env"
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
log_warn ".env file not found. Please set JWT_SECRET=${new_secret} manually"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# If using Kubernetes secrets
|
||
|
|
if command -v kubectl &> /dev/null; then
|
||
|
|
log_info "Updating Kubernetes secret..."
|
||
|
|
kubectl create secret generic jwt-secret \
|
||
|
|
--from-literal=secret="${new_secret}" \
|
||
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
||
|
|
fi
|
||
|
|
|
||
|
|
log_warn "IMPORTANT: Restart all services using JWT_SECRET after rotation"
|
||
|
|
log_warn "All existing JWT tokens will be invalidated"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Rotate database password
|
||
|
|
rotate_db_password() {
|
||
|
|
log_info "Rotating database password..."
|
||
|
|
|
||
|
|
local new_password=$(generate_secret 32)
|
||
|
|
|
||
|
|
# Update in environment
|
||
|
|
if [ -f .env ]; then
|
||
|
|
if grep -q "^DB_PASSWORD=" .env; then
|
||
|
|
sed -i.bak "s/^DB_PASSWORD=.*/DB_PASSWORD=${new_password}/" .env
|
||
|
|
log_info "Updated DB_PASSWORD in .env (backup created: .env.bak)"
|
||
|
|
else
|
||
|
|
echo "DB_PASSWORD=${new_password}" >> .env
|
||
|
|
log_info "Added DB_PASSWORD to .env"
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
log_warn ".env file not found. Please set DB_PASSWORD=${new_password} manually"
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Update database password
|
||
|
|
log_warn "IMPORTANT: You must update the database password manually:"
|
||
|
|
log_warn " ALTER USER postgres WITH PASSWORD '${new_password}';"
|
||
|
|
|
||
|
|
# If using Kubernetes secrets
|
||
|
|
if command -v kubectl &> /dev/null; then
|
||
|
|
log_info "Updating Kubernetes secret..."
|
||
|
|
kubectl create secret generic db-credentials \
|
||
|
|
--from-literal=password="${new_password}" \
|
||
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# Rotate Keycloak client secret
|
||
|
|
rotate_keycloak_secret() {
|
||
|
|
log_info "Rotating Keycloak client secret..."
|
||
|
|
|
||
|
|
local new_secret=$(generate_secret 32)
|
||
|
|
|
||
|
|
if [ -f .env ]; then
|
||
|
|
if grep -q "^KEYCLOAK_CLIENT_SECRET=" .env; then
|
||
|
|
sed -i.bak "s/^KEYCLOAK_CLIENT_SECRET=.*/KEYCLOAK_CLIENT_SECRET=${new_secret}/" .env
|
||
|
|
log_info "Updated KEYCLOAK_CLIENT_SECRET in .env (backup created: .env.bak)"
|
||
|
|
else
|
||
|
|
echo "KEYCLOAK_CLIENT_SECRET=${new_secret}" >> .env
|
||
|
|
log_info "Added KEYCLOAK_CLIENT_SECRET to .env"
|
||
|
|
fi
|
||
|
|
else
|
||
|
|
log_warn ".env file not found. Please set KEYCLOAK_CLIENT_SECRET=${new_secret} manually"
|
||
|
|
fi
|
||
|
|
|
||
|
|
log_warn "IMPORTANT: Update Keycloak client secret in Keycloak admin console"
|
||
|
|
log_warn "All existing Keycloak sessions will be invalidated"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Rotate Proxmox API tokens
|
||
|
|
rotate_proxmox_tokens() {
|
||
|
|
log_info "Rotating Proxmox API tokens..."
|
||
|
|
|
||
|
|
log_warn "Proxmox API tokens must be rotated manually:"
|
||
|
|
log_warn "1. Log into Proxmox web interface"
|
||
|
|
log_warn "2. Go to Datacenter -> Permissions -> API Tokens"
|
||
|
|
log_warn "3. Revoke old tokens and create new ones"
|
||
|
|
log_warn "4. Update tokens in Kubernetes secrets or configuration"
|
||
|
|
|
||
|
|
# If using Kubernetes secrets
|
||
|
|
if command -v kubectl &> /dev/null; then
|
||
|
|
log_info "To update Kubernetes secret after creating new token:"
|
||
|
|
log_info " kubectl create secret generic proxmox-credentials \\"
|
||
|
|
log_info " --from-literal=credentials.json='{\"username\":\"root@pam\",\"token\":\"NEW_TOKEN\"}' \\"
|
||
|
|
log_info " --dry-run=client -o yaml | kubectl apply -f -"
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
# Main function
|
||
|
|
main() {
|
||
|
|
local credential_type=${1:-all}
|
||
|
|
|
||
|
|
log_info "Starting credential rotation (DoD/MilSpec compliance)"
|
||
|
|
log_info "Credential type: ${credential_type}"
|
||
|
|
|
||
|
|
case "${credential_type}" in
|
||
|
|
jwt)
|
||
|
|
rotate_jwt_secret
|
||
|
|
;;
|
||
|
|
db|database)
|
||
|
|
rotate_db_password
|
||
|
|
;;
|
||
|
|
keycloak)
|
||
|
|
rotate_keycloak_secret
|
||
|
|
;;
|
||
|
|
proxmox)
|
||
|
|
rotate_proxmox_tokens
|
||
|
|
;;
|
||
|
|
all)
|
||
|
|
log_info "Rotating all credentials..."
|
||
|
|
rotate_jwt_secret
|
||
|
|
echo ""
|
||
|
|
rotate_db_password
|
||
|
|
echo ""
|
||
|
|
rotate_keycloak_secret
|
||
|
|
echo ""
|
||
|
|
rotate_proxmox_tokens
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
log_error "Unknown credential type: ${credential_type}"
|
||
|
|
echo "Usage: $0 [jwt|db|keycloak|proxmox|all]"
|
||
|
|
exit 1
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
|
||
|
|
log_info "Credential rotation complete"
|
||
|
|
log_warn "Remember to:"
|
||
|
|
log_warn " 1. Restart all affected services"
|
||
|
|
log_warn " 2. Verify services are working correctly"
|
||
|
|
log_warn " 3. Update any documentation with new rotation date"
|
||
|
|
log_warn " 4. Archive old credentials securely (if required by policy)"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Run main function
|
||
|
|
main "$@"
|
||
|
|
|