xpeditis2.0/infra/prod/scripts/preflight-check.sh
2026-09-07 21:40:50 +02:00

188 lines
7.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# =============================================================================
# Controle go / no-go avant ouverture au public
# =============================================================================
# export KUBECONFIG=~/.kube/xpeditis-prod.yaml
# bash scripts/preflight-check.sh
#
# Chaque point BLOQUANT en echec doit etre corrige avant d'ouvrir le service.
# Les points d'AVERTISSEMENT peuvent etre acceptes consciemment et notes dans
# le registre des risques.
set -uo pipefail
NAMESPACE=xpeditis-prod
API="${PROD_API_URL:-https://api.xpeditis.com}"
DB_HOST="${DB_PRIVATE_IP:-10.10.1.20}"
DB_PUBLIC_IP="${DB_PUBLIC_IP:-}"
BLOCK=0
WARN=0
ok() { printf ' \033[32m[OK]\033[0m %s\n' "$1"; }
block() { printf ' \033[31m[BLOQUANT]\033[0m %s\n' "$1"; BLOCK=$((BLOCK+1)); }
warn() { printf ' \033[33m[ATTENTION]\033[0m %s\n' "$1"; WARN=$((WARN+1)); }
section() { printf '\n\033[1m%s\033[0m\n' "$1"; }
# ---------------------------------------------------------------------------
section "1. Cluster"
if kubectl get nodes >/dev/null 2>&1; then
ok "Cluster joignable"
if kubectl get nodes --no-headers | grep -qv ' Ready'; then
block "Un noeud n'est pas Ready"
else
ok "Tous les noeuds sont Ready"
fi
else
block "Cluster injoignable : rien d'autre ne peut etre verifie"
fi
if kubectl -n "$NAMESPACE" get pods --no-headers 2>/dev/null | grep -qE 'CrashLoop|ImagePull|Error|Pending'; then
block "Des pods ne sont pas sains dans $NAMESPACE"
else
ok "Tous les pods de $NAMESPACE sont sains"
fi
for d in xpeditis-backend xpeditis-frontend; do
READY=$(kubectl -n "$NAMESPACE" get deploy "$d" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo 0)
[[ "${READY:-0}" -ge 2 ]] && ok "$d : ${READY} replicas prets" \
|| warn "$d : ${READY:-0} replica(s) pret(s), 2 attendus (pas de haute disponibilite)"
done
# ---------------------------------------------------------------------------
section "2. Secrets"
if kubectl -n "$NAMESPACE" get secret xpeditis-backend-secrets >/dev/null 2>&1; then
ok "Secrets applicatifs presents"
JWT=$(kubectl -n "$NAMESPACE" get secret xpeditis-backend-secrets -o jsonpath='{.data.JWT_SECRET}' 2>/dev/null | base64 -d 2>/dev/null || echo "")
[[ ${#JWT} -ge 32 ]] && ok "JWT_SECRET fait ${#JWT} caracteres" || block "JWT_SECRET trop court (${#JWT} caracteres, 32 minimum)"
# Les secrets publies dans infra/preprod/docker-stack.preprod.yml sont dans
# l'historique Git : les retrouver en production serait une compromission.
for compromis in "4C4tQC8qym" "9Lc3M9qoPBeHLKHDXGUf1" "hXiy5GMPswMtxMZujjS2O" "RBJfD0QVXC5JDfAHCwdUW"; do
if kubectl -n "$NAMESPACE" get secret xpeditis-backend-secrets -o json 2>/dev/null \
| grep -q "$(printf '%s' "$compromis" | base64 | cut -c1-12)"; then
block "Un secret de preprod (present dans Git, donc compromis) est reutilise en production"
fi
done
ok "Aucun secret de preprod detecte"
STRIPE=$(kubectl -n "$NAMESPACE" get secret xpeditis-backend-secrets -o jsonpath='{.data.STRIPE_SECRET_KEY}' 2>/dev/null | base64 -d 2>/dev/null || echo "")
case "$STRIPE" in
sk_live_*) ok "Cle Stripe en mode LIVE" ;;
sk_test_*) block "Cle Stripe en mode TEST : aucun paiement reel ne sera encaisse" ;;
*) warn "Cle Stripe absente ou non reconnue" ;;
esac
else
block "Secrets applicatifs absents"
fi
if kubectl -n "$NAMESPACE" get secret regcred >/dev/null 2>&1; then
ok "Acces au registre configure"
else
block "Secret 'regcred' absent : aucune image ne pourra etre telechargee"
fi
if sudo k3s secrets-encrypt status 2>/dev/null | grep -qi 'Encryption Status: Enabled'; then
ok "Chiffrement des Secrets au repos actif"
else
warn "Chiffrement des Secrets au repos non verifiable depuis ce poste (a controler sur app-01)"
fi
# ---------------------------------------------------------------------------
section "3. TLS et exposition"
CERT=$(kubectl -n "$NAMESPACE" get certificate xpeditis-wildcard -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || echo "")
[[ "$CERT" == "True" ]] && ok "Certificat wildcard emis" || block "Certificat wildcard non pret"
ISSUER=$(kubectl -n "$NAMESPACE" get certificate xpeditis-wildcard -o jsonpath='{.spec.issuerRef.name}' 2>/dev/null || echo "")
[[ "$ISSUER" == "letsencrypt-prod" ]] && ok "Emetteur : letsencrypt-prod" \
|| block "Emetteur '$ISSUER' : un certificat de staging n'est pas reconnu par les navigateurs"
if curl -sSI --max-time 15 "${API}/api/v1/health" 2>/dev/null | grep -qi '^strict-transport-security:'; then
ok "HSTS actif"
else
block "En-tete HSTS absent"
fi
if [[ -n "$DB_PUBLIC_IP" ]]; then
if command -v nc >/dev/null; then
if nc -z -w3 "$DB_PUBLIC_IP" 5432 2>/dev/null; then
block "PostgreSQL repond sur l'IP PUBLIQUE de db-01"
else
ok "PostgreSQL injoignable publiquement"
fi
if nc -z -w3 "$DB_PUBLIC_IP" 6379 2>/dev/null; then
block "Redis repond sur l'IP PUBLIQUE de db-01"
else
ok "Redis injoignable publiquement"
fi
fi
else
warn "DB_PUBLIC_IP non fournie : exposition de la base non verifiee"
fi
# ---------------------------------------------------------------------------
section "4. Comptes de demonstration"
# La migration 1730000000007-SeedTestUsers creait admin@xpeditis.com avec le mot
# de passe "Password123!", ecrit en clair dans le depot. Elle ne s'execute plus
# quand NODE_ENV=production, et 1756000000000 neutralise ces comptes s'ils
# existent malgre tout.
#
# Ce controle verifie le RESULTAT en conditions reelles, pas l'intention : c'est
# le seul moyen de detecter un NODE_ENV mal positionne ou une base restauree
# depuis une sauvegarde anterieure. Le controle le plus important de la liste.
for compte in admin manager user; do
CODE=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 15 \
-X POST "${API}/api/v1/auth/login" \
-H 'Content-Type: application/json' \
-d "{\"email\":\"${compte}@xpeditis.com\",\"password\":\"Password123!\"}" 2>/dev/null || echo "000")
case "$CODE" in
200|201) block "Le compte de demonstration ${compte}@xpeditis.com accepte encore Password123! (HTTP ${CODE})" ;;
000) warn "Impossible de tester ${compte}@xpeditis.com (API injoignable)" ;;
*) ok "${compte}@xpeditis.com neutralise (HTTP ${CODE})" ;;
esac
done
echo " Verifier aussi qu'il n'existe qu'un seul ADMIN actif, sur db-01 :"
echo " SELECT email, role, is_active FROM users WHERE role = 'ADMIN';"
# ---------------------------------------------------------------------------
section "5. Sauvegardes"
cat <<'MANUEL'
Ces points se verifient sur db-01, ils ne peuvent pas l'etre d'ici :
ssh deploy@<db-01>
systemctl list-timers 'xpeditis-*' # timers actifs
sudo journalctl -u xpeditis-backup -n 30 # derniere execution
sudo /opt/xpeditis/data-node/backup/pg-restore.sh verify
Une sauvegarde jamais restauree n'est pas une sauvegarde. Le test de
restauration DOIT avoir ete passe au moins une fois avant l'ouverture.
MANUEL
# ---------------------------------------------------------------------------
section "6. Fonctionnement"
if bash "$(dirname "${BASH_SOURCE[0]}")/smoke-test.sh" >/dev/null 2>&1; then
ok "Tests de fumee au vert"
else
block "Tests de fumee en echec (relancer smoke-test.sh pour le detail)"
fi
# ---------------------------------------------------------------------------
printf '\n=========================================\n'
printf ' Bloquants : %d Avertissements : %d\n' "$BLOCK" "$WARN"
printf '=========================================\n'
if [[ "$BLOCK" -gt 0 ]]; then
printf '\n\033[31mNO-GO\033[0m : corrigez les points bloquants avant d ouvrir au public.\n'
exit 1
fi
printf '\n\033[32mGO\033[0m : les controles bloquants sont passes.\n'
[[ "$WARN" -gt 0 ]] && printf 'Consignez les %d avertissement(s) dans le registre des risques.\n' "$WARN"
exit 0