From 7b91691ef8105b0e99caa0ae41f8e1c4bffd2cfb Mon Sep 17 00:00:00 2001 From: Matteo Date: Wed, 18 Mar 2026 21:24:30 +0100 Subject: [PATCH] fix : changelog plus readme a jour --- BackupVaultWarden/.env.exemple | 21 ++- BackupVaultWarden/README.md | 121 ++++++++++------ BackupVaultWarden/backup-vaultwarden.sh | 69 +++++++++- CHANGELOG.md | 56 +++----- CheckStorage/.env.exemple | 6 + CheckStorage/README.md | 86 +++++------- CheckStorage/check-storage.sh | 47 +++++-- README.md | 22 ++- RebuildBdd/Checkup/check-target-readiness.sh | 11 +- RebuildBdd/Config/Targets/prod.env.example | 30 ---- RebuildBdd/Config/Targets/prod.env.exemple | 42 ++++++ .../{test.env.example => test.env.exemple} | 84 ++++++------ RebuildBdd/README.md | 38 ++++-- RebuildBdd/bootstrap-target-host.sh | 19 ++- RebuildBdd/rebuild-bdd-core.sh | 3 + RebuildBdd/run-rebuild-bdd.sh | 14 +- RecetteScripts/README.md | 32 +++-- RecetteScripts/backup-bdd-recette.sh | 129 ++++++++++++++++-- RecetteScripts/backup.env.exemple | 11 +- RecetteScripts/check-statut-recette.sh | 4 +- RecetteScripts/rebuild-bdd-recette.sh | 70 ++++++++-- RecetteScripts/rebuild.env.exemple | 14 +- global.env.exemple | 2 +- 23 files changed, 653 insertions(+), 278 deletions(-) delete mode 100644 RebuildBdd/Config/Targets/prod.env.example create mode 100644 RebuildBdd/Config/Targets/prod.env.exemple rename RebuildBdd/Config/Targets/{test.env.example => test.env.exemple} (91%) diff --git a/BackupVaultWarden/.env.exemple b/BackupVaultWarden/.env.exemple index 7db8e83..7244c9a 100644 --- a/BackupVaultWarden/.env.exemple +++ b/BackupVaultWarden/.env.exemple @@ -33,4 +33,23 @@ REMOTE_DIR= ############################################# # Chemin vers la clé privée SSH utilisée pour la connexion -SSH_KEY= \ No newline at end of file +SSH_KEY= + +# Port SSH du serveur distant +BACKUP_REMOTE_SSH_PORT=22 + +# Timeout SSH en secondes +SSH_CONNECT_TIMEOUT=10 + +# Validation stricte des clés hôtes SSH (yes/no) +BACKUP_KNOWN_HOSTS_STRICT=yes + +# Fichier known_hosts utilisé par ssh/scp +BACKUP_KNOWN_HOSTS_FILE=/root/.ssh/known_hosts + +############################################# +# ROTATION DES BACKUPS +############################################# + +# Nombre de jours de conservation des sauvegardes +# BACKUP_RETENTION_DAYS=10 diff --git a/BackupVaultWarden/README.md b/BackupVaultWarden/README.md index f59b59a..80a01ae 100644 --- a/BackupVaultWarden/README.md +++ b/BackupVaultWarden/README.md @@ -28,12 +28,13 @@ Avant de mettre en place le script, vérifier que les éléments suivants sont d - `ssh` - `curl` - `cron` +- `jq` Installation sur Debian / Ubuntu : ```bash sudo apt update -sudo apt install -y tar openssh-client curl cron +sudo apt install -y tar openssh-client curl cron jq ```` --- @@ -43,13 +44,13 @@ sudo apt install -y tar openssh-client curl cron Le script est situé dans : ```bash -/home/matt/vaultwarden/Malio-ops/BackupVaultWarden/ +/home//Malio-ops/BackupVaultWarden/ ``` Structure recommandée : ```bash -/home/matt/vaultwarden/Malio-ops/BackupVaultWarden/ +/home//Malio-ops/BackupVaultWarden/ ├── backup-vaultwarden.sh ├── .env └── README.md @@ -65,24 +66,36 @@ Elles doivent être placées dans un fichier `.env`. ## Exemple de fichier `.env` ```bash -WEBHOOK_URL=https://discord.com/api/webhooks/... +DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/... REMOTE_USER= REMOTE_HOST= -SSH_KEY=/home/matt/.ssh/id_ed25519_vaultwarden_backup +SSH_KEY=/home//.ssh/id_ed25519_backup DATA_DIR=/opt/vaultwarden/data +LOCAL_BACKUP=/var/backups/vaultwarden REMOTE_DIR=/home/backup/backups/vaultwarden +# BACKUP_REMOTE_SSH_PORT=22 +# SSH_CONNECT_TIMEOUT=10 +# BACKUP_KNOWN_HOSTS_STRICT=yes +# BACKUP_KNOWN_HOSTS_FILE=/root/.ssh/known_hosts +# BACKUP_RETENTION_DAYS=10 ``` ## Description des variables -| Variable | Description | -| ----------- | ------------------------------------------------------ | -| WEBHOOK_URL | Webhook Discord pour les notifications | -| REMOTE_USER | Utilisateur du serveur distant | -| REMOTE_HOST | Adresse IP ou DNS du serveur de sauvegarde | -| SSH_KEY | Chemin vers la clé SSH utilisée pour le transfert | -| DATA_DIR | Dossier `data` de Vaultwarden | -| REMOTE_DIR | Dossier de stockage des backups sur le serveur distant | +| Variable | Description | +| --------------------- | -------------------------------------------------------------------- | +| DISCORD_WEBHOOK_URL | Webhook Discord pour les notifications | +| REMOTE_USER | Utilisateur du serveur distant | +| REMOTE_HOST | Adresse IP ou DNS du serveur de sauvegarde | +| SSH_KEY | Chemin vers la clé SSH utilisée pour le transfert | +| DATA_DIR | Dossier `data` de Vaultwarden | +| LOCAL_BACKUP | Dossier local où stocker temporairement l'archive | +| REMOTE_DIR | Dossier de stockage des backups sur le serveur distant | +| BACKUP_REMOTE_SSH_PORT | Port SSH du serveur distant, optionnel, défaut `22` | +| SSH_CONNECT_TIMEOUT | Timeout SSH en secondes, optionnel, défaut `10` | +| BACKUP_KNOWN_HOSTS_STRICT | Validation stricte des hôtes SSH (`yes`/`no`) | +| BACKUP_KNOWN_HOSTS_FILE | Fichier `known_hosts` utilisé par `ssh`/`scp` | +| BACKUP_RETENTION_DAYS | Nombre de jours de conservation distante, optionnel, défaut `10` | --- @@ -121,13 +134,13 @@ Le transfert des sauvegardes utilise une **clé SSH** afin de permettre une conn Sur la machine exécutant les scripts : ```bash -ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_bitwarden +ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_backup ``` #### 2. Copie de la clé vers le serveur distant ```bash -ssh-copy-id -i ~/.ssh/id_ed25519_bitwarden.pub @ +ssh-copy-id -i ~/.ssh/id_ed25519_backup.pub @ ``` Cette commande ajoute la clé dans : @@ -141,20 +154,20 @@ sur la machine IA. #### 3. Vérification de la connexion ```bash -ssh -i ~/.ssh/id_ed25519_bitwarden backup@192.168.0.179 +ssh -i ~/.ssh/id_ed25519_backup -o StrictHostKeyChecking=yes @ ``` #### 4. Vérification des fichiers de clé ```bash -ls ~/.ssh/id_ed25519_bitwarden* +ls ~/.ssh/id_ed25519_backup* ``` Fichiers attendus : ``` -~/.ssh/id_ed25519_bitwarden -~/.ssh/id_ed25519_bitwarden.pub +~/.ssh/id_ed25519_backup +~/.ssh/id_ed25519_backup.pub ``` #### 5. Permissions SSH @@ -163,8 +176,8 @@ Machine locale : ```bash chmod 700 ~/.ssh -chmod 600 ~/.ssh/id_ed25519_bitwarden -chmod 644 ~/.ssh/id_ed25519_bitwarden.pub +chmod 600 ~/.ssh/id_ed25519_backup +chmod 644 ~/.ssh/id_ed25519_backup.pub ``` Machine distante : @@ -174,10 +187,19 @@ chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys ``` -#### 6. Déclaration dans `.env` +#### 6. Provisionnement de `known_hosts` + +Le script est prévu pour fonctionner avec validation stricte des hôtes SSH. ```bash -SSH_KEY=/home/matt/.ssh/id_ed25519_bitwarden +ssh-keyscan -H >> ~/.ssh/known_hosts +chmod 600 ~/.ssh/known_hosts +``` + +#### 7. Déclaration dans `.env` + +```bash +SSH_KEY=/home//.ssh/id_ed25519_backup ``` Cette clé sera utilisée automatiquement par les scripts (`scp` / `ssh`) pour transférer les sauvegardes. @@ -188,7 +210,7 @@ Cette clé sera utilisée automatiquement par les scripts (`scp` / `ssh`) pour t Le script crée une archive compressée du dossier `data` : ```bash -tar -czf "$LOCAL_BACKUP" -C "$(dirname "$DATA_DIR")" "$(basename "$DATA_DIR")" +tar -czf "$LOCAL_BACKUP_FILE" -C "$(dirname "$DATA_DIR")" "$(basename "$DATA_DIR")" ``` Cela permet d’obtenir une sauvegarde portable et compressée. @@ -200,7 +222,7 @@ Cela permet d’obtenir une sauvegarde portable et compressée. Une fois l’archive créée : ```bash -scp "${SSH_OPTS[@]}" "$LOCAL_BACKUP" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/" +scp "${SCP_OPTS[@]}" "$LOCAL_BACKUP_FILE" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/" ``` Le fichier est envoyé vers le serveur de sauvegarde via SCP. @@ -209,23 +231,22 @@ Le fichier est envoyé vers le serveur de sauvegarde via SCP. # 9. Notification Discord -Le script envoie une notification Discord pour informer de l’état de la sauvegarde. +Le script envoie une notification Discord pour informer de l'etat de la sauvegarde. Construction du message : ```bash -local msg="**@here Backup Vaultwarden $color**\n" +msg="**${ping}Backup Vaultwarden ${icon}**\n" msg+="Backup: ${BACKUP_NAME}\n" -msg+="Data transfer: $dumps_display\n" -[[ -n "$details" ]] && msg+="Details: $details" +msg+="Data transfer: ${status_line}\n" +[[ -n "$details" ]] && msg+="Détails: ${details}" ``` Envoi du message : ```bash -curl -fsS -H "Content-Type: application/json" \ --d "{\"content\":\"$msg\"}" \ -"$WEBHOOK_URL" +payload="$(jq -n --arg content "$msg" '{content: $content}')" +curl -fsS -H "Content-Type: application/json" -d "$payload" "$DISCORD_WEBHOOK_URL" ``` Le message indique : @@ -237,7 +258,27 @@ Le message indique : --- -# 10. Planification avec cron +# 10. Rotation distante des sauvegardes + +Le script supprime les archives distantes plus anciennes que la durée de retention configurée. + +Configuration dans `.env` : + +```bash +# BACKUP_RETENTION_DAYS=10 +``` + +Commande utilisée : + +```bash +find "$REMOTE_DIR" -type f -name 'vaultwarden-backup-*.tar.gz' -mtime +$RETENTION_DAYS -delete +``` + +Si la variable n'est pas définie, le script utilise `10` jours par défaut. + +--- + +# 11. Planification avec cron Le script est exécuté automatiquement tous les jours à 19h. @@ -250,7 +291,7 @@ crontab -e Ajouter : ```bash -0 19 * * * /home/matt/vaultwarden/Malio-ops/BackupVaultWarden/backup-vaultwarden.sh >> /var/log/vaultwarden_backup.log 2>&1 +0 19 * * * /home//Malio-ops/BackupVaultWarden/backup-vaultwarden.sh >> /var/log/vaultwarden_backup.log 2>&1 ``` Signification : @@ -267,29 +308,29 @@ Le script s’exécute donc **tous les jours à 19h00**. --- -# 11. Nettoyage +# 12. Nettoyage Une fois la sauvegarde transférée : ```bash -rm -f "$LOCAL_BACKUP" +rm -f "$LOCAL_BACKUP_FILE" ``` Cela évite de remplir le disque de la machine Vaultwarden. --- -# 12. Test manuel +# 13. Test manuel Avant de mettre le script en cron, tester : ```bash -bash /home/matt/vaultwarden/Malio-ops/BackupVaultWarden/backup-vaultwarden.sh +bash /home//Malio-ops/BackupVaultWarden/backup-vaultwarden.sh ``` --- -# 13. Vérification des logs +# 14. Vérification des logs Logs : @@ -299,7 +340,7 @@ cat /var/log/vaultwarden_backup.log --- -# 14. Résumé +# 15. Résumé Le script automatise : diff --git a/BackupVaultWarden/backup-vaultwarden.sh b/BackupVaultWarden/backup-vaultwarden.sh index 3f96dd3..9a362bb 100755 --- a/BackupVaultWarden/backup-vaultwarden.sh +++ b/BackupVaultWarden/backup-vaultwarden.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash set -euo pipefail +umask 077 ####################################### # Chemins fixes du script @@ -27,6 +28,7 @@ log() { # Chargement du .env ####################################### set -a +# shellcheck disable=SC1090 source "$ENV_FILE" set +a @@ -40,6 +42,10 @@ set +a : "${REMOTE_HOST:?Variable REMOTE_HOST manquante dans .env}" : "${REMOTE_DIR:?Variable REMOTE_DIR manquante dans .env}" : "${SSH_KEY:?Variable SSH_KEY manquante dans .env}" +: "${BACKUP_REMOTE_SSH_PORT:=22}" +: "${SSH_CONNECT_TIMEOUT:=10}" +: "${BACKUP_KNOWN_HOSTS_STRICT:=yes}" +: "${BACKUP_KNOWN_HOSTS_FILE:=${HOME}/.ssh/known_hosts}" ####################################### # Variables backup @@ -50,7 +56,53 @@ BACKUP_NAME="${BACKUP_PREFIX}-${DATE}.tar.gz" LOCAL_BACKUP_FILE="${LOCAL_BACKUP}/${BACKUP_NAME}" RETENTION_DAYS="${BACKUP_RETENTION_DAYS:-10}" -SSH_OPTS=(-i "$SSH_KEY" -o IdentitiesOnly=yes -o BatchMode=yes -o ConnectTimeout=10) +[[ "$BACKUP_REMOTE_SSH_PORT" =~ ^[0-9]+$ ]] || { + echo "ERROR: Variable BACKUP_REMOTE_SSH_PORT invalide dans .env" >&2 + exit 1 +} + +[[ "$SSH_CONNECT_TIMEOUT" =~ ^[0-9]+$ ]] || { + echo "ERROR: Variable SSH_CONNECT_TIMEOUT invalide dans .env" >&2 + exit 1 +} + +[[ "$RETENTION_DAYS" =~ ^[0-9]+$ ]] || { + echo "ERROR: Variable BACKUP_RETENTION_DAYS invalide dans .env" >&2 + exit 1 +} + +case "${BACKUP_KNOWN_HOSTS_STRICT,,}" in + yes|y|oui|o|true|1) BACKUP_KNOWN_HOSTS_STRICT="yes" ;; + no|n|non|false|0) BACKUP_KNOWN_HOSTS_STRICT="no" ;; + *) + echo "ERROR: Variable BACKUP_KNOWN_HOSTS_STRICT invalide dans .env" >&2 + exit 1 + ;; +esac + +mkdir -p "$(dirname "$BACKUP_KNOWN_HOSTS_FILE")" +chmod 700 "$(dirname "$BACKUP_KNOWN_HOSTS_FILE")" || true +touch "$BACKUP_KNOWN_HOSTS_FILE" +chmod 600 "$BACKUP_KNOWN_HOSTS_FILE" || true + +SSH_OPTS=( + -i "$SSH_KEY" + -p "$BACKUP_REMOTE_SSH_PORT" + -o IdentitiesOnly=yes + -o BatchMode=yes + -o ConnectTimeout="$SSH_CONNECT_TIMEOUT" + -o StrictHostKeyChecking="$BACKUP_KNOWN_HOSTS_STRICT" + -o UserKnownHostsFile="$BACKUP_KNOWN_HOSTS_FILE" +) +SCP_OPTS=( + -i "$SSH_KEY" + -P "$BACKUP_REMOTE_SSH_PORT" + -o IdentitiesOnly=yes + -o BatchMode=yes + -o ConnectTimeout="$SSH_CONNECT_TIMEOUT" + -o StrictHostKeyChecking="$BACKUP_KNOWN_HOSTS_STRICT" + -o UserKnownHostsFile="$BACKUP_KNOWN_HOSTS_FILE" +) mkdir -p "$LOCAL_BACKUP" @@ -95,11 +147,22 @@ fail() { exit 1 } +require_cmd() { + command -v "$1" >/dev/null 2>&1 || fail "commande requise absente : $1" +} + ####################################### # Vérifications préalables ####################################### [[ -d "$DATA_DIR" ]] || fail "Le dossier source n'existe pas : $DATA_DIR" [[ -f "$SSH_KEY" ]] || fail "La clé SSH est introuvable : $SSH_KEY" +[[ -r "$SSH_KEY" ]] || fail "La clé SSH est non lisible : $SSH_KEY" +[[ ! -L "$SSH_KEY" ]] || fail "La clé SSH ne doit pas être un lien symbolique : $SSH_KEY" +chmod 600 "$SSH_KEY" || true + +for cmd in tar ssh scp jq curl find; do + require_cmd "$cmd" +done log "Début du backup Vaultwarden" log "Source : $DATA_DIR" @@ -123,7 +186,7 @@ ssh "${SSH_OPTS[@]}" "$REMOTE_USER@$REMOTE_HOST" "mkdir -p '$REMOTE_DIR'" \ ####################################### # Envoi du backup ####################################### -scp "${SSH_OPTS[@]}" "$LOCAL_BACKUP_FILE" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/" \ +scp "${SCP_OPTS[@]}" "$LOCAL_BACKUP_FILE" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/" \ || fail "Erreur lors de l'envoi du backup vers $REMOTE_HOST" log "Backup envoyé sur $REMOTE_HOST:$REMOTE_DIR" @@ -147,4 +210,4 @@ rm -f "$LOCAL_BACKUP_FILE" || fail "Impossible de supprimer le backup local $LOC ####################################### log "Backup $BACKUP_NAME terminé et envoyé sur $REMOTE_HOST:$REMOTE_DIR" discord_ping "true" "Backup envoyé avec succès vers $REMOTE_HOST" -echo "Backup $BACKUP_NAME terminé et envoyé sur $REMOTE_HOST:$REMOTE_DIR" \ No newline at end of file +echo "Backup $BACKUP_NAME terminé et envoyé sur $REMOTE_HOST:$REMOTE_DIR" diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a78afe..ca87d16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,45 +1,29 @@ # Changelog -Liste des évolutions du projet Scripts Serveur +Ce projet suit le format [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/) +et applique le versionnement semantique. -## [1.0.0] -### Parameters -Ajouter dans le fichier /RecetteScripts/.env -SSH_TIMEOUT -BACKUP_LOG_DIR -APP_LOG_DIR -DISCORD_WEBHOOK_URL -DISCORD_PING -CHECK_CONNECT_TIMEOUT -CHECK_MAX_TIME -APP_URLS +## [Unreleased] -Ajouter dans le fichier /CheckStorage/.env -WEBHOOK_URL - -Ajouter dans le fichier /BackupVaultWarden/.env -DATA_DIR -LOCAL_BACKUP -REMOTE_USER -REMOTE_HOST -REMOTE_DIR -SSH_KEY +## [1.0.0] - 2026-03-18 ### Added -* [#361] Script dump BDD -* [#367] Avoir une notification discord quand les backup sont faites -* [#368] Script pour check que les applis ne sont pas hors-ligne -* first push -* Reorganisation des fichiers et dossiers -* [#372] Script de check si la machine a le stockage plein -* [#378] Script Backup BDD Vaultwarden -* [#381] Variabiliser tous les scripts -* [#384] Fix Correctif -* [#390] Rotation des scripts -* [#392] Scripts de reconstruction des bases de données -* [#427] Correctifs -* [#433] Ajout du dossier RebuildBdd et creation de la theorique du script de rebuild de base de données -* [#002-19] correctif generaliser sur l'application suite aux nombreuses modifications et ajout de script +- Ajout des scripts legacy de sauvegarde PostgreSQL, de verification de statut applicatif et de supervision du stockage. +- Ajout du script de sauvegarde Vaultwarden. +- Ajout des scripts de reconstruction de bases PostgreSQL dans `RebuildBdd/`. +- Ajout du bootstrap cible, du precheck et de l'orchestration de reconstruction. +- Ajout du support d'utilisation via une interface web avec sorties JSON. +- Ajout des fichiers d'exemple de configuration pour les scripts et les cibles. + ### Changed +- Reorganisation des fichiers et des dossiers du projet. +- Variabilisation des scripts via des fichiers `.env`. +- Ajout de la rotation des sauvegardes. +- Harmonisation progressive de la documentation et des exemples de configuration. +- Ajout du bit executable sur les scripts qui doivent etre lancables directement. ### Fixed +- Correctifs multiples sur les scripts legacy et sur le flux `RebuildBdd`. +- Corrections de chemins de configuration et de telechargement des dumps. +- Corrections de messages Discord, de revue de code et d'orthographe dans la documentation. +- Correctifs sur la preparation PostgreSQL, `scp`, `sudoers` et le reperage des environnements cibles. diff --git a/CheckStorage/.env.exemple b/CheckStorage/.env.exemple index 99028f9..3bc41a1 100644 --- a/CheckStorage/.env.exemple +++ b/CheckStorage/.env.exemple @@ -4,3 +4,9 @@ # Webhook Discord pour notifications DISCORD_WEBHOOK_URL= + +# Mention Discord en cas d'alerte +DISCORD_PING=@here + +# Seuil d'alerte en pourcentage d'utilisation +STORAGE_ALERT_LIMIT=70 diff --git a/CheckStorage/README.md b/CheckStorage/README.md index 16b86fe..6f8f1a6 100644 --- a/CheckStorage/README.md +++ b/CheckStorage/README.md @@ -1,65 +1,51 @@ -# Scripts de vérification de l'espace de stockage +# CheckStorage -Ce projet contient des scripts pour vérifier l'espace de stockage +Script de vérification de l’espace disque sur Ubuntu Server, avec notification Discord optionnelle. -## Préambule -Ce script est conçu pour vérifier l'espace de stockage disponible sur un serveur et envoyer une alerte -La vérification de l'espace de stockage ce fait sur la partition racine. -La limite d'alerte est fixée à 70% d'utilisation, mais vous pouvez ajuster cette valeur dans le script selon vos besoins. +## Fonctionnement -## Installation du script +Le script : -1. Clonez le dépôt GitHub : - ```bash - git clone https://gitea.malio.fr/MALIO-DEV/Scripts-Serveur.git - ``` - -2. Accédez au répertoire du projet : -3. ```bash - cd Scripts-Serveur/CheckStorage - ``` -### Génération de la clé SSH +1. charge `.env` +2. lit l’utilisation de la partition `/` +3. compare le taux d’occupation au seuil configuré +4. envoie une alerte Discord si le seuil est dépassé -Sur la machine exécutant les scripts : +## Pré-requis + +Installation recommandée sur Ubuntu Server : ```bash -ssh-keygen -t ed25519 -f ~/.ssh/check_storage_key +sudo apt update +sudo apt install -y coreutils gawk jq curl ``` -Copier la clé sur le serveur distant : + +`jq` et `curl` ne sont nécessaires que si `DISCORD_WEBHOOK_URL` est renseigné. + +## Configuration ```bash -ssh-copy-id -i ~/.ssh/check_storage_key.pub user@serveur +cp .env.exemple .env +chmod 600 .env ``` -Tester la connexion sans mot de passe : + +Variables disponibles : + +- `DISCORD_WEBHOOK_URL` : webhook Discord, optionnel +- `DISCORD_PING` : mention en cas d’alerte, optionnel, défaut `@here` +- `STORAGE_ALERT_LIMIT` : seuil d’alerte en pourcentage, défaut `70` + +## Utilisation ```bash -ssh -i ~/.ssh/check_storage_key @ +chmod 700 check-storage.sh +./check-storage.sh ``` -## Utilisation du script -0. Copiez le fichier d'environnement exemple et modifiez les variables selon votre configuration : - ```bash - cp .env.example .env - nano .env - ``` - -1. Donnez les permissions d'exécution au script : - ```bash - chmod +x check-storage.sh - ``` -2. Exécutez le script pour vérifier l'espace de stockage : - ```bash - ./check-storage.sh - ``` -## Initialisé un cron pour exécuter le script régulièrement -1. Ouvrez le crontab pour l'édition : - ```bash - crontab -e - ``` -2. Ajoutez la ligne suivante pour exécuter le script tous les jours à 7h50 du matin : - ```bash - 50 7 * * * /chemin/vers/le/script/check-storage.sh - ``` - -## Avertissement -Assurez-vous de remplacer `/chemin/vers/le/script/check-storage.sh` par le chemin réel où se trouve le script sur votre système. +## Cron + +Exemple quotidien à `07:50` : + +```bash +50 7 * * * /chemin/vers/CheckStorage/check-storage.sh +``` diff --git a/CheckStorage/check-storage.sh b/CheckStorage/check-storage.sh index 4d8995a..3084667 100755 --- a/CheckStorage/check-storage.sh +++ b/CheckStorage/check-storage.sh @@ -1,5 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash set -euo pipefail +umask 077 ############################################################################### # CHARGEMENT DU .env @@ -22,8 +23,34 @@ set +a # CONFIGURATION ############################################################################### +require_cmd() { + command -v "$1" >/dev/null 2>&1 || { + echo "ERROR: commande requise absente : $1" >&2 + exit 1 + } +} + # Limite maximale d'utilisation du disque en pourcentage -limit=70 +limit="${STORAGE_ALERT_LIMIT:-70}" +DISCORD_PING="${DISCORD_PING:-@here}" + +[[ "$limit" =~ ^[0-9]+$ ]] || { + echo "ERROR: STORAGE_ALERT_LIMIT invalide" >&2 + exit 1 +} + +(( limit >= 1 && limit <= 99 )) || { + echo "ERROR: STORAGE_ALERT_LIMIT doit être compris entre 1 et 99" >&2 + exit 1 +} + +require_cmd df +require_cmd awk + +if [[ -n "${DISCORD_WEBHOOK_URL:-}" ]]; then + require_cmd jq + require_cmd curl +fi ############################################################################### # RÉCUPÉRATION DES INFORMATIONS DISQUE @@ -48,15 +75,17 @@ avail_gb=$(awk -v b="$avail_bytes" 'BEGIN {printf "%.2f", b/1024/1024/1024}') if [ "$usage" -ge "$limit" ]; then - msgLimit="@here\n**CHECK STOCKAGE :red_circle:**\nLimite autorisé : ${limit}%\nUtilisation actuelle: ${usage}%\nEspace restant: ${free}%\nUtilise / total: ${used_gb} GB / ${total_gb} GB\nDisponible: ${avail_gb} GB\nHeure: $(date)" + 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)" - payload="$(jq -n --arg content "$msgLimit" '{content: $content}')" + if [[ -n "${DISCORD_WEBHOOK_URL:-}" ]]; then + payload="$(jq -n --arg content "$msgLimit" '{content: $content}')" - curl -X POST \ - -H "Accept: application/json" \ - -H "Content-Type: application/json; charset=utf-8" \ - -d "$payload" \ - "$DISCORD_WEBHOOK_URL" + curl -fsS \ + -H "Accept: application/json" \ + -H "Content-Type: application/json; charset=utf-8" \ + -d "$payload" \ + "$DISCORD_WEBHOOK_URL" >/dev/null || true + fi fi diff --git a/README.md b/README.md index 6007c5e..977fb67 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,3 @@ -Voici une version **corrigée, structurée et professionnalisée**, adaptée à votre contexte actuel (scripts rebuild + infra + exploitation). -Format directement exploitable pour README.md / BookStack. - ---- - # MALIO-OPS Ce dépôt centralise l’ensemble des **scripts d’exploitation, de maintenance et d’automatisation** utilisés dans l’infrastructure MALIO. @@ -150,6 +145,16 @@ cp global.env.exemple global.env * Validation des paramètres en entrée (scripts rebuild) * Isolation des environnements (cibles distinctes) * Logs sans données sensibles +* Validation stricte des hôtes SSH recommandée et utilisée par défaut sur les scripts durcis +* Permissions restrictives recommandées sur les `.env`, clés privées et `known_hosts` + +## Déploiement Ubuntu Server + +Le dépôt est maintenant pensé prioritairement pour des cibles **Ubuntu Server** : + +* bootstrap `RebuildBdd` basé sur `apt`, `systemctl` et `sudo -n` +* clients SSH en mode batch avec `StrictHostKeyChecking=yes` quand le mode strict est actif +* exemples `.env` mis à jour pour expliciter les ports SSH, `known_hosts` et les timeouts --- @@ -180,10 +185,3 @@ cp global.env.exemple global.env * `RecetteScripts` = **legacy en cours de migration** * Objectif : convergence vers une **chaîne unique, robuste et automatisable (web/API)** ---- - -Si vous le souhaitez, je peux : - -* restructurer votre README `RebuildBdd` pour qu’il soit au même niveau -* ajouter un schéma d’architecture (flux SSH / dump / restore) -* ou intégrer directement votre workflow réel (IA machine + backup server + cible) dans la doc diff --git a/RebuildBdd/Checkup/check-target-readiness.sh b/RebuildBdd/Checkup/check-target-readiness.sh index e44b378..109fdb2 100755 --- a/RebuildBdd/Checkup/check-target-readiness.sh +++ b/RebuildBdd/Checkup/check-target-readiness.sh @@ -70,6 +70,8 @@ print_stdout() { [[ "$JSON_ONLY" == "yes" ]] || echo "$*" } +LOG_FILE=/dev/stderr + log() { local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*" echo "$msg" >>"$LOG_FILE" @@ -114,13 +116,16 @@ require_env_vars() { validate_env_values() { [[ "$PGPORT" =~ ^[0-9]+$ ]] || fail "PGPORT invalide" + [[ "$ENV_NAME" =~ ^[a-zA-Z0-9_-]+$ ]] || fail "ENV_NAME invalide" [[ -n "$DBS" ]] || fail "DBS vide" [[ "$PGUSER" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_-]*$ ]] || fail "PGUSER invalide" [[ -n "$BACKUP_REMOTE_HOST" ]] || fail "BACKUP_REMOTE_HOST vide" [[ -n "$BACKUP_REMOTE_USER" ]] || fail "BACKUP_REMOTE_USER vide" [[ -n "$BACKUP_REMOTE_DIR" ]] || fail "BACKUP_REMOTE_DIR vide" BACKUP_REMOTE_SSH_PORT="${BACKUP_REMOTE_SSH_PORT:-22}" + BACKUP_KNOWN_HOSTS_STRICT="${BACKUP_KNOWN_HOSTS_STRICT:-yes}" [[ "$BACKUP_REMOTE_SSH_PORT" =~ ^[0-9]+$ ]] || fail "BACKUP_REMOTE_SSH_PORT invalide" + to_bool_yes_no "$BACKUP_KNOWN_HOSTS_STRICT" >/dev/null || fail "BACKUP_KNOWN_HOSTS_STRICT invalide" } prepare_log_file() { @@ -193,7 +198,11 @@ prepare_known_hosts() { chmod 644 "$known_hosts" || fail "impossible de chmod 644 sur known_hosts" if ! ssh-keygen -F "$BACKUP_REMOTE_HOST" -f "$known_hosts" >/dev/null 2>&1; then - log "Ajout de ${BACKUP_REMOTE_HOST}:${BACKUP_REMOTE_SSH_PORT} à known_hosts" + if [[ "$(to_bool_yes_no "$BACKUP_KNOWN_HOSTS_STRICT")" == "yes" ]]; then + fail "hôte ${BACKUP_REMOTE_HOST} absent de known_hosts en mode strict ; provisionner l'empreinte manuellement" + fi + + log "Ajout non strict de ${BACKUP_REMOTE_HOST}:${BACKUP_REMOTE_SSH_PORT} à known_hosts" ssh-keyscan -p "$BACKUP_REMOTE_SSH_PORT" -H "$BACKUP_REMOTE_HOST" >>"$known_hosts" 2>/dev/null || \ fail "échec de récupération de la clé hôte pour ${BACKUP_REMOTE_HOST}" else diff --git a/RebuildBdd/Config/Targets/prod.env.example b/RebuildBdd/Config/Targets/prod.env.example deleted file mode 100644 index 7070ae3..0000000 --- a/RebuildBdd/Config/Targets/prod.env.example +++ /dev/null @@ -1,30 +0,0 @@ - -############################################################################### -# CIBLE : prod -############################################################################### - -# TARGET_HOST_prod=10.0.0.20 -# TARGET_PORT_prod=22 -# TARGET_BOOTSTRAP_USER_prod=backup_liot -# TARGET_BOOTSTRAP_SSH_KEY_prod=/home/matteo/.ssh/id_ed25519_target_prod -# TARGET_RUNTIME_USER_prod=backup_liot -# TARGET_ENABLE_BOOTSTRAP_prod=yes -# TARGET_BOOTSTRAP_ALLOW_PASSWORDLESS_SUDO_prod=yes -# TARGET_REPO_DIR_prod=/home/backup_liot/RebuildBdd -# TARGET_ENV_FILE_prod=/home/backup_liot/RebuildBdd/.env -# TARGET_ENV_NAME_prod=PROD -# TARGET_PGHOST_prod=127.0.0.1 -# TARGET_PGPORT_prod=5432 -# TARGET_PGUSER_prod=backup_liot -# TARGET_PGPASSWORD_prod=change_me_prod_password -# TARGET_DBS_prod="sirh inventory ferme" -# TARGET_BACKUP_SUBDIR_prod=bdd-prod -# TARGET_BACKUP_LOG_DIR_prod=/home/backup_liot/logs/rebuild_bdd -# TARGET_LOCAL_RESTORE_BASE_DIR_prod=/home/backup_liot/RebuildBdd/restore_tmp -# TARGET_SSH_KEY_prod=/home/backup_liot/.ssh/id_ed25519_backup_readonly -# TARGET_REMOTE_ROLES_DIR_NAME_prod=user -# TARGET_EXCLUDED_RESTORE_ROLES_prod="postgres" -# TARGET_AUTO_INSTALL_POSTGRES_prod=yes -# TARGET_AUTO_CREATE_PGUSER_prod=yes -# TARGET_PGUSER_SUPERUSER_prod=no -# TARGET_AUTO_CONFIGURE_SUDOERS_prod=no \ No newline at end of file diff --git a/RebuildBdd/Config/Targets/prod.env.exemple b/RebuildBdd/Config/Targets/prod.env.exemple new file mode 100644 index 0000000..a7d6263 --- /dev/null +++ b/RebuildBdd/Config/Targets/prod.env.exemple @@ -0,0 +1,42 @@ +############################################################################### +# config/targets/prod.env.exemple +############################################################################### + +# SSH bootstrap cible +TARGET_HOST=192.168.1.60 +TARGET_PORT=22 +TARGET_BOOTSTRAP_USER=backup_liot +TARGET_BOOTSTRAP_SSH_KEY=/home/matteo/.ssh/id_ed25519_target_prod +TARGET_RUNTIME_USER=backup_liot + +# 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 + +# PostgreSQL cible +TARGET_ENV_NAME=PROD +TARGET_PGHOST=127.0.0.1 +TARGET_PGPORT=5432 +TARGET_PGUSER=backup_liot +TARGET_PGPASSWORD=change_me_pg_password +TARGET_DBS="sirh inventory ferme" + +# Backup cible +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 + +# Options cible +TARGET_REMOTE_ROLES_DIR_NAME=user +TARGET_EXCLUDED_RESTORE_ROLES="postgres" +TARGET_AUTO_INSTALL_POSTGRES=yes +TARGET_AUTO_CREATE_PGUSER=yes +TARGET_PGUSER_SUPERUSER=no +TARGET_AUTO_CONFIGURE_SUDOERS=no diff --git a/RebuildBdd/Config/Targets/test.env.example b/RebuildBdd/Config/Targets/test.env.exemple similarity index 91% rename from RebuildBdd/Config/Targets/test.env.example rename to RebuildBdd/Config/Targets/test.env.exemple index 8244082..3ca9e61 100644 --- a/RebuildBdd/Config/Targets/test.env.example +++ b/RebuildBdd/Config/Targets/test.env.exemple @@ -1,42 +1,42 @@ -############################################################################### -# config/targets/test.env.example -############################################################################### - -# SSH bootstrap cible -TARGET_HOST=192.168.1.50 -TARGET_PORT=22 -TARGET_BOOTSTRAP_USER=backup_liot -TARGET_BOOTSTRAP_SSH_KEY=/home/matteo/.ssh/id_ed25519_target_test -TARGET_RUNTIME_USER=backup_liot - -# 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 - -# PostgreSQL cible -TARGET_ENV_NAME=RECETTE -TARGET_PGHOST=127.0.0.1 -TARGET_PGPORT=5432 -TARGET_PGUSER=backup_liot -TARGET_PGPASSWORD=change_me_pg_password -TARGET_DBS="sirh inventory ferme" - -# Backup cible -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 - -# Options cible -TARGET_REMOTE_ROLES_DIR_NAME=user -TARGET_EXCLUDED_RESTORE_ROLES="postgres" -TARGET_AUTO_INSTALL_POSTGRES=yes -TARGET_AUTO_CREATE_PGUSER=yes -TARGET_PGUSER_SUPERUSER=no -TARGET_AUTO_CONFIGURE_SUDOERS=no \ No newline at end of file +############################################################################### +# config/targets/test.env.exemple +############################################################################### + +# SSH bootstrap cible +TARGET_HOST=192.168.1.50 +TARGET_PORT=22 +TARGET_BOOTSTRAP_USER=backup_liot +TARGET_BOOTSTRAP_SSH_KEY=/home/matteo/.ssh/id_ed25519_target_test +TARGET_RUNTIME_USER=backup_liot + +# 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 + +# PostgreSQL cible +TARGET_ENV_NAME=RECETTE +TARGET_PGHOST=127.0.0.1 +TARGET_PGPORT=5432 +TARGET_PGUSER=backup_liot +TARGET_PGPASSWORD=change_me_pg_password +TARGET_DBS="sirh inventory ferme" + +# Backup cible +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 + +# Options cible +TARGET_REMOTE_ROLES_DIR_NAME=user +TARGET_EXCLUDED_RESTORE_ROLES="postgres" +TARGET_AUTO_INSTALL_POSTGRES=yes +TARGET_AUTO_CREATE_PGUSER=yes +TARGET_PGUSER_SUPERUSER=no +TARGET_AUTO_CONFIGURE_SUDOERS=no diff --git a/RebuildBdd/README.md b/RebuildBdd/README.md index 102cf3c..4013efb 100644 --- a/RebuildBdd/README.md +++ b/RebuildBdd/README.md @@ -46,7 +46,7 @@ Le projet utilise deux niveaux de configuration : Fichier : ```bash -config/global.env +Config/global.env ```` Contient les paramètres stables, par exemple : @@ -62,7 +62,7 @@ Contient les paramètres stables, par exemple : Fichiers : ```bash -config/targets/.env +Config/Targets/.env ``` Chaque fichier cible contient : @@ -83,9 +83,9 @@ RebuildBdd/ ├── create-target-config.sh ├── run-rebuild-bdd.sh ├── rebuild-bdd-core.sh -├── config/ +├── Config/ │ ├── global.env -│ └── targets/ +│ └── Targets/ │ ├── test.env │ └── prod.env └── Checkup/ @@ -102,7 +102,7 @@ RebuildBdd/ Crée ou met à jour un fichier cible dans : ```bash -config/targets/.env +Config/Targets/.env ``` Usage : @@ -233,6 +233,7 @@ Doit disposer de : * `scp` * `git` * `python3` +* `jq` si vous consommez les JSON côté tooling ### Machine cible @@ -240,7 +241,8 @@ Le bootstrap suppose : * accès SSH fonctionnel ; * utilisateur bootstrap existant ; -* soit `root`, soit `sudo -n` déjà disponible pour le bootstrap initial. +* soit `root`, soit `sudo -n` déjà disponible pour le bootstrap initial ; +* `known_hosts` correctement provisionné si le mode strict SSH est activé vers le serveur de backup. ### Serveur de backup @@ -250,6 +252,20 @@ Doit : * accepter la clé de lecture backup ; * contenir les dumps dans l’arborescence attendue. +## Sécurité / déploiement + +### Clés hôtes SSH + +Si `GLOBAL_BACKUP_KNOWN_HOSTS_STRICT=yes`, l’empreinte du serveur de backup doit déjà être présente dans le `known_hosts` de la cible. Le bootstrap et les checks ne font plus d’ajout aveugle en mode strict. + +### Répertoires de clone + +Les scripts refusent maintenant les chemins de clone manifestement dangereux comme `/`, `/root`, `/home` ou `/home/` pour éviter un `rm -rf` destructeur dû à une mauvaise configuration. + +### Ubuntu Server + +Le flux de bootstrap est pensé pour des cibles Ubuntu Server avec `apt`, `systemctl` et `sudo -n`. + --- ## Structure des backups attendue @@ -290,13 +306,13 @@ Le script recherche : Copier : ```bash -config/global.env.example +Config/.env.exemple ``` vers : ```bash -config/global.env +Config/global.env ``` Renseigner ensuite : @@ -317,13 +333,13 @@ Deux possibilités. Créer un fichier : ```bash -config/targets/test.env +Config/Targets/test.env ``` à partir de : ```bash -config/targets/test.env.example +Config/Targets/test.env.exemple ``` #### B. Via script @@ -400,7 +416,7 @@ Ces informations doivent être stockées dans la configuration serveur. Le flux recommandé est : -1. créer ou mettre à jour `config/targets/.env` +1. créer ou mettre à jour `Config/Targets/.env` 2. lancer `bootstrap-target-host.sh --target ` 3. lancer ensuite `run-rebuild-bdd.sh --target ...` diff --git a/RebuildBdd/bootstrap-target-host.sh b/RebuildBdd/bootstrap-target-host.sh index 0504b18..e367c43 100755 --- a/RebuildBdd/bootstrap-target-host.sh +++ b/RebuildBdd/bootstrap-target-host.sh @@ -221,6 +221,14 @@ if [[ -n "$TARGET_REPO_SUBDIR" ]]; then fi fi +for critical_dir in "$TARGET_CLONE_DIR" "$TARGET_SCRIPT_DIR" "$TARGET_REPO_DIR"; do + [[ -n "$critical_dir" ]] || fail "répertoire critique vide" + [[ "$critical_dir" != "/" ]] || fail "répertoire critique dangereux refusé : $critical_dir" + [[ "$critical_dir" != "/root" ]] || fail "répertoire critique dangereux refusé : $critical_dir" + [[ "$critical_dir" != "/home" ]] || fail "répertoire critique dangereux refusé : $critical_dir" + [[ ! "$critical_dir" =~ ^/home/[^/]+$ ]] || fail "répertoire critique dangereux refusé : $critical_dir" +done + [[ -n "$TARGET_ENV_NAME_VALUE" ]] || fail "TARGET_ENV_NAME manquante" [[ -n "$TARGET_PGHOST_VALUE" ]] || fail "TARGET_PGHOST/GLOBAL_PGHOST manquant" [[ -n "$TARGET_PGPORT_VALUE" ]] || fail "TARGET_PGPORT/GLOBAL_PGPORT manquant" @@ -258,7 +266,7 @@ SSH_OPTS=( -p "$BOOTSTRAP_PORT" -o IdentitiesOnly=yes -o BatchMode=yes - -o StrictHostKeyChecking=accept-new + -o StrictHostKeyChecking=yes -o ConnectTimeout=8 ) @@ -339,6 +347,7 @@ BACKUP_LOG_DIR=$(shell_quote "$TARGET_BACKUP_LOG_DIR_VALUE") LOCAL_RESTORE_BASE_DIR=$(shell_quote "$TARGET_LOCAL_RESTORE_BASE_DIR_VALUE") REMOTE_ROLES_DIR_NAME=$(shell_quote "$TARGET_REMOTE_ROLES_DIR_NAME_VALUE") SSH_KEY=$(shell_quote "$TARGET_SSH_KEY_VALUE") +BACKUP_KNOWN_HOSTS_STRICT=$(shell_quote "$TARGET_BACKUP_KNOWN_HOSTS_STRICT_VALUE") AUTO_INSTALL_POSTGRES=$(shell_quote "$TARGET_AUTO_INSTALL_POSTGRES_VALUE") AUTO_CREATE_PGUSER=$(shell_quote "$TARGET_AUTO_CREATE_PGUSER_VALUE") @@ -385,6 +394,10 @@ if ! command -v ssh-keyscan >/dev/null 2>&1; then fi if ! ssh-keygen -F $(shell_quote "$TARGET_BACKUP_REMOTE_HOST_VALUE") -f $(shell_quote "$REMOTE_KNOWN_HOSTS") >/dev/null 2>&1; then + if [[ $(shell_quote "$STRICT_OPTION") == yes ]]; then + echo 'hôte backup absent de known_hosts en mode strict ; empreinte à provisionner manuellement' >&2 + exit 1 + fi ssh-keyscan -p $(shell_quote "$TARGET_BACKUP_REMOTE_SSH_PORT_VALUE") -H $(shell_quote "$TARGET_BACKUP_REMOTE_HOST_VALUE") >> $(shell_quote "$REMOTE_KNOWN_HOSTS") 2>/dev/null fi " @@ -488,6 +501,10 @@ REMOTE_REPO_CMD=" set -euo pipefail if [[ ! -d $(shell_quote "${TARGET_CLONE_DIR}/.git") ]]; then + if [[ $(shell_quote "$TARGET_CLONE_DIR") == / || $(shell_quote "$TARGET_CLONE_DIR") == /root || $(shell_quote "$TARGET_CLONE_DIR") == /home || $(shell_quote "$TARGET_CLONE_DIR") =~ ^/home/[^/]+$ ]]; then + echo 'TARGET_CLONE_DIR dangereux refusé' >&2 + exit 1 + fi rm -rf $(shell_quote "$TARGET_CLONE_DIR") git clone --branch $(shell_quote "$TARGET_REPO_BRANCH") --single-branch $(shell_quote "$TARGET_REPO_URL") $(shell_quote "$TARGET_CLONE_DIR") else diff --git a/RebuildBdd/rebuild-bdd-core.sh b/RebuildBdd/rebuild-bdd-core.sh index fc80f98..0293402 100755 --- a/RebuildBdd/rebuild-bdd-core.sh +++ b/RebuildBdd/rebuild-bdd-core.sh @@ -83,6 +83,8 @@ print_stdout() { [[ "$JSON_ONLY" == "yes" ]] || echo "$*" } +LOG_FILE=/dev/stderr + log() { local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*" echo "$msg" >>"$LOG_FILE" @@ -220,6 +222,7 @@ RESTORE_ROLES="$(to_bool_yes_no "$RESTORE_ROLES_RAW")" || { } [[ "$PGPORT" =~ ^[0-9]+$ ]] || fail "PGPORT invalide" +[[ "$ENV_NAME" =~ ^[a-zA-Z0-9_-]+$ ]] || fail "ENV_NAME invalide" [[ "$BACKUP_REMOTE_SSH_PORT" =~ ^[0-9]+$ ]] || fail "BACKUP_REMOTE_SSH_PORT invalide" mkdir -p "$BACKUP_LOG_DIR" || { diff --git a/RebuildBdd/run-rebuild-bdd.sh b/RebuildBdd/run-rebuild-bdd.sh index b22687b..cb8b91f 100755 --- a/RebuildBdd/run-rebuild-bdd.sh +++ b/RebuildBdd/run-rebuild-bdd.sh @@ -195,6 +195,14 @@ if [[ -n "$TARGET_REPO_SUBDIR" ]]; then fi fi +for critical_dir in "$TARGET_CLONE_DIR" "$TARGET_SCRIPT_DIR" "$TARGET_REPO_DIR"; do + [[ -n "$critical_dir" ]] || fail "répertoire critique vide" + [[ "$critical_dir" != "/" ]] || fail "répertoire critique dangereux refusé : $critical_dir" + [[ "$critical_dir" != "/root" ]] || fail "répertoire critique dangereux refusé : $critical_dir" + [[ "$critical_dir" != "/home" ]] || fail "répertoire critique dangereux refusé : $critical_dir" + [[ ! "$critical_dir" =~ ^/home/[^/]+$ ]] || fail "répertoire critique dangereux refusé : $critical_dir" +done + TARGET_ENABLE_BOOTSTRAP="$(to_bool_yes_no "$TARGET_ENABLE_BOOTSTRAP")" || fail "TARGET_ENABLE_BOOTSTRAP invalide" BOOTSTRAP_SCRIPT_LOCAL="${SCRIPT_DIR}/bootstrap-target-host.sh" @@ -261,7 +269,7 @@ SSH_OPTS=( -p "$TARGET_PORT" -o IdentitiesOnly=yes -o BatchMode=yes - -o StrictHostKeyChecking=accept-new + -o StrictHostKeyChecking=yes -o ConnectTimeout=8 ) @@ -294,6 +302,10 @@ mkdir -p \"\$(dirname \"\$CLONE_DIR\")\" mkdir -p \"\$(dirname \"\$REPO_DIR\")\" if [[ ! -d \"\$CLONE_DIR/.git\" ]]; then + if [[ \"\$CLONE_DIR\" == / || \"\$CLONE_DIR\" == /root || \"\$CLONE_DIR\" == /home || \"\$CLONE_DIR\" =~ ^/home/[^/]+$ ]]; then + echo '{\"status\":\"error\",\"message\":\"TARGET_CLONE_DIR dangereux refusé\"}' + exit 1 + fi rm -rf \"\$CLONE_DIR\" git clone --branch \"\$REPO_BRANCH\" --single-branch \"\$REPO_URL\" \"\$CLONE_DIR\" >/dev/null 2>&1 else diff --git a/RecetteScripts/README.md b/RecetteScripts/README.md index a211530..ca78ea9 100644 --- a/RecetteScripts/README.md +++ b/RecetteScripts/README.md @@ -47,14 +47,13 @@ Les scripts fonctionnent indépendamment mais utilisent le même principe : Environnement Linux recommandé. -Packages nécessaires : +Packages nécessaires sur Ubuntu Server : ``` postgresql-client curl jq -ssh -scp +openssh-client ``` Commandes PostgreSQL requises : @@ -116,11 +115,18 @@ ssh-copy-id -i ~/.ssh/id_backup_postgres.pub backup@192.168.1.50 Tester la connexion sans mot de passe : ```bash -ssh -i ~/.ssh/id_backup_postgres backup@192.168.1.50 +ssh -i ~/.ssh/id_backup_postgres -o StrictHostKeyChecking=yes backup@192.168.1.50 ``` La connexion doit fonctionner **sans demander de mot de passe**. +Provisionner aussi `known_hosts` avant le premier run : + +```bash +ssh-keyscan -H 192.168.1.50 >> ~/.ssh/known_hosts +chmod 600 ~/.ssh/known_hosts +``` + --- ### Sécuriser les permissions @@ -153,13 +159,18 @@ cp backup.env.exemple .env Puis modifier les variables. +Variables SSH supplémentaires désormais supportées : + +``` +BACKUP_REMOTE_SSH_PORT +BACKUP_KNOWN_HOSTS_STRICT +BACKUP_KNOWN_HOSTS_FILE +``` + --- # 5. Script : backup-bdd-recette.sh -Script : - - ## Objectif Sauvegarder plusieurs bases PostgreSQL et transférer les dumps vers un serveur distant. @@ -205,6 +216,8 @@ Suppression des sauvegardes plus anciennes que : 10 jours ``` +Le script utilise maintenant des options SSH strictes et refuse les clés privées symboliques. + --- ## Exécution @@ -217,9 +230,6 @@ Suppression des sauvegardes plus anciennes que : # 6. Script : check-statut-recette.sh -Script : - - ## Objectif Vérifier la disponibilité des applications web. @@ -306,6 +316,8 @@ Le script : 9. restaure la base via `pg_restore` 10. envoie une notification Discord +Le script supporte désormais le port SSH distant, un fichier `known_hosts` dédié et l’exclusion configurable des rôles via `EXCLUDED_RESTORE_ROLES`. + --- ## Sélection de la base diff --git a/RecetteScripts/backup-bdd-recette.sh b/RecetteScripts/backup-bdd-recette.sh index 2edd349..94cfc98 100755 --- a/RecetteScripts/backup-bdd-recette.sh +++ b/RecetteScripts/backup-bdd-recette.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash set -euo pipefail +umask 077 ############################################################################### # backup-bdd-recette.sh @@ -67,15 +68,82 @@ set +a read -r -a DBS_ARRAY <<< "$DBS" +validate_db_name() { + local db_name="$1" + + [[ -n "$db_name" ]] || { + echo "ERROR: nom de base vide dans DBS" >&2 + exit 1 + } + + [[ "$db_name" =~ ^[a-zA-Z0-9_]+$ ]] || { + echo "ERROR: nom de base invalide dans DBS : $db_name" >&2 + exit 1 + } +} + +for DB in "${DBS_ARRAY[@]}"; do + validate_db_name "$DB" +done + IA_SSH="${BACKUP_REMOTE_USER}@${BACKUP_REMOTE_HOST}" -IA_BASE_DIR="${BACKUP_REMOTE_DIR}" 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}" + +[[ "$BACKUP_REMOTE_SSH_PORT" =~ ^[0-9]+$ ]] || { + echo "ERROR: BACKUP_REMOTE_SSH_PORT invalide" >&2 + exit 1 +} + +[[ "$PGPORT" =~ ^[0-9]+$ ]] || { + echo "ERROR: PGPORT invalide" >&2 + exit 1 +} + +[[ "$SSH_TIMEOUT" =~ ^[0-9]+$ ]] || { + echo "ERROR: SSH_TIMEOUT invalide" >&2 + exit 1 +} + +[[ "$PGUSER" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_-]*$ ]] || { + echo "ERROR: PGUSER invalide" >&2 + exit 1 +} + +case "${BACKUP_KNOWN_HOSTS_STRICT,,}" in + yes|y|oui|o|true|1) BACKUP_KNOWN_HOSTS_STRICT="yes" ;; + no|n|non|false|0) BACKUP_KNOWN_HOSTS_STRICT="no" ;; + *) + echo "ERROR: BACKUP_KNOWN_HOSTS_STRICT invalide" >&2 + exit 1 + ;; +esac + +mkdir -p "$(dirname "$BACKUP_KNOWN_HOSTS_FILE")" +chmod 700 "$(dirname "$BACKUP_KNOWN_HOSTS_FILE")" || true +touch "$BACKUP_KNOWN_HOSTS_FILE" +chmod 600 "$BACKUP_KNOWN_HOSTS_FILE" || true SSH_OPTS=( -i "$SSH_KEY" + -p "$BACKUP_REMOTE_SSH_PORT" -o IdentitiesOnly=yes -o BatchMode=yes -o ConnectTimeout="${SSH_TIMEOUT}" + -o StrictHostKeyChecking="${BACKUP_KNOWN_HOSTS_STRICT}" + -o UserKnownHostsFile="${BACKUP_KNOWN_HOSTS_FILE}" +) + +SCP_OPTS=( + -i "$SSH_KEY" + -P "$BACKUP_REMOTE_SSH_PORT" + -o IdentitiesOnly=yes + -o BatchMode=yes + -o ConnectTimeout="${SSH_TIMEOUT}" + -o StrictHostKeyChecking="${BACKUP_KNOWN_HOSTS_STRICT}" + -o UserKnownHostsFile="${BACKUP_KNOWN_HOSTS_FILE}" ) LOG_DIR="${BACKUP_LOG_DIR}" @@ -85,8 +153,10 @@ TS="$(date +'%Y-%m-%d_%H-%M-%S')" BACKUP_DIR_NAME="backup_${TS}" LOG_FILE="${LOG_DIR}/${BACKUP_DIR_NAME}.log" -TMP_DIR="/tmp/pg_dump_${BACKUP_DIR_NAME}" -mkdir -p "$TMP_DIR" +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 @@ -96,19 +166,46 @@ require_cmd() { command -v "$1" >/dev/null 2>&1 } +safe_remove_dir() { + local dir="${1:-}" + [[ -n "$dir" ]] || return 0 + [[ "$dir" == /tmp/pg_dump_* ]] || { + log "WARNING: suppression refusée pour le chemin inattendu : $dir" + return 1 + } + rm -rf -- "$dir" +} + export PGPASSWORD ####################################### # Vérification dépendances minimales ####################################### -for cmd in ssh scp curl jq pg_dump pg_dumpall; do +for cmd in ssh scp curl jq pg_dump pg_dumpall mktemp; do require_cmd "$cmd" || { echo "ERROR: commande manquante : $cmd" >&2 exit 1 } done +[[ -f "$SSH_KEY" ]] || { + echo "ERROR: clé SSH introuvable : $SSH_KEY" >&2 + exit 1 +} + +[[ -r "$SSH_KEY" ]] || { + echo "ERROR: clé SSH non lisible : $SSH_KEY" >&2 + exit 1 +} + +[[ ! -L "$SSH_KEY" ]] || { + echo "ERROR: la clé SSH ne doit pas être un lien symbolique : $SSH_KEY" >&2 + exit 1 +} + +chmod 600 "$SSH_KEY" || true + ####################################### # Configuration Discord ####################################### @@ -271,19 +368,21 @@ if ! mkdir "$LOCK_DIR" 2>/dev/null; then exit 1 fi -trap 'rm -rf "$LOCK_DIR" "$TMP_DIR"' EXIT +cleanup() { + rm -rf -- "$LOCK_DIR" + safe_remove_dir "$TMP_DIR" || true +} +trap cleanup EXIT ####################################### # Préparation du dossier distant ####################################### -REMOTE_DIR="${IA_BASE_DIR}" - log "Creating remote directories" -MKDIR_CMD="mkdir -p '${REMOTE_DIR}/user'" +MKDIR_CMD="mkdir -p '${BACKUP_REMOTE_DIR}/user'" for DB in "${DBS_ARRAY[@]}"; do - MKDIR_CMD+=" '${REMOTE_DIR}/${DB}'" + MKDIR_CMD+=" '${BACKUP_REMOTE_DIR}/${DB}'" done if ! ssh "${SSH_OPTS[@]}" "$IA_SSH" "$MKDIR_CMD"; then @@ -320,7 +419,7 @@ else fi if [[ -n "${USERS_EXPORT_OK:-}" ]]; then - scp "${SSH_OPTS[@]}" "$ROLES_FILE" "$IA_SSH:${REMOTE_DIR}/user/" + scp "${SCP_OPTS[@]}" "$ROLES_FILE" "$IA_SSH:${BACKUP_REMOTE_DIR}/user/" RET=$? if [[ $RET -ne 0 ]]; then @@ -364,7 +463,7 @@ for DB in "${DBS_ARRAY[@]}"; do continue fi - scp "${SSH_OPTS[@]}" "$FILE" "$IA_SSH:${REMOTE_DIR}/${DB}/" + scp "${SCP_OPTS[@]}" "$FILE" "$IA_SSH:${BACKUP_REMOTE_DIR}/${DB}/" RET=$? if [[ $RET -ne 0 ]]; then @@ -384,7 +483,7 @@ log "Starting remote rotation: delete backups older than ${RETENTION_DAYS} days" set +e -ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${REMOTE_DIR}/user' -type f -name 'user_*.sql' -mtime +${RETENTION_DAYS} -delete" +ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${BACKUP_REMOTE_DIR}/user' -type f -name 'user_*.sql' -mtime +${RETENTION_DAYS} -delete" RET=$? if [[ $RET -ne 0 ]]; then @@ -394,7 +493,7 @@ else fi for DB in "${DBS_ARRAY[@]}"; do - ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${REMOTE_DIR}/${DB}' -type f -name '${DB}_*.dump' -mtime +${RETENTION_DAYS} -delete" + ssh "${SSH_OPTS[@]}" "$IA_SSH" "find '${BACKUP_REMOTE_DIR}/${DB}' -type f -name '${DB}_*.dump' -mtime +${RETENTION_DAYS} -delete" RET=$? if [[ $RET -ne 0 ]]; then @@ -412,7 +511,7 @@ log "Remote rotation finished" # Nettoyage local ####################################### -rm -rf "$TMP_DIR" +safe_remove_dir "$TMP_DIR" || true ####################################### # Bilan final Discord @@ -442,4 +541,4 @@ for DB in "${DBS_ARRAY[@]}"; do fi done -exit 2 \ No newline at end of file +exit 2 diff --git a/RecetteScripts/backup.env.exemple b/RecetteScripts/backup.env.exemple index 4d1882b..fbfdabb 100644 --- a/RecetteScripts/backup.env.exemple +++ b/RecetteScripts/backup.env.exemple @@ -44,9 +44,18 @@ BACKUP_REMOTE_DIR=/home/.../backups/bdd-recette # Clé SSH utilisée pour se connecter au serveur distant SSH_KEY=/home/.../.ssh/id_ed25519_backup +# Port SSH du serveur de backup +BACKUP_REMOTE_SSH_PORT=22 + # Timeout de connexion SSH (secondes) SSH_TIMEOUT=10 +# Validation stricte des clés hôtes SSH (yes/no) +BACKUP_KNOWN_HOSTS_STRICT=yes + +# Fichier known_hosts utilisé par ssh/scp +BACKUP_KNOWN_HOSTS_FILE=/home/.../.ssh/known_hosts + ############################################################################### # LOGS ############################################################################### @@ -62,4 +71,4 @@ BACKUP_LOG_DIR=/var/log/script/ DISCORD_WEBHOOK_URL= # Mention envoyée en cas d'erreur -DISCORD_PING=@here \ No newline at end of file +DISCORD_PING=@here diff --git a/RecetteScripts/check-statut-recette.sh b/RecetteScripts/check-statut-recette.sh index 0f748de..6791028 100755 --- a/RecetteScripts/check-statut-recette.sh +++ b/RecetteScripts/check-statut-recette.sh @@ -201,8 +201,6 @@ check_site() { ####################################### main() { - trap '[[ -n "$STDERR_TMP" ]] && rm -f "$STDERR_TMP"' EXIT - local failures=0 for site in "${SITES[@]}"; do @@ -221,4 +219,4 @@ main() { exit 0 } -main "$@" \ No newline at end of file +main "$@" diff --git a/RecetteScripts/rebuild-bdd-recette.sh b/RecetteScripts/rebuild-bdd-recette.sh index 37c3fff..03af19c 100644 --- a/RecetteScripts/rebuild-bdd-recette.sh +++ b/RecetteScripts/rebuild-bdd-recette.sh @@ -65,8 +65,10 @@ set +a ############################################################################### LOCAL_RESTORE_DIR="${LOCAL_RESTORE_DIR:-${SCRIPT_DIR}/restore_tmp}" REMOTE_ROLES_DIR_NAME="${REMOTE_ROLES_DIR_NAME:-user}" +BACKUP_REMOTE_SSH_PORT="${BACKUP_REMOTE_SSH_PORT:-22}" SSH_CONNECT_TIMEOUT="${SSH_CONNECT_TIMEOUT:-8}" DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-}" +EXCLUDED_RESTORE_ROLES="${EXCLUDED_RESTORE_ROLES:-postgres}" ############################################################################### # Préparation des dossiers locaux @@ -115,6 +117,35 @@ require_cmd() { command -v "$1" >/dev/null 2>&1 } +sql_escape_literal() { + local s="${1:-}" + s="${s//\'/\'\'}" + printf "%s" "$s" +} + +validate_db_name() { + 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" +} + +build_excluded_roles_regex() { + local role regex="" + + for role in $EXCLUDED_RESTORE_ROLES; do + [[ -z "$role" ]] && continue + [[ "$role" =~ ^[a-zA-Z_][a-zA-Z0-9_-]*$ ]] || fail "rôle exclu invalide : ${role}" + if [[ -n "$regex" ]]; then + regex+="|" + fi + regex+="$role" + done + + printf '%s' "$regex" +} + ############################################################################### # Envoi Discord # @@ -151,15 +182,28 @@ send_discord_message() { ############################################################################### [[ -f "$SSH_KEY" ]] || fail "clé SSH introuvable : $SSH_KEY" [[ -r "$SSH_KEY" ]] || fail "clé SSH non lisible : $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" export PGPASSWORD SSH_OPTS=( -i "$SSH_KEY" + -p "$BACKUP_REMOTE_SSH_PORT" -o IdentitiesOnly=yes -o BatchMode=yes -o ConnectTimeout="$SSH_CONNECT_TIMEOUT" - -o StrictHostKeyChecking=accept-new + -o StrictHostKeyChecking=yes +) + +SCP_OPTS=( + -i "$SSH_KEY" + -P "$BACKUP_REMOTE_SSH_PORT" + -o IdentitiesOnly=yes + -o BatchMode=yes + -o ConnectTimeout="$SSH_CONNECT_TIMEOUT" + -o StrictHostKeyChecking=yes ) REMOTE_SSH="${BACKUP_REMOTE_USER}@${BACKUP_REMOTE_HOST}" @@ -217,7 +261,7 @@ if [[ "$POSTGRES_INSTALLED" == "true" ]]; then log "Création du rôle PostgreSQL ${PGUSER} suite à une installation neuve..." sudo -u postgres psql -d postgres -c \ - "CREATE ROLE \"${PGUSER}\" WITH LOGIN SUPERUSER CREATEDB CREATEROLE PASSWORD '${PGPASSWORD}';" \ + "CREATE ROLE \"${PGUSER}\" WITH LOGIN SUPERUSER CREATEDB CREATEROLE PASSWORD '$(sql_escape_literal "$PGPASSWORD")';" \ >>"$LOG_FILE" 2>&1 || fail "échec de création du rôle ${PGUSER}" log "Rôle PostgreSQL ${PGUSER} créé." @@ -251,9 +295,10 @@ if [[ "${USE_LIST,,}" == "oui" || "${USE_LIST,,}" == "o" ]]; then DB="${DBS_ARRAY[$((DB_INDEX - 1))]}" else read -r -p "Nom exact de la base à restaurer : " DB - [[ -n "$DB" ]] || fail "nom de base vide" fi +validate_db_name "$DB" + log "Environnement : $ENV_NAME" log "Base cible sélectionnée : $DB" @@ -312,7 +357,7 @@ LOCAL_DB_DUMP_FILE="${LOCAL_RESTORE_DIR}/$(basename "$LAST_REMOTE_DB_DUMP")" LOCAL_ROLES_FILE="" log "Téléchargement du dump..." -scp "${SSH_OPTS[@]}" "${REMOTE_SSH}:${LAST_REMOTE_DB_DUMP}" "$LOCAL_DB_DUMP_FILE" \ +scp "${SCP_OPTS[@]}" "${REMOTE_SSH}:${LAST_REMOTE_DB_DUMP}" "$LOCAL_DB_DUMP_FILE" \ >>"$LOG_FILE" 2>&1 || fail "échec du téléchargement du dump principal" ############################################################################### @@ -322,7 +367,7 @@ if [[ -n "$LAST_REMOTE_ROLES_FILE" ]]; then LOCAL_ROLES_FILE="${LOCAL_RESTORE_DIR}/$(basename "$LAST_REMOTE_ROLES_FILE")" log "Téléchargement du fichier des rôles..." - scp "${SSH_OPTS[@]}" "${REMOTE_SSH}:${LAST_REMOTE_ROLES_FILE}" "$LOCAL_ROLES_FILE" \ + scp "${SCP_OPTS[@]}" "${REMOTE_SSH}:${LAST_REMOTE_ROLES_FILE}" "$LOCAL_ROLES_FILE" \ >>"$LOG_FILE" 2>&1 || fail "échec du téléchargement du fichier des rôles" else log "La restauration des rôles sera ignorée." @@ -341,7 +386,7 @@ fi ############################################################################### DB_EXISTS="$( psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d postgres -tAc \ - "SELECT 1 FROM pg_database WHERE datname='${DB}'" 2>>"$LOG_FILE" || true + "SELECT 1 FROM pg_database WHERE datname='$(sql_escape_literal "$DB")'" 2>>"$LOG_FILE" || true )" if [[ "$DB_EXISTS" == "1" ]]; then @@ -364,9 +409,14 @@ if [[ -n "$LOCAL_ROLES_FILE" ]]; then FILTERED_ROLES_FILE="${LOCAL_RESTORE_DIR}/filtered_$(basename "$LOCAL_ROLES_FILE")" ROLES_CREATE_LIST="${LOCAL_RESTORE_DIR}/roles_to_create_$(basename "$LOCAL_ROLES_FILE")" ROLES_APPLY_FILE="${LOCAL_RESTORE_DIR}/roles_apply_$(basename "$LOCAL_ROLES_FILE")" + EXCLUDED_ROLES_REGEX="$(build_excluded_roles_regex)" - grep -viE '^(CREATE ROLE|ALTER ROLE) (backup_liot|postgres)\b' "$LOCAL_ROLES_FILE" \ - > "$FILTERED_ROLES_FILE" || true + if [[ -n "$EXCLUDED_ROLES_REGEX" ]]; then + grep -viE "^(CREATE ROLE|ALTER ROLE) (${EXCLUDED_ROLES_REGEX})\\b" "$LOCAL_ROLES_FILE" \ + > "$FILTERED_ROLES_FILE" || true + else + cp "$LOCAL_ROLES_FILE" "$FILTERED_ROLES_FILE" + fi log "Fichier des rôles filtré généré : ${FILTERED_ROLES_FILE}" @@ -383,7 +433,7 @@ if [[ -n "$LOCAL_ROLES_FILE" ]]; then ROLE_EXISTS="$( psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d postgres -tAc \ - "SELECT 1 FROM pg_roles WHERE rolname='${role_name}'" 2>>"$LOG_FILE" || true + "SELECT 1 FROM pg_roles WHERE rolname='$(sql_escape_literal "$role_name")'" 2>>"$LOG_FILE" || true )" if [[ "$ROLE_EXISTS" != "1" ]]; then @@ -448,4 +498,4 @@ Hôte PostgreSQL : ${PGHOST}:${PGPORT} Dump utilisé : $(basename "$LAST_REMOTE_DB_DUMP") Log : ${LOG_FILE}" -send_discord_message "$SUCCESS_MESSAGE" \ No newline at end of file +send_discord_message "$SUCCESS_MESSAGE" diff --git a/RecetteScripts/rebuild.env.exemple b/RecetteScripts/rebuild.env.exemple index 5865dd7..2b4620f 100644 --- a/RecetteScripts/rebuild.env.exemple +++ b/RecetteScripts/rebuild.env.exemple @@ -39,6 +39,9 @@ BACKUP_REMOTE_HOST= # Répertoire racine distant : BACKUP_REMOTE_DIR=/home/.../backups/bdd-recette +# Port SSH du serveur de backup +BACKUP_REMOTE_SSH_PORT=22 + ############################################################################### # SSH ############################################################################### @@ -50,6 +53,12 @@ SSH_KEY=/home/.../.ssh/id_ed25519_backup # Variable optionnelle dans le script, mais utile ici comme valeur par défaut SSH_CONNECT_TIMEOUT=8 +# Validation stricte des clés hôtes SSH (yes/no) +BACKUP_KNOWN_HOSTS_STRICT=yes + +# Fichier known_hosts utilisé par ssh/scp +BACKUP_KNOWN_HOSTS_FILE=/home/.../.ssh/known_hosts + ############################################################################### # LOGS ############################################################################### @@ -72,9 +81,12 @@ LOCAL_RESTORE_DIR=/tmp/rebuild-bdd-recette # Nom du dossier distant contenant les exports SQL des rôles REMOTE_ROLES_DIR_NAME=user +# Rôles PostgreSQL à exclure lors de la restauration +EXCLUDED_RESTORE_ROLES="postgres" + ############################################################################### # DISCORD ############################################################################### # Webhook Discord pour notifier le succès de la restauration -DISCORD_WEBHOOK_URL= \ No newline at end of file +DISCORD_WEBHOOK_URL= diff --git a/global.env.exemple b/global.env.exemple index fb3cd10..1088e64 100644 --- a/global.env.exemple +++ b/global.env.exemple @@ -78,7 +78,7 @@ BACKUP_REMOTE_DIR=/home/.../backups/bdd-recette ############################################# # Clé SSH utilisée pour se connecter au serveur distant -SSH_KEY=/home/.../.ssh/id_ed25519_backup +SSH_KEY=/home//.ssh/id_ed25519_backup # Timeout SSH (secondes) SSH_TIMEOUT=10