From 11f69a9eda21d7eb4c091859ef11fe5d25849c86 Mon Sep 17 00:00:00 2001 From: Matteo Date: Thu, 19 Mar 2026 09:21:45 +0100 Subject: [PATCH 1/4] fix : t 001 a 020 fait --- BackupVaultWarden/README.md | 2 +- BackupVaultWarden/backup-vaultwarden.sh | 18 ++++++++++++ README.md | 8 ++++++ RebuildBdd/Checkup/check-postgresql.sh | 5 +++- RebuildBdd/Checkup/check-target-readiness.sh | 1 + RebuildBdd/Config/.env.exemple | 26 ++++++++--------- RebuildBdd/Config/Targets/prod.env.exemple | 20 ++++++------- RebuildBdd/Config/Targets/test.env.exemple | 20 ++++++------- RebuildBdd/README.md | 30 +++++++++----------- RebuildBdd/bootstrap-target-host.sh | 15 +++++----- RebuildBdd/create-target-config.sh | 1 + RebuildBdd/rebuild-bdd-core.sh | 1 + RebuildBdd/run-rebuild-bdd.sh | 1 + RecetteScripts/README.md | 3 -- RecetteScripts/backup-bdd-recette.sh | 13 +++++++-- RecetteScripts/check-statut-recette.sh | 26 ++++++++++++++--- RecetteScripts/rebuild-bdd-recette.sh | 10 +++++-- global.env.exemple | 12 ++++---- 18 files changed, 137 insertions(+), 75 deletions(-) diff --git a/BackupVaultWarden/README.md b/BackupVaultWarden/README.md index 80a01ae..c90241c 100644 --- a/BackupVaultWarden/README.md +++ b/BackupVaultWarden/README.md @@ -291,7 +291,7 @@ crontab -e Ajouter : ```bash -0 19 * * * /home//Malio-ops/BackupVaultWarden/backup-vaultwarden.sh >> /var/log/vaultwarden_backup.log 2>&1 +0 19 * * * /home//Malio-ops/BackupVaultWarden/backup-vaultwarden.sh 2>&1 ``` Signification : diff --git a/BackupVaultWarden/backup-vaultwarden.sh b/BackupVaultWarden/backup-vaultwarden.sh index 9a362bb..2f39468 100755 --- a/BackupVaultWarden/backup-vaultwarden.sh +++ b/BackupVaultWarden/backup-vaultwarden.sh @@ -41,6 +41,10 @@ set +a : "${REMOTE_USER:?Variable REMOTE_USER manquante dans .env}" : "${REMOTE_HOST:?Variable REMOTE_HOST manquante dans .env}" : "${REMOTE_DIR:?Variable REMOTE_DIR manquante dans .env}" +[[ "$REMOTE_DIR" =~ ^[a-zA-Z0-9/_.-]+$ ]] || { + echo "ERROR: Variable REMOTE_DIR invalide dans .env" >&2 + exit 1 +} : "${SSH_KEY:?Variable SSH_KEY manquante dans .env}" : "${BACKUP_REMOTE_SSH_PORT:=22}" : "${SSH_CONNECT_TIMEOUT:=10}" @@ -151,6 +155,20 @@ require_cmd() { command -v "$1" >/dev/null 2>&1 || fail "commande requise absente : $1" } +####################################### +# Verrou d'execution +####################################### +LOCK_DIR="/tmp/vaultwarden_backup.lock.d" + +if ! mkdir "$LOCK_DIR" 2>/dev/null; then + fail "Backup deja en cours" +fi + +cleanup() { + rm -rf -- "$LOCK_DIR" +} +trap cleanup EXIT + ####################################### # Vérifications préalables ####################################### diff --git a/README.md b/README.md index 977fb67..34712b7 100644 --- a/README.md +++ b/README.md @@ -120,12 +120,20 @@ Un modèle est fourni : global.env.exemple ``` +Ce fichier concerne la configuration legacy de `RecetteScripts`. + Utilisation : ```bash cp global.env.exemple global.env ``` +Pour la configuration de `RebuildBdd`, voir la documentation dédiée : + +```bash +RebuildBdd/README.md +``` + ## Configuration locale * Chaque module peut contenir son propre `.env` diff --git a/RebuildBdd/Checkup/check-postgresql.sh b/RebuildBdd/Checkup/check-postgresql.sh index e28de4b..6bd9c31 100755 --- a/RebuildBdd/Checkup/check-postgresql.sh +++ b/RebuildBdd/Checkup/check-postgresql.sh @@ -137,6 +137,9 @@ PGUSER_SUPERUSER="${PGUSER_SUPERUSER:-no}" POSTGRES_PACKAGE_LIST="${POSTGRES_PACKAGE_LIST:-postgresql postgresql-client postgresql-contrib}" POSTGRES_SERVICE_NAME="${POSTGRES_SERVICE_NAME:-postgresql}" SUDO_BIN="${SUDO_BIN:-sudo}" +read -r -a POSTGRES_PACKAGES <<< "$POSTGRES_PACKAGE_LIST" + +[[ "${#POSTGRES_PACKAGES[@]}" -gt 0 ]] || fail "POSTGRES_PACKAGE_LIST vide" export PGPASSWORD @@ -159,7 +162,7 @@ if ! require_cmd psql || ! require_cmd pg_restore || ! require_cmd createdb || ! log "PostgreSQL absent : installation en cours..." "$SUDO_BIN" apt update >/dev/null 2>&1 || fail "échec de apt update" - "$SUDO_BIN" apt install -y $POSTGRES_PACKAGE_LIST >/dev/null 2>&1 || fail "échec de l'installation PostgreSQL" + "$SUDO_BIN" apt install -y "${POSTGRES_PACKAGES[@]}" >/dev/null 2>&1 || fail "échec de l'installation PostgreSQL" POSTGRES_INSTALLED="yes" log "Installation PostgreSQL terminée." else diff --git a/RebuildBdd/Checkup/check-target-readiness.sh b/RebuildBdd/Checkup/check-target-readiness.sh index 109fdb2..cd6f618 100755 --- a/RebuildBdd/Checkup/check-target-readiness.sh +++ b/RebuildBdd/Checkup/check-target-readiness.sh @@ -92,6 +92,7 @@ to_bool_yes_no() { v="${v,,}" case "$v" in yes|y|oui|o|true|1) echo "yes" ;; + # Valeur vide traitée comme "no" pour conserver le comportement historique. no|n|non|false|0|"") echo "no" ;; *) return 1 ;; esac diff --git a/RebuildBdd/Config/.env.exemple b/RebuildBdd/Config/.env.exemple index ff66962..344a48b 100644 --- a/RebuildBdd/Config/.env.exemple +++ b/RebuildBdd/Config/.env.exemple @@ -8,18 +8,18 @@ RESTORE_ROLES=yes # Dépôt scripts GLOBAL_REPO_URL=git@gitea.example.tld:team/RebuildBdd.git -GLOBAL_REPO_BRANCH=main - -# Backup central -GLOBAL_BACKUP_REMOTE_USER=backup -GLOBAL_BACKUP_REMOTE_HOST=192.168.1.60 -GLOBAL_BACKUP_REMOTE_PORT=22 -GLOBAL_BACKUP_REMOTE_BASE_DIR=/home/backup/backups - -# Clé SSH de lecture backup copiée sur les cibles -GLOBAL_BACKUP_SSH_PRIVATE_KEY=/home/matteo/.ssh/id_ed25519_backup_readonly -GLOBAL_BACKUP_SSH_PUBLIC_KEY=/home/matteo/.ssh/id_ed25519_backup_readonly.pub -GLOBAL_BACKUP_KNOWN_HOSTS_STRICT=yes +GLOBAL_REPO_BRANCH=main + +# Backup central +GLOBAL_BACKUP_REMOTE_USER=backup +GLOBAL_BACKUP_REMOTE_HOST= +GLOBAL_BACKUP_REMOTE_PORT=22 +GLOBAL_BACKUP_REMOTE_BASE_DIR=/home/backup/backups + +# Clé SSH de lecture backup copiée sur les cibles +GLOBAL_BACKUP_SSH_PRIVATE_KEY=/home//.ssh/id_ed25519_backup_readonly +GLOBAL_BACKUP_SSH_PUBLIC_KEY=/home//.ssh/id_ed25519_backup_readonly.pub +GLOBAL_BACKUP_KNOWN_HOSTS_STRICT=yes # Defaults PostgreSQL GLOBAL_PGHOST=127.0.0.1 @@ -35,4 +35,4 @@ GLOBAL_BOOTSTRAP_ALLOW_PASSWORDLESS_SUDO=yes GLOBAL_AUTO_INSTALL_POSTGRES=yes GLOBAL_AUTO_CREATE_PGUSER=yes GLOBAL_PGUSER_SUPERUSER=no -GLOBAL_AUTO_CONFIGURE_SUDOERS=no \ No newline at end of file +GLOBAL_AUTO_CONFIGURE_SUDOERS=no diff --git a/RebuildBdd/Config/Targets/prod.env.exemple b/RebuildBdd/Config/Targets/prod.env.exemple index a7d6263..cf29bb5 100644 --- a/RebuildBdd/Config/Targets/prod.env.exemple +++ b/RebuildBdd/Config/Targets/prod.env.exemple @@ -3,25 +3,25 @@ ############################################################################### # SSH bootstrap cible -TARGET_HOST=192.168.1.60 +TARGET_HOST= TARGET_PORT=22 -TARGET_BOOTSTRAP_USER=backup_liot -TARGET_BOOTSTRAP_SSH_KEY=/home/matteo/.ssh/id_ed25519_target_prod -TARGET_RUNTIME_USER=backup_liot +TARGET_BOOTSTRAP_USER= +TARGET_BOOTSTRAP_SSH_KEY=/home//.ssh/id_ed25519_target_prod +TARGET_RUNTIME_USER= # Bootstrap TARGET_ENABLE_BOOTSTRAP=yes TARGET_BOOTSTRAP_ALLOW_PASSWORDLESS_SUDO=yes # Repo local cible -TARGET_REPO_DIR=/home/backup_liot/RebuildBdd -TARGET_ENV_FILE=/home/backup_liot/RebuildBdd/.env +TARGET_REPO_DIR=/home//RebuildBdd +TARGET_ENV_FILE=/home//RebuildBdd/.env # PostgreSQL cible TARGET_ENV_NAME=PROD TARGET_PGHOST=127.0.0.1 TARGET_PGPORT=5432 -TARGET_PGUSER=backup_liot +TARGET_PGUSER= TARGET_PGPASSWORD=change_me_pg_password TARGET_DBS="sirh inventory ferme" @@ -29,9 +29,9 @@ TARGET_DBS="sirh inventory ferme" TARGET_BACKUP_SUBDIR=bdd-prod # Logs / tmp / ssh cible -TARGET_BACKUP_LOG_DIR=/home/backup_liot/logs/rebuild_bdd -TARGET_LOCAL_RESTORE_BASE_DIR=/home/backup_liot/RebuildBdd/restore_tmp -TARGET_SSH_KEY=/home/backup_liot/.ssh/id_ed25519_backup_readonly +TARGET_BACKUP_LOG_DIR=/home//logs/rebuild_bdd +TARGET_LOCAL_RESTORE_BASE_DIR=/home//RebuildBdd/restore_tmp +TARGET_SSH_KEY=/home//.ssh/id_ed25519_backup_readonly # Options cible TARGET_REMOTE_ROLES_DIR_NAME=user diff --git a/RebuildBdd/Config/Targets/test.env.exemple b/RebuildBdd/Config/Targets/test.env.exemple index 3ca9e61..cb0be0e 100644 --- a/RebuildBdd/Config/Targets/test.env.exemple +++ b/RebuildBdd/Config/Targets/test.env.exemple @@ -3,25 +3,25 @@ ############################################################################### # SSH bootstrap cible -TARGET_HOST=192.168.1.50 +TARGET_HOST= TARGET_PORT=22 -TARGET_BOOTSTRAP_USER=backup_liot -TARGET_BOOTSTRAP_SSH_KEY=/home/matteo/.ssh/id_ed25519_target_test -TARGET_RUNTIME_USER=backup_liot +TARGET_BOOTSTRAP_USER= +TARGET_BOOTSTRAP_SSH_KEY=/home//.ssh/id_ed25519_target_test +TARGET_RUNTIME_USER= # Bootstrap TARGET_ENABLE_BOOTSTRAP=yes TARGET_BOOTSTRAP_ALLOW_PASSWORDLESS_SUDO=yes # Repo local cible -TARGET_REPO_DIR=/home/backup_liot/RebuildBdd -TARGET_ENV_FILE=/home/backup_liot/RebuildBdd/.env +TARGET_REPO_DIR=/home//RebuildBdd +TARGET_ENV_FILE=/home//RebuildBdd/.env # PostgreSQL cible TARGET_ENV_NAME=RECETTE TARGET_PGHOST=127.0.0.1 TARGET_PGPORT=5432 -TARGET_PGUSER=backup_liot +TARGET_PGUSER= TARGET_PGPASSWORD=change_me_pg_password TARGET_DBS="sirh inventory ferme" @@ -29,9 +29,9 @@ TARGET_DBS="sirh inventory ferme" TARGET_BACKUP_SUBDIR=bdd-recette # Logs / tmp / ssh cible -TARGET_BACKUP_LOG_DIR=/home/backup_liot/logs/rebuild_bdd -TARGET_LOCAL_RESTORE_BASE_DIR=/home/backup_liot/RebuildBdd/restore_tmp -TARGET_SSH_KEY=/home/backup_liot/.ssh/id_ed25519_backup_readonly +TARGET_BACKUP_LOG_DIR=/home//logs/rebuild_bdd +TARGET_LOCAL_RESTORE_BASE_DIR=/home//RebuildBdd/restore_tmp +TARGET_SSH_KEY=/home//.ssh/id_ed25519_backup_readonly # Options cible TARGET_REMOTE_ROLES_DIR_NAME=user diff --git a/RebuildBdd/README.md b/RebuildBdd/README.md index 4013efb..5d0169a 100644 --- a/RebuildBdd/README.md +++ b/RebuildBdd/README.md @@ -110,14 +110,14 @@ Usage : ```bash ./create-target-config.sh \ --target test \ - --host 192.168.1.50 \ + --host \ --port 22 \ - --bootstrap-user backup_liot \ + --bootstrap-user \ --bootstrap-key /home/user/.ssh/id_ed25519_target_test \ - --runtime-user backup_liot \ - --repo-dir /home/backup_liot/RebuildBdd \ + --runtime-user \ + --repo-dir /home//RebuildBdd \ --env-name RECETTE \ - --pguser backup_liot \ + --pguser \ --pgpassword secret \ --dbs "sirh inventory ferme" \ --backup-subdir bdd-recette @@ -443,7 +443,7 @@ Exemple : "environment": "RECETTE", "database": "sirh", "dump_file": "/home/backup/backups/bdd-recette/sirh/sirh_2026-03-16_19-00-01.dump", - "log_file": "/home/backup_liot/logs/rebuild_bdd/restore_recette_web_001_2026-03-17_09-10-00.log" + "log_file": "/home//logs/rebuild_bdd/restore_recette_web_001_2026-03-17_09-10-00.log" } ``` @@ -459,7 +459,7 @@ Exemple : "environment": "RECETTE", "database": "sirh", "dump_file": "/home/backup/backups/bdd-recette/sirh/sirh_2026-03-16_19-00-01.dump", - "log_file": "/home/backup_liot/logs/rebuild_bdd/restore_recette_web_001_2026-03-17_09-10-00.log" + "log_file": "/home//logs/rebuild_bdd/restore_recette_web_001_2026-03-17_09-10-00.log" } ``` @@ -500,7 +500,7 @@ TARGET_BACKUP_LOG_DIR Exemple : ```bash -/home/backup_liot/logs/rebuild_bdd/ +/home//logs/rebuild_bdd/ ``` Le chemin du log est renvoyé dans le JSON final. @@ -537,14 +537,14 @@ Avant mise en production, tester au minimum : ```bash ./create-target-config.sh \ --target test \ - --host 192.168.1.50 \ + --host \ --port 22 \ - --bootstrap-user backup_liot \ - --bootstrap-key /home/matteo/.ssh/id_ed25519_target_test \ - --runtime-user backup_liot \ - --repo-dir /home/backup_liot/RebuildBdd \ + --bootstrap-user \ + --bootstrap-key /home//.ssh/id_ed25519_target_test \ + --runtime-user \ + --repo-dir /home//RebuildBdd \ --env-name RECETTE \ - --pguser backup_liot \ + --pguser \ --pgpassword secret \ --dbs "sirh inventory ferme" \ --backup-subdir bdd-recette @@ -578,5 +578,3 @@ Le projet permet désormais une utilisation : * intégrée au web ; avec préparation des cibles, exécution non interactive et retour JSON. - -``` diff --git a/RebuildBdd/bootstrap-target-host.sh b/RebuildBdd/bootstrap-target-host.sh index e367c43..2ff9e9c 100755 --- a/RebuildBdd/bootstrap-target-host.sh +++ b/RebuildBdd/bootstrap-target-host.sh @@ -94,6 +94,7 @@ to_bool_yes_no() { v="${v,,}" case "$v" in yes|y|oui|o|true|1) echo "yes" ;; + # Valeur vide traitée comme "no" pour conserver le comportement historique. no|n|non|false|0|"") echo "no" ;; *) return 1 ;; esac @@ -385,6 +386,13 @@ log "Correction des permissions SSH côté cible" ssh "${SSH_OPTS[@]}" "$REMOTE" "$REMOTE_SSH_PERMS_CMD" \ || fail "échec de correction des permissions SSH sur la cible" +STRICT_OPTION="yes" +case "${TARGET_BACKUP_KNOWN_HOSTS_STRICT_VALUE,,}" in + yes|y|oui|o|true|1) STRICT_OPTION="yes" ;; + no|n|non|false|0) STRICT_OPTION="no" ;; + *) fail "TARGET_BACKUP_KNOWN_HOSTS_STRICT invalide" ;; +esac + REMOTE_KNOWN_HOSTS_CMD=" set -euo pipefail @@ -406,13 +414,6 @@ log "Ajout du serveur de backup dans known_hosts côté cible" ssh "${SSH_OPTS[@]}" "$REMOTE" "$REMOTE_KNOWN_HOSTS_CMD" \ || fail "échec de préparation known_hosts sur la cible" -STRICT_OPTION="yes" -case "${TARGET_BACKUP_KNOWN_HOSTS_STRICT_VALUE,,}" in - yes|y|oui|o|true|1) STRICT_OPTION="yes" ;; - no|n|non|false|0) STRICT_OPTION="no" ;; - *) fail "TARGET_BACKUP_KNOWN_HOSTS_STRICT invalide" ;; -esac - REMOTE_BACKUP_TEST_CMD=" set -euo pipefail diff --git a/RebuildBdd/create-target-config.sh b/RebuildBdd/create-target-config.sh index fe60867..63ad499 100644 --- a/RebuildBdd/create-target-config.sh +++ b/RebuildBdd/create-target-config.sh @@ -79,6 +79,7 @@ to_bool_yes_no() { v="${v,,}" case "$v" in yes|y|oui|o|true|1) echo "yes" ;; + # Valeur vide traitée comme "no" pour conserver le comportement historique. no|n|non|false|0|"") echo "no" ;; *) return 1 ;; esac diff --git a/RebuildBdd/rebuild-bdd-core.sh b/RebuildBdd/rebuild-bdd-core.sh index 0293402..41ed462 100755 --- a/RebuildBdd/rebuild-bdd-core.sh +++ b/RebuildBdd/rebuild-bdd-core.sh @@ -121,6 +121,7 @@ to_bool_yes_no() { v="${v,,}" case "$v" in yes|y|oui|o|true|1) echo "yes" ;; + # Valeur vide traitée comme "no" pour conserver le comportement historique. no|n|non|false|0|"") echo "no" ;; *) return 1 ;; esac diff --git a/RebuildBdd/run-rebuild-bdd.sh b/RebuildBdd/run-rebuild-bdd.sh index cb8b91f..b95e350 100755 --- a/RebuildBdd/run-rebuild-bdd.sh +++ b/RebuildBdd/run-rebuild-bdd.sh @@ -88,6 +88,7 @@ to_bool_yes_no() { v="${v,,}" case "$v" in yes|y|oui|o|true|1) echo "yes" ;; + # Valeur vide traitée comme "no" pour conserver le comportement historique. no|n|non|false|0|"") echo "no" ;; *) return 1 ;; esac diff --git a/RecetteScripts/README.md b/RecetteScripts/README.md index ca78ea9..95626fc 100644 --- a/RecetteScripts/README.md +++ b/RecetteScripts/README.md @@ -292,9 +292,6 @@ CHECK APP RECETTE 🟢 # 7. Script : rebuild-bdd-recette.sh -Script : - - ## Objectif Restaurer une base PostgreSQL à partir d’un dump distant. diff --git a/RecetteScripts/backup-bdd-recette.sh b/RecetteScripts/backup-bdd-recette.sh index 94cfc98..e06487b 100755 --- a/RecetteScripts/backup-bdd-recette.sh +++ b/RecetteScripts/backup-bdd-recette.sh @@ -18,7 +18,8 @@ umask 077 # 6. exporte les rôles PostgreSQL ; # 7. dump chaque base au format personnalisé PostgreSQL ; # 8. transfère chaque fichier vers le serveur distant ; -# 9. applique une rotation distante sur 10 jours ; +# 9. applique une rotation distante selon BACKUP_RETENTION_DAYS +# (10 jours par défaut) ; # 10. envoie un bilan sur Discord : # - 1 message global si tout est OK ; # - en cas d’erreur partielle : @@ -50,6 +51,10 @@ set +a ####################################### : "${ENV_NAME:?Variable ENV_NAME manquante}" +[[ "$ENV_NAME" =~ ^[a-zA-Z0-9_-]+$ ]] || { + echo "Variable ENV_NAME invalide : $ENV_NAME" >&2 + exit 1 +} : "${PGHOST:?Variable PGHOST manquante}" : "${PGPORT:?Variable PGPORT manquante}" : "${PGUSER:?Variable PGUSER manquante}" @@ -58,6 +63,10 @@ set +a : "${BACKUP_REMOTE_USER:?Variable BACKUP_REMOTE_USER manquante}" : "${BACKUP_REMOTE_HOST:?Variable BACKUP_REMOTE_HOST manquante}" : "${BACKUP_REMOTE_DIR:?Variable BACKUP_REMOTE_DIR manquante}" +[[ "$BACKUP_REMOTE_DIR" =~ ^[a-zA-Z0-9/_.-]+$ ]] || { + echo "Variable BACKUP_REMOTE_DIR invalide : $BACKUP_REMOTE_DIR" >&2 + exit 1 +} : "${SSH_KEY:?Variable SSH_KEY manquante}" : "${SSH_TIMEOUT:?Variable SSH_TIMEOUT manquante}" : "${BACKUP_LOG_DIR:?Variable BACKUP_LOG_DIR manquante}" @@ -87,7 +96,7 @@ for DB in "${DBS_ARRAY[@]}"; do done IA_SSH="${BACKUP_REMOTE_USER}@${BACKUP_REMOTE_HOST}" -RETENTION_DAYS=10 +RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-10}" BACKUP_REMOTE_SSH_PORT="${BACKUP_REMOTE_SSH_PORT:-22}" BACKUP_KNOWN_HOSTS_STRICT="${BACKUP_KNOWN_HOSTS_STRICT:-yes}" BACKUP_KNOWN_HOSTS_FILE="${BACKUP_KNOWN_HOSTS_FILE:-${HOME}/.ssh/known_hosts}" diff --git a/RecetteScripts/check-statut-recette.sh b/RecetteScripts/check-statut-recette.sh index 6791028..84cfc59 100755 --- a/RecetteScripts/check-statut-recette.sh +++ b/RecetteScripts/check-statut-recette.sh @@ -44,13 +44,23 @@ set +a : "${CHECK_MAX_TIME:?Variable CHECK_MAX_TIME manquante}" : "${APP_URLS:?Variable APP_URLS manquante}" +[[ "$CHECK_CONNECT_TIMEOUT" =~ ^[0-9]+$ ]] || { + echo "ERROR: Variable CHECK_CONNECT_TIMEOUT invalide" >&2 + exit 1 +} + +[[ "$CHECK_MAX_TIME" =~ ^[0-9]+$ ]] || { + echo "ERROR: Variable CHECK_MAX_TIME invalide" >&2 + exit 1 +} + ####################################### # Sites à vérifier ####################################### read -r -a SITES <<< "$APP_URLS" -SCHEME="http" +SCHEME="${APP_SCHEME:-http}" CONNECT_TIMEOUT="${CHECK_CONNECT_TIMEOUT}" MAX_TIME="${CHECK_MAX_TIME}" @@ -75,6 +85,16 @@ DISCORD_PING="${DISCORD_PING:-@here}" SUMMARY_LINES=() FAILURES=0 +TMPFILES=() + +cleanup() { + local tmpfile + for tmpfile in "${TMPFILES[@]}"; do + [[ -n "$tmpfile" ]] || continue + rm -f -- "$tmpfile" + done +} +trap cleanup EXIT ####################################### # Logging @@ -158,6 +178,7 @@ check_site() { local http_code curl_exit err local stderr stderr="$(mktemp)" + TMPFILES+=("$stderr") http_code="$( curl -sS -o /dev/null \ @@ -170,15 +191,12 @@ check_site() { if [[ "$curl_exit" -ne 0 ]]; then err="$(head -n 1 "$stderr" | tr -d '\r')" - rm -f "$stderr" log_line "DOWN" "$host" "curl exit=$curl_exit : ${err:-"(aucun)"}" add_summary_line "$host" "DOWN" "DOWN - curl" return 1 fi - rm -f "$stderr" - if [[ "$http_code" =~ ^[0-9]{3}$ ]]; then if [[ "$http_code" -ge 200 && "$http_code" -le 399 ]]; then log_line "OK" "$host" "HTTP $http_code" diff --git a/RecetteScripts/rebuild-bdd-recette.sh b/RecetteScripts/rebuild-bdd-recette.sh index 03af19c..6d6d157 100644 --- a/RecetteScripts/rebuild-bdd-recette.sh +++ b/RecetteScripts/rebuild-bdd-recette.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash set -euo pipefail +umask 077 ############################################################################### # rebuild-bdd-recette.sh @@ -49,6 +50,10 @@ set +a # Variables obligatoires ############################################################################### : "${ENV_NAME:?Variable ENV_NAME manquante}" +[[ "$ENV_NAME" =~ ^[a-zA-Z0-9_-]+$ ]] || { + echo "Variable ENV_NAME invalide : $ENV_NAME" >&2 + exit 1 +} : "${PGHOST:?Variable PGHOST manquante}" : "${PGPORT:?Variable PGPORT manquante}" : "${PGUSER:?Variable PGUSER manquante}" @@ -110,6 +115,7 @@ cleanup() { "${FILTERED_ROLES_FILE:-}" \ "${ROLES_CREATE_LIST:-}" \ "${ROLES_APPLY_FILE:-}" + rm -rf "${LOCAL_RESTORE_DIR:-}" 2>/dev/null || true } trap cleanup EXIT @@ -150,14 +156,14 @@ build_excluded_roles_regex() { # Envoi Discord # # Envoi simple d'un message texte via webhook Discord. -# Si WEBHOOK_URL n'est pas défini, on ignore silencieusement l'envoi. +# Si DISCORD_WEBHOOK_URL n'est pas défini, on ignore silencieusement l'envoi. ############################################################################### send_discord_message() { local message="$1" local payload="" [[ -n "$DISCORD_WEBHOOK_URL" ]] || { - log "WEBHOOK_URL non défini : notification Discord ignorée." + log "DISCORD_WEBHOOK_URL non défini : notification Discord ignorée." return 0 } diff --git a/global.env.exemple b/global.env.exemple index 1088e64..f531fe6 100644 --- a/global.env.exemple +++ b/global.env.exemple @@ -89,6 +89,7 @@ SSH_TIMEOUT=10 ############################################# # Nombre de jours de conservation des sauvegardes +# Utilisé par backup-bdd-recette.sh et backup-vaultwarden.sh BACKUP_RETENTION_DAYS=10 @@ -96,12 +97,11 @@ BACKUP_RETENTION_DAYS=10 # APPLICATIONS À SURVEILLER ############################################# -# Liste des applications à vérifier -APPS=" -ferme.malio-dev.fr -inventory.malio-dev.fr -sirh.malio-dev.fr -" +# Liste des applications à vérifier (séparées par espace) +APP_URLS="ferme.malio-dev.fr inventory.malio-dev.fr sirh.malio-dev.fr" + +# Schéma utilisé pour les applications surveillées +APP_SCHEME="http" ############################################# From 9af65f7739075a65c3b7577217227e4709ed5e89 Mon Sep 17 00:00:00 2001 From: Matteo Date: Thu, 19 Mar 2026 09:57:12 +0100 Subject: [PATCH 2/4] fix : t 021 a 033 fait --- BackupVaultWarden/backup-vaultwarden.sh | 11 +- CHANGELOG.md | 14 +++ CheckStorage/check-storage.sh | 24 ++-- RebuildBdd/Checkup/check-postgresql.sh | 30 ++--- RebuildBdd/Checkup/check-target-readiness.sh | 1 + RebuildBdd/rebuild-bdd-core.sh | 48 +++++--- RebuildBdd/run-rebuild-bdd.sh | 4 + RecetteScripts/backup-bdd-recette.sh | 120 ++++++++++++------- RecetteScripts/check-statut-recette.sh | 6 +- RecetteScripts/rebuild-bdd-recette.sh | 47 ++++---- 10 files changed, 190 insertions(+), 115 deletions(-) diff --git a/BackupVaultWarden/backup-vaultwarden.sh b/BackupVaultWarden/backup-vaultwarden.sh index 2f39468..fe31f5e 100755 --- a/BackupVaultWarden/backup-vaultwarden.sh +++ b/BackupVaultWarden/backup-vaultwarden.sh @@ -113,11 +113,14 @@ mkdir -p "$LOCAL_BACKUP" ####################################### # Notification Discord ####################################### -discord_ping() { +send_discord() { local success="$1" local details="${2:-}" + local payload="" [[ -z "$DISCORD_WEBHOOK_URL" ]] && return 0 + require_cmd jq || return 0 + require_cmd curl || return 0 local icon status_line if [[ "$success" == "true" ]]; then @@ -136,7 +139,6 @@ discord_ping() { msg+="Data transfer: ${status_line}\n" [[ -n "$details" ]] && msg+="Détails: ${details}" - local payload payload="$(jq -n --arg content "$msg" '{content: $content}')" curl -fsS -H "Content-Type: application/json" -d "$payload" "$DISCORD_WEBHOOK_URL" >/dev/null || true } @@ -147,7 +149,7 @@ discord_ping() { fail() { local detail="$1" log "ERROR: $detail" - discord_ping "false" "$detail" + send_discord "false" "$detail" exit 1 } @@ -165,6 +167,7 @@ if ! mkdir "$LOCK_DIR" 2>/dev/null; then fi cleanup() { + rm -f "${LOCAL_BACKUP_FILE:-}" rm -rf -- "$LOCK_DIR" } trap cleanup EXIT @@ -227,5 +230,5 @@ rm -f "$LOCAL_BACKUP_FILE" || fail "Impossible de supprimer le backup local $LOC # Fin ####################################### log "Backup $BACKUP_NAME terminé et envoyé sur $REMOTE_HOST:$REMOTE_DIR" -discord_ping "true" "Backup envoyé avec succès vers $REMOTE_HOST" +send_discord "true" "Backup envoyé avec succès vers $REMOTE_HOST" echo "Backup $BACKUP_NAME terminé et envoyé sur $REMOTE_HOST:$REMOTE_DIR" diff --git a/CHANGELOG.md b/CHANGELOG.md index ca87d16..f0ee94e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ et applique le versionnement semantique. ## [Unreleased] +### Changed +- Harmonisation des fonctions d'envoi Discord dans les scripts de sauvegarde, de supervision et de reconstruction. +- Ajout d'un fichier de log dedie a l'orchestrateur `RebuildBdd/run-rebuild-bdd.sh`. +- Renforcement de la journalisation et de la gestion des erreurs dans `RecetteScripts/backup-bdd-recette.sh`. + +### Fixed +- Nettoyage du backup local temporaire dans `BackupVaultWarden/backup-vaultwarden.sh` en sortie de script. +- Gestion plus robuste des dependances shell et des verifications de disponibilite PostgreSQL dans les scripts `RebuildBdd`. +- Acceptation explicite du flag `--non-interactive` dans `RebuildBdd/Checkup/check-target-readiness.sh` pour compatibilite de workflow. +- Validation des noms de base et refus des cles SSH symboliques dans les scripts de reconstruction. +- Suppression des notifications Discord fragiles quand `jq` ou `curl` sont absents, avec envoi silencieux en secours. +- Detection et nettoyage des verrous perimes dans `RecetteScripts/backup-bdd-recette.sh`. +- Filtrage des privileges `SUPERUSER` lors de la restauration des roles dans `RecetteScripts/rebuild-bdd-recette.sh`. + ## [1.0.0] - 2026-03-18 ### Added diff --git a/CheckStorage/check-storage.sh b/CheckStorage/check-storage.sh index 3084667..2e974bd 100755 --- a/CheckStorage/check-storage.sh +++ b/CheckStorage/check-storage.sh @@ -52,6 +52,20 @@ if [[ -n "${DISCORD_WEBHOOK_URL:-}" ]]; then require_cmd curl fi +send_discord() { + local message="$1" + local payload="" + + [[ -n "${DISCORD_WEBHOOK_URL:-}" ]] || return 0 + + payload="$(jq -n --arg content "$message" '{content: $content}')" || return 0 + + curl -fsS \ + -H "Content-Type: application/json" \ + -d "$payload" \ + "$DISCORD_WEBHOOK_URL" >/dev/null || true +} + ############################################################################### # RÉCUPÉRATION DES INFORMATIONS DISQUE ############################################################################### @@ -77,15 +91,7 @@ if [ "$usage" -ge "$limit" ]; then msgLimit="${DISCORD_PING}\n**CHECK STOCKAGE :red_circle:**\nLimite autorisée : ${limit}%\nUtilisation actuelle : ${usage}%\nEspace restant : ${free}%\nUtilisé / total : ${used_gb} GB / ${total_gb} GB\nDisponible : ${avail_gb} GB\nHeure : $(date)" - if [[ -n "${DISCORD_WEBHOOK_URL:-}" ]]; then - payload="$(jq -n --arg content "$msgLimit" '{content: $content}')" - - curl -fsS \ - -H "Accept: application/json" \ - -H "Content-Type: application/json; charset=utf-8" \ - -d "$payload" \ - "$DISCORD_WEBHOOK_URL" >/dev/null || true - fi + send_discord "$msgLimit" fi diff --git a/RebuildBdd/Checkup/check-postgresql.sh b/RebuildBdd/Checkup/check-postgresql.sh index 6bd9c31..61c27af 100755 --- a/RebuildBdd/Checkup/check-postgresql.sh +++ b/RebuildBdd/Checkup/check-postgresql.sh @@ -41,19 +41,19 @@ fail() { exit 1 } -require_cmd() { +has_cmd() { command -v "$1" >/dev/null 2>&1 } postgres_server_ready() { - require_cmd postgres || return 1 - require_cmd pg_ctlcluster || return 1 - require_cmd pg_lsclusters || return 1 + has_cmd postgres || return 1 + has_cmd pg_ctlcluster || return 1 + has_cmd pg_lsclusters || return 1 return 0 } ensure_postgres_cluster() { - if ! require_cmd pg_lsclusters || ! require_cmd pg_createcluster; then + if ! has_cmd pg_lsclusters || ! has_cmd pg_createcluster; then return 0 fi @@ -66,7 +66,7 @@ ensure_postgres_cluster() { version="$(find /etc/postgresql -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | LC_ALL=C sort -V | tail -n 1)" fi - if [[ -z "$version" ]] && require_cmd psql; then + if [[ -z "$version" ]] && has_cmd psql; then version="$(psql --version 2>/dev/null | awk '{print $3}' | cut -d. -f1)" fi @@ -82,15 +82,15 @@ collect_postgres_diagnostics() { if "$SUDO_BIN" systemctl status "$POSTGRES_SERVICE_NAME" --no-pager >/dev/null 2>&1; then diagnostics+="systemctl status ${POSTGRES_SERVICE_NAME}: OK; " - elif require_cmd systemctl; then + elif has_cmd systemctl; then diagnostics+="systemctl status ${POSTGRES_SERVICE_NAME}: $( "$SUDO_BIN" systemctl status "$POSTGRES_SERVICE_NAME" --no-pager 2>/dev/null | tail -n 5 | tr '\n' ' ' ); " fi - if require_cmd pg_lsclusters; then + if has_cmd pg_lsclusters; then diagnostics+="pg_lsclusters: $(pg_lsclusters --no-header 2>/dev/null | tr '\n' ' '); " fi - if require_cmd journalctl; then + if has_cmd journalctl; then diagnostics+="journalctl: $( "$SUDO_BIN" journalctl -u "$POSTGRES_SERVICE_NAME" -n 10 --no-pager 2>/dev/null | tr '\n' ' ' ); " fi @@ -102,11 +102,11 @@ start_postgres_service() { return 0 fi - if require_cmd service && "$SUDO_BIN" service "$POSTGRES_SERVICE_NAME" start >/dev/null 2>&1; then + if has_cmd service && "$SUDO_BIN" service "$POSTGRES_SERVICE_NAME" start >/dev/null 2>&1; then return 0 fi - if require_cmd pg_lsclusters && require_cmd pg_ctlcluster; then + if has_cmd pg_lsclusters && has_cmd pg_ctlcluster; then local version cluster while read -r version cluster _; do [[ -n "$version" && -n "$cluster" ]] || continue @@ -143,7 +143,7 @@ read -r -a POSTGRES_PACKAGES <<< "$POSTGRES_PACKAGE_LIST" export PGPASSWORD -if ! require_cmd "$SUDO_BIN"; then +if ! has_cmd "$SUDO_BIN"; then fail "sudo absent sur la cible" fi @@ -157,7 +157,7 @@ fi POSTGRES_INSTALLED="no" -if ! require_cmd psql || ! require_cmd pg_restore || ! require_cmd createdb || ! require_cmd dropdb || ! postgres_server_ready; then +if ! has_cmd psql || ! has_cmd pg_restore || ! has_cmd createdb || ! has_cmd dropdb || ! postgres_server_ready; then [[ "${AUTO_INSTALL_POSTGRES,,}" == "yes" ]] || fail "PostgreSQL absent et AUTO_INSTALL_POSTGRES=no" log "PostgreSQL absent : installation en cours..." @@ -181,15 +181,17 @@ else fi log "Vérification de la disponibilité de PostgreSQL..." +PG_READY=false for _ in {1..20}; do if "$SUDO_BIN" -u postgres psql -d postgres -c "SELECT 1;" >/dev/null 2>&1; then + PG_READY=true log "PostgreSQL répond correctement." break fi sleep 1 done -if ! "$SUDO_BIN" -u postgres psql -d postgres -c "SELECT 1;" >/dev/null 2>&1; then +if [[ "$PG_READY" != true ]]; then fail "PostgreSQL ne répond pas correctement" fi diff --git a/RebuildBdd/Checkup/check-target-readiness.sh b/RebuildBdd/Checkup/check-target-readiness.sh index cd6f618..8dbd361 100755 --- a/RebuildBdd/Checkup/check-target-readiness.sh +++ b/RebuildBdd/Checkup/check-target-readiness.sh @@ -30,6 +30,7 @@ while [[ $# -gt 0 ]]; do shift 2 ;; --non-interactive) + # Flag accepté pour compatibilité avec les autres scripts du workflow. NON_INTERACTIVE="yes" shift ;; diff --git a/RebuildBdd/rebuild-bdd-core.sh b/RebuildBdd/rebuild-bdd-core.sh index 41ed462..25ed421 100755 --- a/RebuildBdd/rebuild-bdd-core.sh +++ b/RebuildBdd/rebuild-bdd-core.sh @@ -97,6 +97,10 @@ fail() { } require_cmd() { + command -v "$1" >/dev/null 2>&1 || fail "commande requise absente : $1" +} + +has_cmd() { command -v "$1" >/dev/null 2>&1 } @@ -137,6 +141,13 @@ sql_escape_literal() { printf "%s" "$s" } +validate_db_name() { + local db_name="${1:-}" + + [[ -n "$db_name" ]] || return 1 + [[ "$db_name" =~ ^[a-zA-Z0-9_]+$ ]] || return 1 +} + build_excluded_roles_regex() { local roles_string="${1:-}" local role @@ -168,6 +179,22 @@ build_excluded_roles_regex() { printf '%s' "$joined" } +send_discord() { + local message="$1" + local payload="" + + [[ -n "$DISCORD_WEBHOOK_URL" ]] || return 0 + has_cmd jq || return 0 + has_cmd curl || return 0 + + payload="$(jq -n --arg content "$message" '{content: $content}')" || return 0 + + curl -fsS "$DISCORD_WEBHOOK_URL" \ + -H "Content-Type: application/json" \ + -d "$payload" \ + >/dev/null || true +} + cleanup() { rm -f \ "${LOCAL_DB_DUMP_FILE:-}" \ @@ -252,7 +279,7 @@ else fi for cmd in ssh scp psql pg_restore createdb dropdb python3 grep sed find basename curl; do - require_cmd "$cmd" || fail "commande requise absente : $cmd" + require_cmd "$cmd" done CHECK_SCRIPT="${SCRIPT_DIR}/Checkup/check-postgresql.sh" @@ -264,6 +291,7 @@ fi [[ -f "$SSH_KEY" ]] || fail "clé SSH source backup introuvable : $SSH_KEY" [[ -r "$SSH_KEY" ]] || fail "clé SSH source backup non lisible : $SSH_KEY" +[[ ! -L "$SSH_KEY" ]] || fail "clé SSH source backup ne doit pas être un lien symbolique : $SSH_KEY" export PGPASSWORD @@ -319,7 +347,7 @@ for candidate in "${DBS_ARRAY[@]}"; do done [[ -n "$DB" ]] || fail "base refusée : non présente dans DBS" -[[ "$DB" =~ ^[a-zA-Z0-9_]+$ ]] || fail "nom de base invalide" +validate_db_name "$DB" || fail "nom de base invalide" log "Environnement : $ENV_NAME" log "Base cible : $DB" @@ -479,27 +507,13 @@ pg_restore \ "$LOCAL_DB_DUMP_FILE" \ >>"$LOG_FILE" 2>&1 || fail "échec restauration base ${DB}" -send_discord_message() { - local message="$1" - local payload="" - - [[ -n "$DISCORD_WEBHOOK_URL" ]] || return 0 - - payload="$(python3 -c 'import json,sys; print(json.dumps({"content": sys.argv[1]}))' "$message")" || return 0 - - curl -sS -X POST "$DISCORD_WEBHOOK_URL" \ - -H "Content-Type: application/json" \ - -d "$payload" \ - >/dev/null || true -} - SUCCESS_MESSAGE="✅ REBUILD BDD ${ENV_NAME} Base restaurée : ${DB} Hôte PostgreSQL : ${PGHOST}:${PGPORT} Dump utilisé : $(basename "$LAST_REMOTE_DB_DUMP") Log : ${LOG_FILE}" -send_discord_message "$SUCCESS_MESSAGE" +send_discord "$SUCCESS_MESSAGE" log "Restauration terminée avec succès pour ${DB}" print_json_and_exit "success" "restauration terminée avec succès" 0 diff --git a/RebuildBdd/run-rebuild-bdd.sh b/RebuildBdd/run-rebuild-bdd.sh index b95e350..8b462ee 100755 --- a/RebuildBdd/run-rebuild-bdd.sh +++ b/RebuildBdd/run-rebuild-bdd.sh @@ -124,6 +124,10 @@ REQUESTED_DB="${CLI_DB:-${REQUESTED_DB:-}}" ALLOW_OVERWRITE_RAW="${CLI_OVERWRITE:-${ALLOW_OVERWRITE:-no}}" RESTORE_ROLES_RAW="${CLI_RESTORE_ROLES:-${RESTORE_ROLES:-yes}}" REQUEST_ID="${CLI_REQUEST_ID:-${REQUEST_ID:-$(date '+%Y%m%d%H%M%S')_$RANDOM}}" +LOG_DIR="${RUN_REBUILD_BDD_LOG_DIR:-${SCRIPT_DIR}/logs}" +mkdir -p "$LOG_DIR" +LOG_FILE="${LOG_DIR}/run_rebuild_bdd_${REQUEST_ID}.log" +exec > >(tee -a "$LOG_FILE") 2>&1 ALLOW_OVERWRITE="$(to_bool_yes_no "$ALLOW_OVERWRITE_RAW")" || fail "ALLOW_OVERWRITE invalide" RESTORE_ROLES="$(to_bool_yes_no "$RESTORE_ROLES_RAW")" || fail "RESTORE_ROLES invalide" diff --git a/RecetteScripts/backup-bdd-recette.sh b/RecetteScripts/backup-bdd-recette.sh index e06487b..2743098 100755 --- a/RecetteScripts/backup-bdd-recette.sh +++ b/RecetteScripts/backup-bdd-recette.sh @@ -162,17 +162,22 @@ TS="$(date +'%Y-%m-%d_%H-%M-%S')" BACKUP_DIR_NAME="backup_${TS}" LOG_FILE="${LOG_DIR}/${BACKUP_DIR_NAME}.log" +exec > >(tee -a "$LOG_FILE") 2>&1 + TMP_DIR="$(mktemp -d /tmp/pg_dump_XXXXXX)" || { echo "ERROR: impossible de créer le dossier temporaire" >&2 exit 1 } -exec > >(tee -a "$LOG_FILE") 2>&1 +log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } -log() { echo "---- $(date +'%Y-%m-%d %H:%M:%S') ---- $*"; } +fail() { + log "ERROR: $*" + exit 1 +} require_cmd() { - command -v "$1" >/dev/null 2>&1 + command -v "$1" >/dev/null 2>&1 || fail "commande manquante : $1" } safe_remove_dir() { @@ -192,10 +197,7 @@ export PGPASSWORD ####################################### for cmd in ssh scp curl jq pg_dump pg_dumpall mktemp; do - require_cmd "$cmd" || { - echo "ERROR: commande manquante : $cmd" >&2 - exit 1 - } + require_cmd "$cmd" done [[ -f "$SSH_KEY" ]] || { @@ -222,14 +224,14 @@ chmod 600 "$SSH_KEY" || true DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-}" DISCORD_PING="${DISCORD_PING:-@here}" -discord_send() { +send_discord() { local msg="$1" + local payload [[ -z "${DISCORD_WEBHOOK_URL:-}" ]] && return 0 - local payload payload="$(jq -n --arg content "$msg" '{content: $content}')" || { log "ERROR: impossible de construire le payload JSON Discord" - return 1 + return 0 } curl -fsS \ @@ -251,7 +253,7 @@ Dumps transfer: ✅ Users transfer: ✅ EOF )" - discord_send "$msg" + send_discord "$msg" } ####################################### @@ -265,7 +267,7 @@ discord_msg_users_ok_simple() { Users backup validé EOF )" - discord_send "$msg" + send_discord "$msg" } discord_msg_users_error() { @@ -297,7 +299,7 @@ EOF )" fi - discord_send "$msg" + send_discord "$msg" } ####################################### @@ -312,7 +314,7 @@ discord_msg_db_ok_simple() { Backup validé : ${db} EOF )" - discord_send "$msg" + send_discord "$msg" } discord_msg_db_error() { @@ -347,7 +349,7 @@ EOF )" fi - discord_send "$msg" + send_discord "$msg" } ####################################### @@ -370,11 +372,36 @@ declare -A DB_DETAILS ####################################### LOCK_DIR="/tmp/pg_multi_dump_stream.lock.d" +LOCK_PID_FILE="${LOCK_DIR}/pid" if ! mkdir "$LOCK_DIR" 2>/dev/null; then - log "ERROR: Backup déjà en cours" - discord_msg_users_error "" "" "Lock already exists" - exit 1 + stale_lock="no" + existing_pid="" + + if [[ -f "$LOCK_PID_FILE" ]]; then + existing_pid="$(<"$LOCK_PID_FILE")" + fi + + if [[ "$existing_pid" =~ ^[0-9]+$ ]] && kill -0 "$existing_pid" 2>/dev/null; then + log "ERROR: Backup déjà en cours (PID ${existing_pid})" + discord_msg_users_error "" "" "Lock already exists (PID ${existing_pid})" + exit 1 + fi + + stale_lock="yes" + log "WARNING: lock périmé détecté, nettoyage en cours" + rm -rf -- "$LOCK_DIR" + + mkdir "$LOCK_DIR" 2>/dev/null || fail "impossible de recréer le lock après nettoyage" +fi + +echo $$ > "$LOCK_PID_FILE" || { + rm -rf -- "$LOCK_DIR" + fail "impossible d'écrire le PID du lock" +} + +if [[ "${stale_lock:-no}" == "yes" ]]; then + log "Lock périmé nettoyé." fi cleanup() { @@ -406,18 +433,18 @@ fi ROLES_FILE="${TMP_DIR}/user_${TS}.sql" -set +e - log "Export des rôles PostgreSQL" -pg_dumpall \ +if pg_dumpall \ -h "$PGHOST" \ -p "$PGPORT" \ -U "$PGUSER" \ --globals-only \ - > "$ROLES_FILE" - -RET=$? + > "$ROLES_FILE"; then + RET=0 +else + RET=$? +fi if [[ $RET -ne 0 ]]; then USERS_OK= @@ -428,8 +455,11 @@ else fi if [[ -n "${USERS_EXPORT_OK:-}" ]]; then - scp "${SCP_OPTS[@]}" "$ROLES_FILE" "$IA_SSH:${BACKUP_REMOTE_DIR}/user/" - RET=$? + if scp "${SCP_OPTS[@]}" "$ROLES_FILE" "$IA_SSH:${BACKUP_REMOTE_DIR}/user/"; then + RET=0 + else + RET=$? + fi if [[ $RET -ne 0 ]]; then USERS_OK= @@ -444,14 +474,10 @@ if [[ -n "${USERS_EXPORT_OK:-}" ]]; then fi fi -set -e - ####################################### # Dump des bases ####################################### -set +e - for DB in "${DBS_ARRAY[@]}"; do FILE="${TMP_DIR}/${DB}_${TS}.dump" @@ -461,8 +487,11 @@ for DB in "${DBS_ARRAY[@]}"; do log "Dump $DB" - pg_dump -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -Fc -d "$DB" -f "$FILE" - RET=$? + if pg_dump -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -Fc -d "$DB" -f "$FILE"; then + RET=0 + else + RET=$? + fi if [[ $RET -ne 0 ]]; then DUMPS_OK= @@ -472,8 +501,11 @@ for DB in "${DBS_ARRAY[@]}"; do continue fi - scp "${SCP_OPTS[@]}" "$FILE" "$IA_SSH:${BACKUP_REMOTE_DIR}/${DB}/" - RET=$? + if scp "${SCP_OPTS[@]}" "$FILE" "$IA_SSH:${BACKUP_REMOTE_DIR}/${DB}/"; then + RET=0 + else + RET=$? + fi if [[ $RET -ne 0 ]]; then DUMPS_OK= @@ -482,18 +514,17 @@ for DB in "${DBS_ARRAY[@]}"; do fi done -set -e - ####################################### # Rotation distante ####################################### log "Starting remote rotation: delete backups older than ${RETENTION_DAYS} days" -set +e - -ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${BACKUP_REMOTE_DIR}/user' -type f -name 'user_*.sql' -mtime +${RETENTION_DAYS} -delete" -RET=$? +if ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${BACKUP_REMOTE_DIR}/user' -type f -name 'user_*.sql' -mtime +${RETENTION_DAYS} -delete"; then + RET=0 +else + RET=$? +fi if [[ $RET -ne 0 ]]; then log "ERROR: remote rotation failed for users" @@ -502,8 +533,11 @@ else fi for DB in "${DBS_ARRAY[@]}"; do - ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${BACKUP_REMOTE_DIR}/${DB}' -type f -name '${DB}_*.dump' -mtime +${RETENTION_DAYS} -delete" - RET=$? + if ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${BACKUP_REMOTE_DIR}/${DB}' -type f -name '${DB}_*.dump' -mtime +${RETENTION_DAYS} -delete"; then + RET=0 + else + RET=$? + fi if [[ $RET -ne 0 ]]; then log "ERROR: remote rotation failed for ${DB}" @@ -512,8 +546,6 @@ for DB in "${DBS_ARRAY[@]}"; do fi done -set -e - log "Remote rotation finished" ####################################### diff --git a/RecetteScripts/check-statut-recette.sh b/RecetteScripts/check-statut-recette.sh index 84cfc59..3d8ce59 100755 --- a/RecetteScripts/check-statut-recette.sh +++ b/RecetteScripts/check-statut-recette.sh @@ -135,7 +135,7 @@ add_summary_line() { ####################################### # Envoi du message Discord récapitulatif ####################################### -send_discord_summary() { +send_discord() { [[ -z "${DISCORD_WEBHOOK_URL:-}" ]] && return 0 local header_icon ping_prefix="" @@ -154,7 +154,7 @@ send_discord_summary() { done local payload - payload="$(jq -n --arg content "$msg" '{content: $content}')" + payload="$(jq -n --arg content "$msg" '{content: $content}')" || return 0 curl -fsS -H "Content-Type: application/json" \ -d "$payload" \ @@ -228,7 +228,7 @@ main() { done FAILURES="$failures" - send_discord_summary + send_discord if [[ "$failures" -gt 0 ]]; then exit 2 diff --git a/RecetteScripts/rebuild-bdd-recette.sh b/RecetteScripts/rebuild-bdd-recette.sh index 6d6d157..ac7b107 100644 --- a/RecetteScripts/rebuild-bdd-recette.sh +++ b/RecetteScripts/rebuild-bdd-recette.sh @@ -120,6 +120,10 @@ cleanup() { trap cleanup EXIT require_cmd() { + command -v "$1" >/dev/null 2>&1 || fail "commande requise absente : $1" +} + +has_cmd() { command -v "$1" >/dev/null 2>&1 } @@ -130,11 +134,10 @@ sql_escape_literal() { } validate_db_name() { - local db_name="$1" + local db_name="${1:-}" - [[ -n "$db_name" ]] || fail "nom de base vide" - [[ "$db_name" =~ ^[A-Za-z0-9_]+$ ]] || \ - fail "nom de base invalide : seuls les lettres, chiffres et underscores sont autorisés" + [[ -n "$db_name" ]] || return 1 + [[ "$db_name" =~ ^[a-zA-Z0-9_]+$ ]] || return 1 } build_excluded_roles_regex() { @@ -158,29 +161,20 @@ build_excluded_roles_regex() { # Envoi simple d'un message texte via webhook Discord. # Si DISCORD_WEBHOOK_URL n'est pas défini, on ignore silencieusement l'envoi. ############################################################################### -send_discord_message() { +send_discord() { local message="$1" local payload="" - [[ -n "$DISCORD_WEBHOOK_URL" ]] || { - log "DISCORD_WEBHOOK_URL non défini : notification Discord ignorée." - return 0 - } + [[ -n "$DISCORD_WEBHOOK_URL" ]] || return 0 + has_cmd jq || return 0 + has_cmd curl || return 0 - if ! require_cmd curl; then - log "curl absent : notification Discord ignorée." - return 0 - fi + payload="$(jq -n --arg content "$message" '{content: $content}')" || return 0 - payload="$(jq -n --arg content "$message" '{content: $content}')" || { - log "Impossible de construire le payload JSON Discord." - return 0 - } - - curl -sS -X POST "$DISCORD_WEBHOOK_URL" \ + curl -fsS "$DISCORD_WEBHOOK_URL" \ -H "Content-Type: application/json" \ -d "$payload" \ - >/dev/null || log "Échec d'envoi de la notification Discord." + >/dev/null || true } ############################################################################### @@ -188,6 +182,7 @@ send_discord_message() { ############################################################################### [[ -f "$SSH_KEY" ]] || fail "clé SSH introuvable : $SSH_KEY" [[ -r "$SSH_KEY" ]] || fail "clé SSH non lisible : $SSH_KEY" +[[ ! -L "$SSH_KEY" ]] || fail "clé SSH ne doit pas être un lien symbolique : $SSH_KEY" [[ "$PGPORT" =~ ^[0-9]+$ ]] || fail "PGPORT invalide" [[ "$BACKUP_REMOTE_SSH_PORT" =~ ^[0-9]+$ ]] || fail "BACKUP_REMOTE_SSH_PORT invalide" [[ "$PGUSER" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_-]*$ ]] || fail "PGUSER invalide" @@ -221,7 +216,7 @@ REMOTE_SSH="${BACKUP_REMOTE_USER}@${BACKUP_REMOTE_HOST}" ############################################################################### POSTGRES_INSTALLED=false -if ! require_cmd psql || ! require_cmd pg_restore || ! require_cmd createdb || ! require_cmd dropdb; then +if ! has_cmd psql || ! has_cmd pg_restore || ! has_cmd createdb || ! has_cmd dropdb; then log "PostgreSQL absent : installation en cours..." sudo apt update >>"$LOG_FILE" 2>&1 || fail "échec de apt update" @@ -248,15 +243,17 @@ fi # Attente disponibilité PostgreSQL ############################################################################### log "Vérification de la disponibilité de PostgreSQL..." +PG_READY=false for _ in {1..20}; do if sudo -u postgres psql -d postgres -c "SELECT 1;" >/dev/null 2>&1; then + PG_READY=true log "PostgreSQL répond correctement." break fi sleep 1 done -if ! sudo -u postgres psql -d postgres -c "SELECT 1;" >/dev/null 2>&1; then +if [[ "$PG_READY" != true ]]; then fail "PostgreSQL ne répond pas correctement" fi @@ -303,7 +300,7 @@ else read -r -p "Nom exact de la base à restaurer : " DB fi -validate_db_name "$DB" +validate_db_name "$DB" || fail "nom de base invalide" log "Environnement : $ENV_NAME" log "Base cible sélectionnée : $DB" @@ -424,6 +421,8 @@ if [[ -n "$LOCAL_ROLES_FILE" ]]; then cp "$LOCAL_ROLES_FILE" "$FILTERED_ROLES_FILE" fi + sed -i -E '/^ALTER ROLE .* (NO)?SUPERUSER\b/d' "$FILTERED_ROLES_FILE" + log "Fichier des rôles filtré généré : ${FILTERED_ROLES_FILE}" sed -nE 's/^CREATE ROLE "?([^" ;]+)"?;$/\1/p' "$FILTERED_ROLES_FILE" \ @@ -504,4 +503,4 @@ Hôte PostgreSQL : ${PGHOST}:${PGPORT} Dump utilisé : $(basename "$LAST_REMOTE_DB_DUMP") Log : ${LOG_FILE}" -send_discord_message "$SUCCESS_MESSAGE" +send_discord "$SUCCESS_MESSAGE" From 3bad5bad824c100844078f13d67ad454bba378a7 Mon Sep 17 00:00:00 2001 From: Matteo Date: Thu, 19 Mar 2026 10:02:53 +0100 Subject: [PATCH 3/4] fix : adapt check-statut-recette.sh for better Supervisor integration --- RecetteScripts/check-statut-recette.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/RecetteScripts/check-statut-recette.sh b/RecetteScripts/check-statut-recette.sh index 3d8ce59..a86d2ba 100755 --- a/RecetteScripts/check-statut-recette.sh +++ b/RecetteScripts/check-statut-recette.sh @@ -135,8 +135,21 @@ add_summary_line() { ####################################### # Envoi du message Discord récapitulatif ####################################### +should_send_discord() { + if [[ "$FAILURES" -gt 0 ]]; then + return 0 + fi + + local current_hour current_minute + current_hour="$(date +'%H')" + current_minute="$(date +'%M')" + + [[ "$current_hour" == "19" && "$current_minute" -ge 0 && "$current_minute" -le 4 ]] +} + send_discord() { [[ -z "${DISCORD_WEBHOOK_URL:-}" ]] && return 0 + should_send_discord || return 0 local header_icon ping_prefix="" if [[ "$FAILURES" -eq 0 ]]; then From a9e492962cc888ad7057721f36e450962d1f7351 Mon Sep 17 00:00:00 2001 From: Matteo Date: Thu, 19 Mar 2026 11:04:57 +0100 Subject: [PATCH 4/4] feat : easter egg --- BackupVaultWarden/README.md | 33 +++++++++++++++++++++++++- CheckStorage/README.md | 31 ++++++++++++++++++++++++ README.md | 47 +++++++++++++++++++++++++++++++++++++ RebuildBdd/README.md | 32 +++++++++++++++++++++++++ RecetteScripts/README.md | 31 ++++++++++++++++++++++++ 5 files changed, 173 insertions(+), 1 deletion(-) diff --git a/BackupVaultWarden/README.md b/BackupVaultWarden/README.md index c90241c..4f9f98e 100644 --- a/BackupVaultWarden/README.md +++ b/BackupVaultWarden/README.md @@ -101,6 +101,38 @@ REMOTE_DIR=/home/backup/backups/vaultwarden # 5. Chargement des variables dans le script +
+ + EggMaster + + +
+ Question 2 + +Quel format minimal faut-il donner a `printf` pour afficher une chaine brute ? + +
+ +
+ Indice commande 2 + +```text +'%s' +``` + +
+ +
+ Fragment 2 + +```text +xlIHBldGl0IHN0YWdpYWlyZSBtYXR0ZW8gZHVu +``` + +
+ +
+ Le script charge directement le fichier `.env` avec `source` et exporte automatiquement les variables pendant le chargement. Mécanisme utilisé : @@ -353,4 +385,3 @@ Le script automatise : Ce système permet d’obtenir **une sauvegarde fiable, centralisée et surveillée de Vaultwarden**. -``` diff --git a/CheckStorage/README.md b/CheckStorage/README.md index 6f8f1a6..5f892cb 100644 --- a/CheckStorage/README.md +++ b/CheckStorage/README.md @@ -11,6 +11,37 @@ Le script : 3. compare le taux d’occupation au seuil configuré 4. envoie une alerte Discord si le seuil est dépassé +
+ + EggMaster + + +
+ Question 3 + +Quel operateur shell permet d'envoyer la sortie d'une commande vers la suivante ? + +
+ +
+ Indice commande 3 + +```text +| +``` + +
+ +
+ Fragment 3 + +```text +b3llciB2b2ljaSB1biBsaWVuIG1hZ2lxdWUgZW +``` + +
+ +
## Pré-requis Installation recommandée sur Ubuntu Server : diff --git a/README.md b/README.md index 34712b7..555bd28 100644 --- a/README.md +++ b/README.md @@ -193,3 +193,50 @@ Le dépôt est maintenant pensé prioritairement pour des cibles **Ubuntu Server * `RecetteScripts` = **legacy en cours de migration** * Objectif : convergence vers une **chaîne unique, robuste et automatisable (web/API)** +--- + +
+ + EggMaster + + +Un message est disperse dans les `README` du depot. + +Ordre de reconstruction : + +1. `README.md` +2. `BackupVaultWarden/README.md` +3. `CheckStorage/README.md` +4. `RebuildBdd/README.md` +5. `RecetteScripts/README.md` + +La commande de dechiffrement n'est pas donnee directement. +Elle se reconstruit aussi via des questions cachees dans les `README`. + +
+ Question 1 + +Quelle commande shell permet d'afficher exactement une chaine, sans interpretation particuliere, avant de la transmettre a une autre commande ? + +
+ +
+ Indice commande 1 + +```text +printf +``` + +
+ +
+ Fragment 1 + +```text +YmllbiB2dSB0dSBtJ2FzIHRyb3V2ZXIgbW9pIG +``` + +
+ +
+ diff --git a/RebuildBdd/README.md b/RebuildBdd/README.md index 5d0169a..48521f7 100644 --- a/RebuildBdd/README.md +++ b/RebuildBdd/README.md @@ -36,6 +36,38 @@ En pratique : --- +
+ + EggMaster + + +
+ Question 4 + +Quel utilitaire standard permet de decoder la chaine reconstituee ? + +
+ +
+ Indice commande 4 + +```text +base64 +``` + +
+ +
+ Fragment 4 + +```text +4gcmVjb21wZW5zZSBodHRwczovL3d3dy55b3V0 +``` + +
+ +
+ ## Architecture ### Configuration diff --git a/RecetteScripts/README.md b/RecetteScripts/README.md index 95626fc..87e6972 100644 --- a/RecetteScripts/README.md +++ b/RecetteScripts/README.md @@ -289,6 +289,37 @@ CHECK APP RECETTE 🟢 ``` --- +
+ + EggMaster + + +
+ Question 5 + +Quelle option demande explicitement un decodage plutot qu'un encodage ? + +
+ +
+ Indice commande 5 + +```text +-d +``` + +
+ +
+ Fragment 5 + +```text +dWJlLmNvbS93YXRjaD92PWRRdzR3OVdnWGNR +``` + +
+ +
# 7. Script : rebuild-bdd-recette.sh