#!/usr/bin/env bash # # Migrate legacy configuration structure to consolidated etc/ # # This script migrates: # - certificates/ -> etc/certificates/ # - config/ -> etc/crypto/ and etc/env/ # - policies/ -> etc/policy/ # - etc/rootpack/ -> etc/crypto/profiles/ # # Usage: # ./devops/scripts/migrate-config.sh [--dry-run] # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)" DRY_RUN=false [[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log_info() { echo -e "${BLUE}[INFO]${NC} $*"; } log_ok() { echo -e "${GREEN}[OK]${NC} $*"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } log_error() { echo -e "${RED}[ERROR]${NC} $*"; } log_dry() { echo -e "${YELLOW}[DRY-RUN]${NC} $*"; } # Execute or log command run_cmd() { if [[ "${DRY_RUN}" == true ]]; then log_dry "$*" else "$@" fi } # Create backup create_backup() { local backup_file="${ROOT_DIR}/config-backup-$(date +%Y%m%d-%H%M%S).tar.gz" log_info "Creating backup: ${backup_file}" if [[ "${DRY_RUN}" == true ]]; then log_dry "Would create backup of: certificates/ config/ policies/ etc/" return fi local dirs_to_backup=() [[ -d "${ROOT_DIR}/certificates" ]] && dirs_to_backup+=("certificates") [[ -d "${ROOT_DIR}/config" ]] && dirs_to_backup+=("config") [[ -d "${ROOT_DIR}/policies" ]] && dirs_to_backup+=("policies") [[ -d "${ROOT_DIR}/etc" ]] && dirs_to_backup+=("etc") if [[ ${#dirs_to_backup[@]} -gt 0 ]]; then cd "${ROOT_DIR}" tar -czvf "${backup_file}" "${dirs_to_backup[@]}" log_ok "Backup created: ${backup_file}" else log_warn "No directories to backup" fi } # Create new directory structure create_directories() { log_info "Creating new directory structure..." local dirs=( "etc/certificates/trust-roots" "etc/certificates/signing" "etc/crypto/profiles/cn" "etc/crypto/profiles/eu" "etc/crypto/profiles/kr" "etc/crypto/profiles/ru" "etc/crypto/profiles/us-fips" "etc/env" "etc/policy/packs" "etc/policy/schemas" ) for dir in "${dirs[@]}"; do run_cmd mkdir -p "${ROOT_DIR}/${dir}" done log_ok "Directory structure created" } # Migrate certificates/ migrate_certificates() { local src_dir="${ROOT_DIR}/certificates" if [[ ! -d "${src_dir}" ]]; then log_info "No certificates/ directory found, skipping" return fi log_info "Migrating certificates/..." # Trust roots (CA bundles) for f in "${src_dir}"/*-bundle*.pem "${src_dir}"/*-root*.pem "${src_dir}"/*_bundle*.pem "${src_dir}"/*_root*.pem 2>/dev/null; do [[ -f "$f" ]] || continue run_cmd mv "$f" "${ROOT_DIR}/etc/certificates/trust-roots/" log_ok "Moved: $(basename "$f") -> etc/certificates/trust-roots/" done # Signing keys for f in "${src_dir}"/*-signing-*.pem "${src_dir}"/*_signing_*.pem 2>/dev/null; do [[ -f "$f" ]] || continue run_cmd mv "$f" "${ROOT_DIR}/etc/certificates/signing/" log_ok "Moved: $(basename "$f") -> etc/certificates/signing/" done # Move remaining .pem and .cer files to trust-roots for f in "${src_dir}"/*.pem "${src_dir}"/*.cer 2>/dev/null; do [[ -f "$f" ]] || continue run_cmd mv "$f" "${ROOT_DIR}/etc/certificates/trust-roots/" log_ok "Moved: $(basename "$f") -> etc/certificates/trust-roots/" done # Remove empty directory if [[ -d "${src_dir}" ]] && [[ -z "$(ls -A "${src_dir}")" ]]; then run_cmd rmdir "${src_dir}" log_ok "Removed empty: certificates/" fi } # Migrate config/ migrate_config_dir() { local src_dir="${ROOT_DIR}/config" if [[ ! -d "${src_dir}" ]]; then log_info "No config/ directory found, skipping" return fi log_info "Migrating config/..." # Map env files to crypto profiles declare -A env_mapping=( [".env.fips.example"]="us-fips/env.sample" [".env.eidas.example"]="eu/env.sample" [".env.ru-free.example"]="ru/env.sample" [".env.ru-paid.example"]="ru/env-paid.sample" [".env.sm.example"]="cn/env.sample" [".env.kcmvp.example"]="kr/env.sample" ) for src_name in "${!env_mapping[@]}"; do local src_file="${src_dir}/env/${src_name}" local dst_file="${ROOT_DIR}/etc/crypto/profiles/${env_mapping[$src_name]}" if [[ -f "${src_file}" ]]; then run_cmd mkdir -p "$(dirname "${dst_file}")" run_cmd mv "${src_file}" "${dst_file}" log_ok "Moved: ${src_name} -> etc/crypto/profiles/${env_mapping[$src_name]}" fi done # Remove crypto-profiles.sample.json (superseded) if [[ -f "${src_dir}/crypto-profiles.sample.json" ]]; then run_cmd rm "${src_dir}/crypto-profiles.sample.json" log_ok "Removed: config/crypto-profiles.sample.json (superseded by etc/crypto/)" fi # Remove empty directories [[ -d "${src_dir}/env" ]] && [[ -z "$(ls -A "${src_dir}/env" 2>/dev/null)" ]] && run_cmd rmdir "${src_dir}/env" [[ -d "${src_dir}" ]] && [[ -z "$(ls -A "${src_dir}" 2>/dev/null)" ]] && run_cmd rmdir "${src_dir}" } # Migrate policies/ migrate_policies() { local src_dir="${ROOT_DIR}/policies" if [[ ! -d "${src_dir}" ]]; then log_info "No policies/ directory found, skipping" return fi log_info "Migrating policies/..." # Move policy packs for f in "${src_dir}"/*.yaml 2>/dev/null; do [[ -f "$f" ]] || continue run_cmd mv "$f" "${ROOT_DIR}/etc/policy/packs/" log_ok "Moved: $(basename "$f") -> etc/policy/packs/" done # Move schemas if [[ -d "${src_dir}/schemas" ]]; then for f in "${src_dir}/schemas"/*.json 2>/dev/null; do [[ -f "$f" ]] || continue run_cmd mv "$f" "${ROOT_DIR}/etc/policy/schemas/" log_ok "Moved: schemas/$(basename "$f") -> etc/policy/schemas/" done [[ -z "$(ls -A "${src_dir}/schemas" 2>/dev/null)" ]] && run_cmd rmdir "${src_dir}/schemas" fi # Move AGENTS.md if present [[ -f "${src_dir}/AGENTS.md" ]] && run_cmd mv "${src_dir}/AGENTS.md" "${ROOT_DIR}/etc/policy/" # Remove empty directory [[ -d "${src_dir}" ]] && [[ -z "$(ls -A "${src_dir}" 2>/dev/null)" ]] && run_cmd rmdir "${src_dir}" } # Migrate etc/rootpack/ to etc/crypto/profiles/ migrate_rootpack() { local src_dir="${ROOT_DIR}/etc/rootpack" if [[ ! -d "${src_dir}" ]]; then log_info "No etc/rootpack/ directory found, skipping" return fi log_info "Migrating etc/rootpack/ to etc/crypto/profiles/..." for region_dir in "${src_dir}"/*; do [[ -d "${region_dir}" ]] || continue local region_name=$(basename "${region_dir}") local target_dir="${ROOT_DIR}/etc/crypto/profiles/${region_name}" run_cmd mkdir -p "${target_dir}" for f in "${region_dir}"/*; do [[ -f "$f" ]] || continue run_cmd mv "$f" "${target_dir}/" log_ok "Moved: rootpack/${region_name}/$(basename "$f") -> etc/crypto/profiles/${region_name}/" done [[ -z "$(ls -A "${region_dir}" 2>/dev/null)" ]] && run_cmd rmdir "${region_dir}" done [[ -d "${src_dir}" ]] && [[ -z "$(ls -A "${src_dir}" 2>/dev/null)" ]] && run_cmd rmdir "${src_dir}" } # Validate migration validate_migration() { log_info "Validating migration..." local errors=0 # Check new structure exists local required=( "etc/certificates" "etc/crypto/profiles" "etc/policy" ) for dir in "${required[@]}"; do if [[ ! -d "${ROOT_DIR}/${dir}" ]]; then log_error "Missing: ${dir}" ((errors++)) fi done # Check legacy directories are gone local legacy=( "certificates" "config" "policies" "etc/rootpack" ) for dir in "${legacy[@]}"; do if [[ -d "${ROOT_DIR}/${dir}" ]] && [[ -n "$(ls -A "${ROOT_DIR}/${dir}" 2>/dev/null)" ]]; then log_warn "Legacy directory still has content: ${dir}" fi done if [[ ${errors} -gt 0 ]]; then log_error "Validation failed" return 1 fi log_ok "Migration validated" } # Print summary print_summary() { echo "" echo "========================================" if [[ "${DRY_RUN}" == true ]]; then echo " Migration Dry Run Complete" else echo " Migration Complete" fi echo "========================================" echo "" echo "New structure:" echo " etc/certificates/ - Trust anchors and signing keys" echo " etc/crypto/profiles/ - Regional crypto profiles" echo " etc/policy/ - Policy engine configuration" echo "" if [[ "${DRY_RUN}" == true ]]; then echo "Run without --dry-run to apply changes" else echo "Next steps:" echo " 1. Update Docker Compose volume mounts" echo " 2. Update any hardcoded paths in scripts" echo " 3. Restart services and validate" echo "" echo "Rollback:" echo " tar -xzvf config-backup-*.tar.gz" fi echo "" } # Main main() { if [[ "${DRY_RUN}" == true ]]; then log_info "DRY RUN - no changes will be made" fi create_backup create_directories migrate_certificates migrate_config_dir migrate_policies migrate_rootpack validate_migration print_summary } main "$@"