#!/usr/bin/env bash # ============================================================================= # Neutralisation des comptes de demonstration -- OUTIL DE SECOURS ET D'AUDIT # ============================================================================= # # LE CAS NOMINAL N'A PLUS BESOIN DE CE SCRIPT. # # Le traitement est desormais fait par les migrations, donc automatiquement et # sans risque d'oubli : # # 1730000000007-SeedTestUsers ne s'execute plus en # production (APP_ENV) # 1756000000000-NeutralizeSeedAccountsInProduction filet de securite # 1756000000001-BootstrapAdminFromEnv cree VOTRE administrateur # # Ce script reste utile dans trois situations : # # - une base de production migree AVANT l'ajout de la garde APP_ENV ; # - un deploiement ou APP_ENV n'etait pas correctement positionne (le journal # du Job de migration affiche alors "Seeded test users successfully") ; # - une verification manuelle : il affiche l'etat des comptes sans rien # changer s'il n'y a rien a changer. # # ssh deploy@ # sudo bash /opt/xpeditis/infra-prod/scripts/harden-seed-data.sh # # RAPPEL DU PROBLEME # # La migration 1730000000007-SeedTestUsers cree trois comptes dont le mot de # passe est ecrit en clair dans le depot : # # admin@xpeditis.com role ADMIN Password123! # manager@xpeditis.com role MANAGER Password123! # user@xpeditis.com role USER Password123! # # Sur une base de production, cela donne un acces complet a la plateforme a # quiconque a lu le depot. # # SEULS les comptes dont le hash est encore celui de SeedTestUsers sont traites : # un compte dont le mot de passe a ete change est un vrai compte, et le # neutraliser verrouillerait l'administrateur reellement utilise. (Un compte # remis a Password123! via "mot de passe oublie" aurait un autre sel : le test # de connexion de preflight-check.sh le detecte.) # # Si le compte de demonstration est le SEUL administrateur actif, PostgreSQL # refuse la neutralisation (declencheur trg_users_keep_active_admin, SQLSTATE # XP001) et la transaction est annulee : creez ou promouvez d'abord votre # administrateur, puis relancez ce script. # # Ce script ne SUPPRIME pas les lignes (des cles etrangeres peuvent y pointer, # et une suppression en cascade dans audit_logs serait pire). Il : # 1. renomme les adresses vers un domaine invalide -- ce qui libere au passage # admin@xpeditis.com pour votre vrai compte ; # 2. remplace le mot de passe par une valeur aleatoire inutilisable ; # 3. desactive les comptes (is_active = false). # # Idempotent : peut etre relance sans risque. set -euo pipefail COMPOSE_DIR="${COMPOSE_DIR:-/opt/xpeditis/data-node}" ENV_FILE="${ENV_FILE:-${COMPOSE_DIR}/.env.data}" [[ -f "$ENV_FILE" ]] || { echo "Fichier d'environnement introuvable : $ENV_FILE" >&2; exit 1; } # shellcheck disable=SC1090 set -a; source "$ENV_FILE"; set +a psql_run() { docker compose -f "${COMPOSE_DIR}/docker-compose.data.yml" --env-file "$ENV_FILE" \ exec -T -u postgres postgres psql -v ON_ERROR_STOP=1 -d "$POSTGRES_DB" "$@" } # Hash Argon2id de "Password123!" pose par la migration SeedTestUsers. SEED_HASH='$argon2id$v=19$m=65536,t=3,p=4$Uj+yeQiaqgBFqyTJ5FX3Cw$wpRCYORyFwjQFSuO3gpmzh10gx9wjYFOCvVZ8TVaP8Q' echo ">>> Etat avant intervention" psql_run -v seed_hash="$SEED_HASH" <<'SQL' SELECT email, role, is_active, password_hash = :'seed_hash' AS mot_de_passe_public FROM users WHERE email IN ('admin@xpeditis.com','manager@xpeditis.com','user@xpeditis.com'); SQL echo echo ">>> Neutralisation" psql_run -v seed_hash="$SEED_HASH" <<'SQL' BEGIN; -- Mot de passe remplace par une valeur aleatoire : le format reste un hash -- Argon2 valide en apparence, mais aucun mot de passe ne peut y correspondre. UPDATE users SET email = 'seed-desactive-' || substr(id::text, 1, 8) || '@invalid.local', -- md5(random()) plutot que gen_random_bytes : pas besoin de l'extension -- pgcrypto, qui n'est pas installee sur cette base. password_hash = '$argon2id$v=19$m=65536,t=3,p=4$' || md5(random()::text) || '$' || md5(random()::text) || md5(clock_timestamp()::text), is_active = false, updated_at = NOW() WHERE email IN ('admin@xpeditis.com', 'manager@xpeditis.com', 'user@xpeditis.com') AND password_hash = :'seed_hash'; COMMIT; SQL echo echo ">>> Etat apres intervention" psql_run -c " SELECT email, role, is_active FROM users WHERE email LIKE 'seed-desactive-%@invalid.local'; " echo echo ">>> Controle : aucun compte ne doit garder le mot de passe public" RESTE=$(psql_run -tA -v seed_hash="$SEED_HASH" <<'SQL' SELECT count(*) FROM users WHERE email IN ('admin@xpeditis.com','manager@xpeditis.com','user@xpeditis.com') AND password_hash = :'seed_hash'; SQL ) if [[ "$RESTE" != "0" ]]; then echo "ECHEC : ${RESTE} compte(s) de demonstration acceptent encore Password123!." >&2 exit 1 fi echo "OK : aucun compte de demonstration n'accepte le mot de passe public." echo echo ">>> Organisations de demonstration presentes (a examiner, non modifiees)" # Non supprimees automatiquement : les comptes desactives y sont rattaches, et # une suppression en cascade toucherait aussi audit_logs. psql_run -c " SELECT id, name, created_at FROM organizations WHERE name IN ('Test Freight Forwarder Inc.','Demo Shipping Company','Sample Shipper Ltd.'); " cat <<'NEXT' ----------------------------------------------------------------------------- Etape suivante : disposer d'un administrateur. La migration 1756000000001-BootstrapAdminFromEnv s'en charge normalement, a partir de BOOTSTRAP_ADMIN_EMAIL (ConfigMap). Si elle a ete ignoree parce qu'un ADMIN actif existait alors -- typiquement le compte de demonstration que ce script vient de neutraliser -- relancez simplement le Job de migration : kubectl -n xpeditis-prod delete job -l app.kubernetes.io/name=xpeditis-migrate # puis redeployez, ou rejouez le Job pour le tag courant Elle ne rejouera pas les migrations deja appliquees ; pour forcer uniquement l'amorcage, creez votre compte a la main : 1. Inscrivez-vous sur https://app.xpeditis.com/fr/register 2. Promouvez le compte : UPDATE users SET role = 'ADMIN' WHERE email = ''; Dans les deux cas, verifiez qu'il n'existe qu'un seul ADMIN actif : SELECT email, role, is_active FROM users WHERE role = 'ADMIN'; Puis controlez depuis l'exterieur que l'ancien compte est bien mort : curl -s -o /dev/null -w '%{http_code}\n' -X POST \ https://api.xpeditis.com/api/v1/auth/login \ -H 'Content-Type: application/json' \ -d '{"email":"admin@xpeditis.com","password":"Password123!"}' # Attendu : 401 (et surtout pas 200 ni 201) ----------------------------------------------------------------------------- NEXT