30 Commits

Author SHA1 Message Date
df77d8be21 Merge pull request 'fix/427-correctifs' (#12) from fix/427-correctifs into develop
Reviewed-on: #12
2026-03-13 08:01:30 +00:00
038ddfe242 Actualiser BackupVaultWarden/README.md 2026-03-13 07:59:18 +00:00
AkiNoKure
a1ace94094 fix : correctifs 2026-03-13 08:55:11 +01:00
210594b008 Merge pull request 'feat/392-script-reconstruction-bdd' (#11) from feat/392-script-reconstruction-bdd into develop
Reviewed-on: #11
2026-03-12 08:53:22 +00:00
AkiNoKure
e221e82108 feat : script de reconstruction de bdd 2026-03-12 09:49:35 +01:00
AkiNoKure
fabc9be4d4 fix : pb chemin env 2026-03-11 17:11:30 +01:00
AkiNoKure
9d4a5050e9 feat : rebuild-bdd-recette 2026-03-11 11:14:44 +01:00
5729d0d484 Merge pull request 'fix/code-review' (#10) from fix/code-review into develop
Reviewed-on: #10
2026-03-10 15:17:56 +00:00
AkiNoKure
89b1229efb fix : code review 2026-03-10 16:17:11 +01:00
AkiNoKure
f9b1d1da24 Merge remote-tracking branch 'origin/fix/code-review' into fix/code-review
# Conflicts:
#	CODE_REVIEW.md
2026-03-10 16:16:51 +01:00
AkiNoKure
049574ffeb fix : code review 2026-03-10 16:15:21 +01:00
c257270982 Actualiser CODE_REVIEW.md 2026-03-10 15:14:25 +00:00
AkiNoKure
f72328e0ce fix : code review 2026-03-10 15:54:22 +01:00
AkiNoKure
29eff11b23 Merge remote-tracking branch 'refs/remotes/origin/fix/code-review' into fix/code-review 2026-03-10 09:09:18 +01:00
Matthieu
99072361c5 docs: ajout de la revue de code complète du dépôt
Revue couvrant les 4 scripts (backup-vaultwarden, check-storage,
backup-bdd-recette, check-statut-recette) avec identification des
problèmes de sécurité, qualité et suggestions d'amélioration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 17:20:13 +01:00
623424343e Merge pull request 'feat : ajout de la rotation pour les scripts de backup' (#9) from feat/384-correctif into develop
Reviewed-on: #9
2026-03-09 15:51:09 +00:00
AkiNoKure
066ede6000 feat : ajout de la rotation pour les scripts de backup 2026-03-09 16:46:50 +01:00
30df5ca8d6 Merge pull request 'feat/384-correctif' (#8) from feat/384-correctif into develop
Reviewed-on: MALIO-DEV/Scripts-Serveur#8
2026-03-09 12:23:15 +00:00
AkiNoKure
4b76e88853 Merge branch 'develop' into feat/384-correctif 2026-03-09 13:21:32 +01:00
AkiNoKure
99f8694250 fix : corrections orthographique 2026-03-09 13:19:32 +01:00
7f18e2f2e9 Merge pull request 'fix : review' (#7) from feat/384-correctif into develop
Reviewed-on: MALIO-DEV/Scripts-Serveur#7
2026-03-09 10:50:01 +00:00
AkiNoKure
d0ceea8bad fix : review 2026-03-09 11:48:21 +01:00
dd226592db Merge pull request 'feat/384-correctif' (#6) from feat/384-correctif into develop
Reviewed-on: MALIO-DEV/Scripts-Serveur#6
2026-03-09 10:35:47 +00:00
AkiNoKure
e81b953ac2 fix : discord message 2026-03-09 11:31:37 +01:00
AkiNoKure
c80a74adc5 fix : versionning 2026-03-09 11:03:28 +01:00
AkiNoKure
97eeffd9ea fix : correctifs multiple 2026-03-09 10:49:29 +01:00
AkiNoKure
14359b111f Merge branch 'refs/heads/develop' into feat/384-correctif 2026-03-09 10:48:21 +01:00
e860fd0f16 Merge pull request 'fix : correctif du script' (#5) from fix/372-script-storage into develop
Reviewed-on: MALIO-DEV/Scripts-Serveur#5
2026-03-09 09:32:44 +00:00
Lethary
fbbb68af88 fix : correctif du script 2026-03-09 10:30:06 +01:00
AkiNoKure
c2d1b716e0 fix : correctifs multiples 2026-03-09 09:08:44 +01:00
24 changed files with 2395 additions and 522 deletions

View File

@@ -1 +0,0 @@
WEBHOOK_URL=

45
.gitignore vendored
View File

@@ -1,9 +1,40 @@
# Secrets / environment
.env
.env.*
!.env.example
!.env.exemple
########################################
# Environment / secrets
########################################
.env
!.env.exemple
!.env.example
########################################
# Logs
########################################
*.log
logs/
var/log/
backup.log
########################################
# Temporary / system files
########################################
*.tmp
*.swp
*.swo
*~
########################################
# IDE / Editors
########################################
# IDE / editor
.idea/
.vscode/
.vscode/
*.iml
########################################
# OS files
########################################
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,33 @@
#############################################
# VAULTWARDEN BACKUP CONFIGURATION
#############################################
# Répertoire contenant les données Vaultwarden
DATA_DIR=
#############################################
# BACKUP LOCAL
#############################################
# Dossier local où seront stockées les archives
LOCAL_BACKUP=
#############################################
# SERVEUR DE BACKUP DISTANT
#############################################
# Utilisateur SSH du serveur distant
REMOTE_USER=
# Host ou IP du serveur distant
REMOTE_HOST=
# Répertoire distant de stockage des backups
REMOTE_DIR=
#############################################
# AUTHENTIFICATION SSH
#############################################
# Chemin vers la clé privée SSH utilisée pour la connexion
SSH_KEY=

313
BackupVaultWarden/README.md Normal file
View File

@@ -0,0 +1,313 @@
markdown
# README — Mise en place du script de sauvegarde Vaultwarden
Ce script permet dautomatiser la sauvegarde de Vaultwarden afin de conserver une copie du dossier `data`, de la transférer vers un serveur distant et denvoyer une notification Discord en cas de succès ou déchec.
---
# 1. Objectif du script
Le script de sauvegarde Vaultwarden permet de :
- sauvegarder les données de Vaultwarden ;
- compresser larchive avec un nom daté ;
- transférer la sauvegarde vers un serveur distant ;
- envoyer une notification Discord ;
- automatiser lexécution via `cron`.
Ce mécanisme permet de sécuriser les mots de passe, les utilisateurs et la configuration stockés dans le dossier `data` de Vaultwarden.
---
# 2. Pré-requis
Avant de mettre en place le script, vérifier que les éléments suivants sont disponibles sur la machine Vaultwarden :
- `bash`
- `tar`
- `scp`
- `ssh`
- `curl`
- `cron`
Installation sur Debian / Ubuntu :
```bash
sudo apt update
sudo apt install -y tar openssh-client curl cron
````
---
# 3. Emplacement du script
Le script est situé dans :
```bash
/home/matt/vaultwarden/Malio-ops/BackupVaultWarden/
```
Structure recommandée :
```bash
/home/matt/vaultwarden/Malio-ops/BackupVaultWarden/
├── backup-vaultwarden.sh
├── .env
└── README.md
```
---
# 4. Configuration sécurisée avec le fichier .env
Les informations sensibles ne doivent pas être stockées directement dans le script.
Elles doivent être placées dans un fichier `.env`.
## Exemple de fichier `.env`
```bash
WEBHOOK_URL=https://discord.com/api/webhooks/...
REMOTE_USER=backup
REMOTE_HOST=192.168.1.50
SSH_KEY=/home/matt/.ssh/id_ed25519_vaultwarden_backup
DATA_DIR=/opt/vaultwarden/data
REMOTE_DIR=/home/backup/backups/vaultwarden
```
## 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 |
---
# 5. Chargement des variables dans le script
Le script récupère les variables du fichier `.env`.
Exemple :
```bash
REMOTE_USER=$(grep -E '^REMOTE_USER=' .env | cut -d '=' -f2-)
```
Explication :
* `grep` recherche la variable dans `.env`
* `cut` récupère uniquement la valeur après `=`
* la variable shell reçoit la valeur correspondante
Cela permet :
* d'améliorer la sécurité
* de modifier la configuration sans toucher au script
---
### 6. Connexion au serveur de sauvegarde (Machine IA)
Le transfert des sauvegardes utilise une **clé SSH** afin de permettre une connexion automatique au serveur distant sans mot de passe.
#### 1. Génération de la clé
Sur la machine exécutant les scripts :
```bash
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_bitwarden
```
#### 2. Copie de la clé vers le serveur distant
```bash
ssh-copy-id -i ~/.ssh/id_ed25519_bitwarden.pub backup@192.168.0.179
```
Cette commande ajoute la clé dans :
```
~/.ssh/authorized_keys
```
sur la machine IA.
#### 3. Vérification de la connexion
```bash
ssh -i ~/.ssh/id_ed25519_bitwarden backup@192.168.0.179
```
#### 4. Vérification des fichiers de clé
```bash
ls ~/.ssh/id_ed25519_bitwarden*
```
Fichiers attendus :
```
~/.ssh/id_ed25519_bitwarden
~/.ssh/id_ed25519_bitwarden.pub
```
#### 5. Permissions SSH
Machine locale :
```bash
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519_bitwarden
chmod 644 ~/.ssh/id_ed25519_bitwarden.pub
```
Machine distante :
```bash
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
```
#### 6. Déclaration dans `.env`
```bash
SSH_KEY=/home/matt/.ssh/id_ed25519_bitwarden
```
Cette clé sera utilisée automatiquement par les scripts (`scp` / `ssh`) pour transférer les sauvegardes.
# 7. Sauvegarde des données Vaultwarden
Le script crée une archive compressée du dossier `data` :
```bash
tar -czf "$LOCAL_BACKUP" -C "$(dirname "$DATA_DIR")" "$(basename "$DATA_DIR")"
```
Cela permet dobtenir une sauvegarde portable et compressée.
---
# 8. Transfert vers le serveur distant
Une fois larchive créée :
```bash
scp "${SSH_OPTS[@]}" "$LOCAL_BACKUP" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/"
```
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.
Construction du message :
```bash
local msg="**@here Backup Vaultwarden $color**\n"
msg+="Backup: ${BACKUP_NAME}\n"
msg+="Data transfer: $dumps_display\n"
[[ -n "$details" ]] && msg+="Details: $details"
```
Envoi du message :
```bash
curl -fsS -H "Content-Type: application/json" \
-d "{\"content\":\"$msg\"}" \
"$WEBHOOK_URL"
```
Le message indique :
* si la sauvegarde a réussi
* si elle a échoué
* le nom du backup
* les détails de lerreur
---
# 10. Planification avec cron
Le script est exécuté automatiquement tous les jours à 19h.
Ouvrir le crontab :
```bash
crontab -e
```
Ajouter :
```bash
0 19 * * * /home/matt/vaultwarden/Malio-ops/BackupVaultWarden/backup-vaultwarden.sh >> /var/log/vaultwarden_backup.log 2>&1
```
Signification :
| Champ | Valeur |
| ------------ | ------ |
| minute | 0 |
| heure | 19 |
| jour du mois | * |
| mois | * |
| jour semaine | * |
Le script sexécute donc **tous les jours à 19h00**.
---
# 11. Nettoyage
Une fois la sauvegarde transférée :
```bash
rm -f "$LOCAL_BACKUP"
```
Cela évite de remplir le disque de la machine Vaultwarden.
---
# 12. Test manuel
Avant de mettre le script en cron, tester :
```bash
bash /home/matt/vaultwarden/Malio-ops/BackupVaultWarden/backup-vaultwarden.sh
```
---
# 13. Vérification des logs
Logs :
```bash
cat /var/log/vaultwarden_backup.log
```
---
# 14. Résumé
Le script automatise :
* la sauvegarde du dossier `data`
* la compression et la datation du backup
* le transfert sécurisé via SSH
* la notification Discord
* lexécution automatique via cron
* la configuration sécurisée via `.env`
Ce système permet dobtenir **une sauvegarde fiable, centralisée et surveillée de Vaultwarden**.
```

View File

@@ -5,7 +5,7 @@ set -euo pipefail
# Chemins fixes du script
#######################################
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="/home/matt/vaultwarden/scripts/Scripts-Serveur/backup_vaultwarden/.env"
ENV_FILE="${SCRIPT_DIR}/.env"
LOG_FILE="/var/log/vaultwarden_backup.log"
mkdir -p "$(dirname "$LOG_FILE")"
@@ -45,9 +45,11 @@ set +a
# Variables backup
#######################################
DATE="$(date +'%Y-%m-%d_%H-%M-%S')"
BACKUP_NAME="vaultwarden-backup-${DATE}.tar.gz"
BACKUP_PREFIX="vaultwarden-backup"
BACKUP_NAME="${BACKUP_PREFIX}-${DATE}.tar.gz"
LOCAL_BACKUP_DIR="$LOCAL_BACKUP"
LOCAL_BACKUP_FILE="${LOCAL_BACKUP_DIR}/${BACKUP_NAME}"
RETENTION_DAYS=10
SSH_OPTS=(-i "$SSH_KEY" -o IdentitiesOnly=yes -o BatchMode=yes -o ConnectTimeout=10)
@@ -66,13 +68,15 @@ discord_ping() {
if [[ "$success" == "true" ]]; then
icon="🟢"
status_line="✅"
ping=""
else
icon="🔴"
status_line="❌"
ping="@here "
fi
local msg
msg="**@here ${icon} Backup Vaultwarden**\n"
msg="**${ping}Backup Vaultwarden ${icon}**\n"
msg+="Backup: ${BACKUP_NAME}\n"
msg+="Data transfer: ${status_line}\n"
[[ -n "$details" ]] && msg+="Détails: ${details}"
@@ -110,6 +114,8 @@ log "Destination distante : ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}"
tar -czf "$LOCAL_BACKUP_FILE" -C "$(dirname "$DATA_DIR")" "$(basename "$DATA_DIR")" \
|| fail "Erreur lors de la compression du dossier $DATA_DIR"
log "Backup local créé : $LOCAL_BACKUP_FILE"
#######################################
# Création dossier distant
#######################################
@@ -122,6 +128,17 @@ ssh "${SSH_OPTS[@]}" "$REMOTE_USER@$REMOTE_HOST" "mkdir -p '$REMOTE_DIR'" \
scp "${SSH_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"
#######################################
# Rotation distante - suppression > 10 jours
#######################################
ssh "${SSH_OPTS[@]}" "$REMOTE_USER@$REMOTE_HOST" \
"find '$REMOTE_DIR' -type f -name '${BACKUP_PREFIX}-*.tar.gz' -mtime +$RETENTION_DAYS -delete" \
|| fail "Erreur lors de la rotation distante des sauvegardes"
log "Rotation distante OK"
#######################################
# Nettoyage local
#######################################
@@ -132,4 +149,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"
echo "Backup $BACKUP_NAME terminé et envoyé sur $REMOTE_HOST:$REMOTE_DIR"

40
CHANGELOG.md Normal file
View File

@@ -0,0 +1,40 @@
# Changelog
Liste des évolutions du projet Scripts Serveur
## [0.0.1]
### 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
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
### 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
### Changed
### Fixed

View File

@@ -0,0 +1,6 @@
#############################################
# DISCORD
#############################################
# Webhook Discord pour notifications
WEBHOOK_URL=

View File

@@ -18,15 +18,37 @@ La limite d'alerte est fixée à 70% d'utilisation, mais vous pouvez ajuster cet
3. ```bash
cd Scripts-Serveur/CheckStorage
```
### Génération de la clé SSH
Sur la machine exécutant les scripts :
```bash
ssh-keygen -t ed25519 -f ~/.ssh/check_storage_key
```
Copier la clé sur le serveur distant :
```bash
ssh-copy-id -i ~/.ssh/check_storage_key.pub user@serveur
```
Tester la connexion sans mot de passe :
```bash
ssh -i ~/.ssh/id_backup_postgres backup@192.168.1.50
```
## 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
chmod +x check-storage.sh
```
2. Exécutez le script pour vérifier l'espace de stockage :
```bash
./check_storage.sh
./check-storage.sh
```
## Initialisé un cron pour exécuter le script régulièrement
@@ -36,8 +58,8 @@ La limite d'alerte est fixée à 70% d'utilisation, mais vous pouvez ajuster cet
```
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
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.
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.

View File

@@ -0,0 +1,69 @@
#!/bin/bash
set -euo pipefail
###############################################################################
# CHARGEMENT DU .env
###############################################################################
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/.env"
if [[ ! -f "$ENV_FILE" ]]; then
echo "ERROR: fichier .env introuvable : $ENV_FILE" >&2
exit 1
fi
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
###############################################################################
# CONFIGURATION
###############################################################################
# Limite maximale d'utilisation du disque en pourcentage
limit=70
###############################################################################
# RÉCUPÉRATION DES INFORMATIONS DISQUE
###############################################################################
read -r total_bytes used_bytes avail_bytes usage <<<"$(df -B1 / | awk 'NR==2 {gsub(/%/,"",$5); print $2, $3, $4, $5}')"
# Calcul du pourcentage d'espace libre
free=$((100 - usage))
###############################################################################
# CONVERSION EN GIGAOCTETS
###############################################################################
used_gb=$(awk -v b="$used_bytes" 'BEGIN {printf "%.2f", b/1024/1024/1024}')
total_gb=$(awk -v b="$total_bytes" 'BEGIN {printf "%.2f", b/1024/1024/1024}')
avail_gb=$(awk -v b="$avail_bytes" 'BEGIN {printf "%.2f", b/1024/1024/1024}')
###############################################################################
# VÉRIFICATION DU SEUIL D'UTILISATION
###############################################################################
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)"
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" \
"$WEBHOOK_URL"
fi
###############################################################################
# AFFICHAGE DES INFORMATIONS STOCKAGE
###############################################################################
echo "Espace disponible : ${avail_gb} GB"
echo "Espace utilise / espace total : ${used_gb} GB / ${total_gb} GB"
echo "Name: ${HOSTNAME}"

View File

@@ -1,22 +0,0 @@
#!/bin/bash
limit=70
# Mettre le lien de votre webhook Discord dans un .env
WEBHOOK_URL=$(grep -E '^WEBHOOK_URL=' .env | cut -d '=' -f2-)
# Récupérer l'utilisation du disque en pourcentage
usage=$(df -h / | awk 'NR==2 {gsub(/%/,"",$5); print $5}')
# Calculer l'espace libre en pourcentage
free=$((100 - usage))
# Si l'utilisation dépasse la limite, envoyer une alerte sur Discord
if [ "$usage" -ge "$limit" ]; then
msgLimit="@here\n**CHECK STOCKAGE :red_circle:**\nLimite autorisé : ${limit}% \nUtilisation actuelle: ${usage}%\nEspace restant: ${free}%\nHeure: $(date)"
curl -X POST \
-H "Accept: application/json" \
-H "Content-Type: application/json; charset=utf-8" \
-d "{\"content\":\"$msgLimit\"}" \
"$WEBHOOK_URL"
# Log de l'alerte
echo "ALERTE >> ${usage}% d'utilisation, check fait le $(date)"
echo "------------------------------------------------------------"
fi

View File

@@ -1,4 +1,40 @@
# Scripts Serveur MALIO
# Malio-Ops MALIO
Ce dépôt sert au **versionnement des scripts utilisés dans linfrastructure du projet Ferme**.
Lobjectif est de conserver un historique clair des scripts, suivre les évolutions et permettre leur amélioration progressive.
## Contenu du dépôt
Le dépôt peut contenir différents types de scripts utilisés pour lexploitation technique :
* scripts de **backup PostgreSQL**
* scripts de **monitoring des applications**
* scripts **dautomatisation système**
* scripts **dadministration ou de maintenance**
## Objectif
Ce dépôt permet de :
* suivre les **modifications des scripts dans le temps**
* garder une **version stable et reproductible**
* faciliter la **maintenance et les corrections**
* centraliser les scripts utilisés sur les serveurs
## Organisation
Les évolutions importantes sont documentées dans le fichier :
```
CHANGELOG.md
```
Ce fichier décrit les nouvelles fonctionnalités, corrections et modifications apportées aux scripts.
## Remarque
Ce dépôt est dédié au **versionnement des scripts uniquement**.
Les configurations sensibles (mots de passe, clés, variables denvironnement) ne doivent pas être stockées directement dans le dépôt et doivent être placées dans des fichiers `.env` locaux.
Ce projet contient des scripts pour la gestion et la maintenance des serveurs de MALIO.

378
RecetteScripts/README.md Normal file
View File

@@ -0,0 +1,378 @@
# RecetteScripts
Scripts Bash permettant dautomatiser la gestion dun environnement **PostgreSQL de recette**.
Ces scripts permettent :
* la **sauvegarde automatisée des bases**
* la **surveillance de la disponibilité des applications**
* la **reconstruction dune base à partir dun dump**
Chaque script possède son propre **fichier `.env` dédié** afin de séparer les configurations.(un global.env.exemple est disponible à la racine du projet)
---
# 0. Arborescence du projet
```
RecetteScripts
├── backup-bdd-recette.sh # script de sauvegarde PostgreSQL
├── backup.env.exemple # exemple de configuration backup
├── check-statut-recette.sh # script de monitoring des applications
├── check-statut.env.exemple # exemple de configuration monitoring
├── rebuild-bdd-recette.sh # script de restauration PostgreSQL
├── rebuild.env.exemple # exemple de configuration restauration
└── README.md
```
---
# 1. Principe général
Les scripts fonctionnent indépendamment mais utilisent le même principe :
1. chargement dun fichier `.env`
2. vérification des variables obligatoires
3. exécution de la tâche principale
4. génération de logs
5. notification Discord (optionnelle)
---
# 2. Prérequis
Environnement Linux recommandé.
Packages nécessaires :
```
postgresql-client
curl
jq
ssh
scp
```
Commandes PostgreSQL requises :
```
pg_dump
pg_dumpall
pg_restore
psql
createdb
dropdb
```
---
### 3 Connexion SSH
Une connexion SSH avec **clé privée** est nécessaire afin de permettre les transferts automatisés de fichiers vers le serveur distant (dump PostgreSQL, rôles, etc.).
### Génération de la clé SSH
Sur la machine exécutant les scripts :
```bash
ssh-keygen -t ed25519 -f ~/.ssh/id_backup_postgres
```
Explication :
* `-t ed25519` : algorithme recommandé
* `-f` : chemin de la clé
Deux fichiers seront créés :
```
~/.ssh/id_backup_postgres
~/.ssh/id_backup_postgres.pub
```
---
### Copier la clé sur le serveur distant
Méthode recommandée :
```bash
ssh-copy-id -i ~/.ssh/id_backup_postgres.pub user@serveur
```
Exemple :
```bash
ssh-copy-id -i ~/.ssh/id_backup_postgres.pub backup@192.168.1.50
```
---
### Vérifier la connexion
Tester la connexion sans mot de passe :
```bash
ssh -i ~/.ssh/id_backup_postgres backup@192.168.1.50
```
La connexion doit fonctionner **sans demander de mot de passe**.
---
### Sécuriser les permissions
Les permissions doivent être restreintes :
```bash
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_backup_postgres
chmod 644 ~/.ssh/id_backup_postgres.pub
```
---
# 4. Configuration
Chaque script possède un **fichier dexemple** :
```
backup.env.exemple
check-statut.env.exemple
rebuild.env.exemple
```
Pour utiliser les scripts :
```
cp backup.env.exemple .env
```
Puis modifier les variables.
---
# 5. Script : backup-bdd-recette.sh
Script :
## Objectif
Sauvegarder plusieurs bases PostgreSQL et transférer les dumps vers un serveur distant.
---
## Fonctionnement
Le script :
1. charge la configuration `.env`
2. vérifie les dépendances
3. empêche lexécution simultanée (lock)
4. exporte les rôles PostgreSQL
5. crée un dump de chaque base
6. transfère les dumps vers un serveur distant
7. applique une rotation des sauvegardes
8. envoie un résumé sur Discord
---
## Format des fichiers
Dump base :
```
base_TIMESTAMP.dump
```
Export utilisateurs :
```
user_TIMESTAMP.sql
```
---
## Rotation automatique
Suppression des sauvegardes plus anciennes que :
```
10 jours
```
---
## Exécution
```
./backup-bdd-recette.sh
```
---
# 6. Script : check-statut-recette.sh
Script :
## Objectif
Vérifier la disponibilité des applications web.
Ce script agit comme un **mini système de monitoring**.
---
## Vérifications
Pour chaque application :
1. résolution DNS
2. requête HTTP
3. analyse du code HTTP
Codes valides :
```
200 → 399
```
---
## Exemple de configuration
```
APP_URLS="ferme.malio-dev.fr sirh.malio-dev.fr inventory.malio-dev.fr"
```
---
## Logs
Fichier généré :
```
app_health_YYYY-MM-DD.log
```
Format :
```
date | statut | host | détail
```
---
## Exemple de notification Discord
```
CHECK APP RECETTE 🟢
✅ ferme.malio-dev.fr : OK
✅ sirh.malio-dev.fr : OK
✅ inventory.malio-dev.fr : OK
```
---
# 7. Script : rebuild-bdd-recette.sh
Script :
## Objectif
Restaurer une base PostgreSQL à partir dun dump distant.
---
## Fonctionnement
Le script :
1. charge la configuration `.env`
2. installe PostgreSQL si nécessaire
3. démarre le service PostgreSQL
4. demande la base à restaurer
5. récupère le dernier dump sur le serveur distant
6. récupère le dernier export des rôles
7. crée les rôles manquants
8. supprime la base existante si nécessaire
9. restaure la base via `pg_restore`
10. envoie une notification Discord
---
## Sélection de la base
Les bases disponibles sont lues depuis :
```
DBS="sirh inventory ferme"
```
Exemple :
```
1) sirh
2) inventory
3) ferme
```
---
## Commande utilisée pour la restauration
```
pg_restore
--clean
--if-exists
--no-owner
--no-privileges
```
Ces options évitent les conflits entre environnements.
---
# 8. Logs
Les scripts produisent des logs détaillés :
```
backup logs
restore logs
app health logs
```
Ces logs permettent :
* diagnostic des erreurs
* audit des opérations
* suivi des backups
---
# 9. Automatisation recommandée
### Backup et check quotidien
```
0 19 * * * /scripts/backup-bdd-recette.sh
0 19 * * * /scripts/check-statut-recette.sh
```
---
# 10. Bonnes pratiques
Recommandé :
* isoler le **serveur de stockage**
* vérifier régulièrement les restaurations
---

View File

@@ -0,0 +1,440 @@
#!/usr/bin/env bash
set -euo pipefail
###############################################################################
# backup-bdd-recette.sh
#
# Ce script réalise une sauvegarde logique de plusieurs bases PostgreSQL
# définies dans le fichier .env, exporte également la liste des rôles/users,
# puis transfère lensemble vers une machine distante de stockage.
#
# Fonctionnement global :
# 1. charge la configuration depuis le fichier .env ;
# 2. vérifie les dépendances nécessaires ;
# 3. prépare les chemins, logs et variables de connexion ;
# 4. empêche lexécution simultanée grâce à un verrou ;
# 5. crée les dossiers de destination sur la machine distante ;
# 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 ;
# 10. envoie un bilan sur Discord :
# - 1 message global si tout est OK ;
# - en cas derreur partielle :
# * USERS OK -> message simple ;
# * USERS KO -> message détaillé ;
# * DB OK -> message simple ;
# * DB KO -> message détaillé.
###############################################################################
#######################################
# Chargement du .env
#######################################
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/.env"
if [[ ! -f "$ENV_FILE" ]]; then
echo "ERROR: fichier .env introuvable : $ENV_FILE" >&2
exit 1
fi
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
#######################################
# Vérification des variables requises
#######################################
: "${ENV_NAME:?Variable ENV_NAME manquante}"
: "${PGHOST:?Variable PGHOST manquante}"
: "${PGPORT:?Variable PGPORT manquante}"
: "${PGUSER:?Variable PGUSER manquante}"
: "${PGPASSWORD:?Variable PGPASSWORD manquante}"
: "${DBS:?Variable DBS manquante}"
: "${BACKUP_REMOTE_USER:?Variable BACKUP_REMOTE_USER manquante}"
: "${BACKUP_REMOTE_HOST:?Variable BACKUP_REMOTE_HOST manquante}"
: "${BACKUP_REMOTE_DIR:?Variable BACKUP_REMOTE_DIR manquante}"
: "${SSH_KEY:?Variable SSH_KEY manquante}"
: "${SSH_TIMEOUT:?Variable SSH_TIMEOUT manquante}"
: "${BACKUP_LOG_DIR:?Variable BACKUP_LOG_DIR manquante}"
#######################################
# Configuration principale
#######################################
read -r -a DBS_ARRAY <<< "$DBS"
IA_SSH="${BACKUP_REMOTE_USER}@${BACKUP_REMOTE_HOST}"
IA_BASE_DIR="${BACKUP_REMOTE_DIR}"
RETENTION_DAYS=10
SSH_OPTS=(
-i "$SSH_KEY"
-o IdentitiesOnly=yes
-o BatchMode=yes
-o ConnectTimeout="${SSH_TIMEOUT}"
)
LOG_DIR="${BACKUP_LOG_DIR}"
mkdir -p "$LOG_DIR"
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"
exec > >(tee -a "$LOG_FILE") 2>&1
log() { echo "---- $(date +'%Y-%m-%d %H:%M:%S') ---- $*"; }
require_cmd() {
command -v "$1" >/dev/null 2>&1
}
export PGPASSWORD
#######################################
# Vérification dépendances minimales
#######################################
for cmd in ssh scp curl jq pg_dump pg_dumpall; do
require_cmd "$cmd" || {
echo "ERROR: commande manquante : $cmd" >&2
exit 1
}
done
#######################################
# Configuration Discord
#######################################
DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-}"
DISCORD_PING="${DISCORD_PING:-@here}"
discord_send() {
local msg="$1"
[[ -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
}
curl -fsS \
-H "Content-Type: application/json" \
-d "$payload" \
"$DISCORD_WEBHOOK_URL" >/dev/null || true
}
#######################################
# Message global OK
#######################################
discord_msg_global_ok() {
local msg
msg="$(cat <<EOF
**BACKUP BDD ${ENV_NAME} 🟢**
Name: ${BACKUP_DIR_NAME}
Dumps transfer: ✅
Users transfer: ✅
EOF
)"
discord_send "$msg"
}
#######################################
# Messages USERS
#######################################
discord_msg_users_ok_simple() {
local msg
msg="$(cat <<EOF
**BACKUP BDD ${ENV_NAME} 🟢**
Users backup validé
EOF
)"
discord_send "$msg"
}
discord_msg_users_error() {
local export_ok="$1"
local transfer_ok="$2"
local details="$3"
local export_disp transfer_disp
export_disp=$([[ -n "$export_ok" ]] && echo "✅" || echo "❌")
transfer_disp=$([[ -n "$transfer_ok" ]] && echo "✅" || echo "❌")
local msg
if [[ -n "$details" ]]; then
msg="$(cat <<EOF
**${DISCORD_PING} BACKUP BDD ${ENV_NAME} 🔴**
Name: ${BACKUP_DIR_NAME}
Users export: ${export_disp}
Users transfer: ${transfer_disp}
Details: ${details}
EOF
)"
else
msg="$(cat <<EOF
**${DISCORD_PING} BACKUP BDD ${ENV_NAME} 🔴**
Name: ${BACKUP_DIR_NAME}
Users export: ${export_disp}
Users transfer: ${transfer_disp}
EOF
)"
fi
discord_send "$msg"
}
#######################################
# Messages DB
#######################################
discord_msg_db_ok_simple() {
local db="$1"
local msg
msg="$(cat <<EOF
**BACKUP BDD ${ENV_NAME} 🟢**
Backup validé : ${db}
EOF
)"
discord_send "$msg"
}
discord_msg_db_error() {
local db="$1"
local dump_ok="$2"
local transfer_ok="$3"
local details="$4"
local dump_disp transfer_disp
dump_disp=$([[ -n "$dump_ok" ]] && echo "✅" || echo "❌")
transfer_disp=$([[ -n "$transfer_ok" ]] && echo "✅" || echo "❌")
local msg
if [[ -n "$details" ]]; then
msg="$(cat <<EOF
**${DISCORD_PING} BACKUP BDD ${ENV_NAME} 🔴**
Name: ${BACKUP_DIR_NAME}
Database: ${db}
Dump: ${dump_disp}
Transfer: ${transfer_disp}
Details: ${details}
EOF
)"
else
msg="$(cat <<EOF
**${DISCORD_PING} BACKUP BDD ${ENV_NAME} 🔴**
Name: ${BACKUP_DIR_NAME}
Database: ${db}
Dump: ${dump_disp}
Transfer: ${transfer_disp}
EOF
)"
fi
discord_send "$msg"
}
#######################################
# Variables de statut globales
#######################################
DUMPS_OK=true
USERS_OK=true
USERS_EXPORT_OK=true
USERS_TRANSFER_OK=true
USERS_DETAILS=""
declare -A DB_DUMP_OK
declare -A DB_TRANSFER_OK
declare -A DB_DETAILS
#######################################
# Verrou dexécution
#######################################
LOCK_DIR="/tmp/pg_multi_dump_stream.lock.d"
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
log "ERROR: Backup déjà en cours"
discord_msg_users_error "" "" "Lock already exists"
exit 1
fi
trap 'rm -rf "$LOCK_DIR" "$TMP_DIR"' EXIT
#######################################
# Préparation du dossier distant
#######################################
REMOTE_DIR="${IA_BASE_DIR}"
log "Creating remote directories"
if ! ssh "${SSH_OPTS[@]}" "$IA_SSH" "mkdir -p '${REMOTE_DIR}/ferme' '${REMOTE_DIR}/sirh' '${REMOTE_DIR}/inventory' '${REMOTE_DIR}/user'"; then
log "ERROR: remote mkdir failed"
discord_msg_users_error "" "" "Remote mkdir failed"
exit 1
fi
#######################################
# Export des rôles PostgreSQL
#######################################
ROLES_FILE="${TMP_DIR}/user_${TS}.sql"
set +e
log "Export des rôles PostgreSQL"
pg_dumpall \
-h "$PGHOST" \
-p "$PGPORT" \
-U "$PGUSER" \
--globals-only \
> "$ROLES_FILE"
RET=$?
if [[ $RET -ne 0 ]]; then
USERS_OK=
USERS_EXPORT_OK=
USERS_DETAILS="roles export failed"
else
log "Export des rôles OK : $ROLES_FILE"
fi
if [[ -n "${USERS_EXPORT_OK:-}" ]]; then
scp "${SSH_OPTS[@]}" "$ROLES_FILE" "$IA_SSH:${REMOTE_DIR}/user/"
RET=$?
if [[ $RET -ne 0 ]]; then
USERS_OK=
USERS_TRANSFER_OK=
if [[ -n "$USERS_DETAILS" ]]; then
USERS_DETAILS+=" | roles transfer failed"
else
USERS_DETAILS="roles transfer failed"
fi
else
log "Transfert des rôles OK"
fi
fi
set -e
#######################################
# Dump des bases
#######################################
set +e
for DB in "${DBS_ARRAY[@]}"; do
FILE="${TMP_DIR}/${DB}_${TS}.dump"
DB_DUMP_OK["$DB"]=true
DB_TRANSFER_OK["$DB"]=true
DB_DETAILS["$DB"]=""
log "Dump $DB"
pg_dump -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -Fc -d "$DB" -f "$FILE"
RET=$?
if [[ $RET -ne 0 ]]; then
DUMPS_OK=
DB_DUMP_OK["$DB"]=
DB_TRANSFER_OK["$DB"]=
DB_DETAILS["$DB"]="dump failed"
continue
fi
scp "${SSH_OPTS[@]}" "$FILE" "$IA_SSH:${REMOTE_DIR}/${DB}/"
RET=$?
if [[ $RET -ne 0 ]]; then
DUMPS_OK=
DB_TRANSFER_OK["$DB"]=
DB_DETAILS["$DB"]="transfer failed"
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 '${REMOTE_DIR}/user' -type f -name 'user_*.sql' -mtime +${RETENTION_DAYS} -delete"
RET=$?
if [[ $RET -ne 0 ]]; then
log "ERROR: remote rotation failed for users"
else
log "Remote rotation OK for users"
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"
RET=$?
if [[ $RET -ne 0 ]]; then
log "ERROR: remote rotation failed for ${DB}"
else
log "Remote rotation OK for ${DB}"
fi
done
set -e
log "Remote rotation finished"
#######################################
# Nettoyage local
#######################################
rm -rf "$TMP_DIR"
#######################################
# Bilan final Discord
#######################################
MODE_KO=
[[ -z "${DUMPS_OK:-}" ]] && MODE_KO=true
[[ -z "${USERS_OK:-}" ]] && MODE_KO=true
if [[ -z "${MODE_KO:-}" ]]; then
discord_msg_global_ok
exit 0
fi
if [[ -n "${USERS_EXPORT_OK:-}" && -n "${USERS_TRANSFER_OK:-}" ]]; then
discord_msg_users_ok_simple
else
discord_msg_users_error "${USERS_EXPORT_OK:+true}" "${USERS_TRANSFER_OK:+true}" "$USERS_DETAILS"
fi
for DB in "${DBS_ARRAY[@]}"; do
if [[ -n "${DB_DUMP_OK[$DB]:-}" && -n "${DB_TRANSFER_OK[$DB]:-}" ]]; then
discord_msg_db_ok_simple "$DB"
else
discord_msg_db_error "$DB" "${DB_DUMP_OK[$DB]:+true}" "${DB_TRANSFER_OK[$DB]:+true}" "${DB_DETAILS[$DB]}"
fi
done
exit 2

View File

@@ -0,0 +1,65 @@
###############################################################################
# ENVIRONNEMENT
###############################################################################
# Nom de l'environnement
ENV_NAME=RECETTE
###############################################################################
# POSTGRESQL
###############################################################################
# Host du serveur PostgreSQL
PGHOST=localhost
# Port PostgreSQL
PGPORT=5432
# Utilisateur utilisé pour réaliser les dumps
PGUSER=
# Mot de passe PostgreSQL
PGPASSWORD=
# Bases de données à sauvegarder (séparées par des espaces)
DBS="sirh inventory ferme"
###############################################################################
# SERVEUR DISTANT DE BACKUP
###############################################################################
# Utilisateur SSH du serveur de backup
BACKUP_REMOTE_USER=
# Host ou IP du serveur distant
BACKUP_REMOTE_HOST=
# Dossier distant où seront stockées les sauvegardes
BACKUP_REMOTE_DIR=/home/.../backups/bdd-recette
###############################################################################
# SSH
###############################################################################
# Clé SSH utilisée pour se connecter au serveur distant
SSH_KEY=/home/.../.ssh/id_ed25519_backup
# Timeout de connexion SSH (secondes)
SSH_TIMEOUT=10
###############################################################################
# LOGS
###############################################################################
# Dossier où seront stockés les logs du script
BACKUP_LOG_DIR=/var/log/script/
###############################################################################
# DISCORD (optionnel)
###############################################################################
# Webhook Discord pour envoyer les notifications
DISCORD_WEBHOOK_URL=
# Mention envoyée en cas d'erreur
DISCORD_PING=@here

View File

@@ -0,0 +1,227 @@
#!/usr/bin/env bash
set -uo pipefail
###############################################################################
# check-statut-recette.sh
#
# Ce script vérifie la disponibilité de plusieurs applications web définies
# dans le fichier .env.
#
# Fonctionnement global :
# 1. charge la configuration depuis le fichier .env ;
# 2. vérifie le DNS de chaque application ;
# 3. effectue une requête HTTP avec curl ;
# 4. écrit le résultat dans un fichier de log local ;
# 5. construit un message récapitulatif unique ;
# 6. envoie une seule notification Discord avec tous les statuts.
###############################################################################
#######################################
# Chargement du .env
#######################################
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/.env"
if [[ ! -f "$ENV_FILE" ]]; then
echo "ERROR: fichier .env introuvable : $ENV_FILE" >&2
exit 1
fi
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
#######################################
# Vérification des variables requises
#######################################
: "${ENV_NAME:?Variable ENV_NAME manquante}"
: "${APP_LOG_DIR:?Variable APP_LOG_DIR manquante}"
: "${CHECK_CONNECT_TIMEOUT:?Variable CHECK_CONNECT_TIMEOUT manquante}"
: "${CHECK_MAX_TIME:?Variable CHECK_MAX_TIME manquante}"
: "${APP_URLS:?Variable APP_URLS manquante}"
#######################################
# Sites à vérifier
#######################################
read -r -a SITES <<< "$APP_URLS"
SCHEME="http"
CONNECT_TIMEOUT="${CHECK_CONNECT_TIMEOUT}"
MAX_TIME="${CHECK_MAX_TIME}"
#######################################
# Logs
#######################################
LOG_DIR="${APP_LOG_DIR}"
mkdir -p "$LOG_DIR"
LOG_FILE="${LOG_DIR}/app_health_$(date +'%Y-%m-%d').log"
#######################################
# Discord
#######################################
DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-}"
DISCORD_PING="${DISCORD_PING:-@here}"
#######################################
# Variables globales de synthèse
#######################################
SUMMARY_LINES=()
FAILURES=0
#######################################
# Logging
#######################################
log_line() {
printf "%s | %s | %s | %s\n" \
"$(date +'%Y-%m-%d %H:%M:%S')" "$1" "$2" "$3" | tee -a "$LOG_FILE"
}
#######################################
# DNS
#######################################
dns_ok() {
getent hosts "$1" >/dev/null 2>&1
}
#######################################
# Ajout au résumé Discord
#######################################
add_summary_line() {
local site="$1"
local status="$2"
local detail="$3"
local icon
if [[ "$status" == "OK" ]]; then
icon="✅"
else
icon="❌"
fi
SUMMARY_LINES+=("${icon} ${site} : ${detail}")
}
#######################################
# Envoi du message Discord récapitulatif
#######################################
send_discord_summary() {
[[ -z "${DISCORD_WEBHOOK_URL:-}" ]] && return 0
local header_icon ping_prefix=""
if [[ "$FAILURES" -eq 0 ]]; then
header_icon="🟢"
else
header_icon="🔴"
ping_prefix="${DISCORD_PING} "
fi
local msg
msg="$(printf '**%sCHECK APP %s %s**\n' \
"$ping_prefix" \
"$ENV_NAME" \
"$header_icon"
)"
local line
for line in "${SUMMARY_LINES[@]}"; do
msg+="$(printf '%s\n' "$line")"
done
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
}
#######################################
# Check application
#######################################
check_site() {
local host="$1"
local url="${SCHEME}://${host}/"
if ! dns_ok "$host"; then
log_line "DOWN" "$host" "Résolution impossible (getent hosts)"
add_summary_line "$host" "DOWN" "DOWN - DNS"
return 1
fi
local http_code curl_exit err
local stderr
stderr="$(mktemp)"
http_code="$(
curl -sS -o /dev/null \
-w '%{http_code}' \
--connect-timeout "$CONNECT_TIMEOUT" \
--max-time "$MAX_TIME" \
"$url" 2>"$stderr"
)"
curl_exit=$?
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"
add_summary_line "$host" "OK" "OK"
return 0
fi
log_line "DOWN" "$host" "HTTP $http_code (erreur appli)"
add_summary_line "$host" "DOWN" "DOWN - HTTP $http_code"
return 1
fi
log_line "DOWN" "$host" "Code HTTP inattendu: $http_code"
add_summary_line "$host" "DOWN" "DOWN - code HTTP invalide"
return 1
}
#######################################
# Main
#######################################
main() {
local failures=0
for site in "${SITES[@]}"; do
if ! check_site "$site"; then
failures=$((failures + 1))
fi
done
FAILURES="$failures"
send_discord_summary
if [[ "$failures" -gt 0 ]]; then
exit 2
fi
exit 0
}
main "$@"

View File

@@ -0,0 +1,42 @@
###############################################################################
# ENVIRONNEMENT
###############################################################################
# Nom de l'environnement surveillé
ENV_NAME=RECETTE
###############################################################################
# LOGS
###############################################################################
# Dossier où seront stockés les logs du script
APP_LOG_DIR=/var/log/script
###############################################################################
# PARAMÈTRES DE VÉRIFICATION HTTP
###############################################################################
# Timeout de connexion à l'application (secondes)
# Si le serveur ne répond pas dans ce délai, la connexion échoue
CHECK_CONNECT_TIMEOUT=5
# Temps maximum total autorisé pour la requête HTTP (secondes)
CHECK_MAX_TIME=10
###############################################################################
# APPLICATIONS À SURVEILLER
###############################################################################
# Liste des applications à vérifier (séparées par des espaces)
APP_URLS="ferme.malio-dev.fr inventory.malio-dev.fr sirh.malio-dev.fr"
###############################################################################
# DISCORD
###############################################################################
# Webhook Discord pour envoyer le résumé des vérifications
DISCORD_WEBHOOK_URL=https:
# Mention Discord en cas de problème
DISCORD_PING=@here

View File

@@ -0,0 +1,447 @@
#!/usr/bin/env bash
set -euo pipefail
###############################################################################
# rebuild-bdd-recette.sh
#
# Script de reconstruction d'une base PostgreSQL à partir d'un dump distant.
#
# Fonctionnement global :
# 1. charge la configuration depuis le fichier .env ;
# 2. prépare les chemins, logs et options SSH ;
# 3. installe PostgreSQL si absent ;
# 4. démarre PostgreSQL si nécessaire ;
# 5. crée le rôle PGUSER uniquement si PostgreSQL vient d'être installé ;
# 6. propose à l'utilisateur de choisir une base à reconstruire ;
# 7. teste la connexion SSH au serveur distant ;
# 8. recherche le dernier dump distant de la base choisie ;
# 9. recherche le dernier fichier SQL des rôles dans le dossier "user" ;
# 10. télécharge les fichiers nécessaires ;
# 11. restaure les rôles via psql (avec filtrage des rôles sensibles) ;
# 12. supprime puis recrée la base cible ;
# 13. restaure la base choisie via pg_restore ;
# 14. envoie une notification Discord si tout s'est bien passé.
###############################################################################
###############################################################################
# Chemins fixes du script
###############################################################################
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="${SCRIPT_DIR}/.env"
###############################################################################
# Vérification du fichier .env
###############################################################################
if [[ ! -f "$ENV_FILE" ]]; then
echo "ERROR: fichier .env introuvable : $ENV_FILE" >&2
exit 1
fi
###############################################################################
# Chargement du .env
###############################################################################
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
###############################################################################
# Variables obligatoires
###############################################################################
: "${ENV_NAME:?Variable ENV_NAME manquante}"
: "${PGHOST:?Variable PGHOST manquante}"
: "${PGPORT:?Variable PGPORT manquante}"
: "${PGUSER:?Variable PGUSER manquante}"
: "${PGPASSWORD:?Variable PGPASSWORD manquante}"
: "${DBS:?Variable DBS manquante}"
: "${BACKUP_REMOTE_USER:?Variable BACKUP_REMOTE_USER manquante}"
: "${BACKUP_REMOTE_HOST:?Variable BACKUP_REMOTE_HOST manquante}"
: "${BACKUP_REMOTE_DIR:?Variable BACKUP_REMOTE_DIR manquante}"
: "${SSH_KEY:?Variable SSH_KEY manquante}"
: "${BACKUP_LOG_DIR:?Variable BACKUP_LOG_DIR manquante}"
###############################################################################
# Variables optionnelles
###############################################################################
LOCAL_RESTORE_DIR="${LOCAL_RESTORE_DIR:-${SCRIPT_DIR}/restore_tmp}"
REMOTE_ROLES_DIR_NAME="${REMOTE_ROLES_DIR_NAME:-user}"
SSH_CONNECT_TIMEOUT="${SSH_CONNECT_TIMEOUT:-8}"
DISCORD_WEBHOOK_URL="${DISCORD_WEBHOOK_URL:-}"
###############################################################################
# Préparation des dossiers locaux
###############################################################################
mkdir -p "$BACKUP_LOG_DIR" || {
echo "ERROR: impossible de créer le dossier de logs : $BACKUP_LOG_DIR" >&2
exit 1
}
mkdir -p "$LOCAL_RESTORE_DIR" || {
echo "ERROR: impossible de créer le dossier local de restauration : $LOCAL_RESTORE_DIR" >&2
exit 1
}
TIMESTAMP="$(date '+%Y-%m-%d_%H-%M-%S')"
LOG_FILE="${BACKUP_LOG_DIR}/restore_${ENV_NAME,,}_${TIMESTAMP}.log"
touch "$LOG_FILE" || {
echo "ERROR: impossible d'écrire dans le fichier de log : $LOG_FILE" >&2
exit 1
}
###############################################################################
# Fonctions utilitaires
###############################################################################
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
fail() {
log "ERROR: $*"
exit 1
}
cleanup() {
rm -f \
"${LOCAL_DB_DUMP_FILE:-}" \
"${LOCAL_ROLES_FILE:-}" \
"${FILTERED_ROLES_FILE:-}" \
"${ROLES_CREATE_LIST:-}" \
"${ROLES_APPLY_FILE:-}"
}
trap cleanup EXIT
require_cmd() {
command -v "$1" >/dev/null 2>&1
}
###############################################################################
# Envoi Discord
#
# Envoi simple d'un message texte via webhook Discord.
# Si 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."
return 0
}
if ! require_cmd curl; then
log "curl absent : notification Discord ignorée."
return 0
fi
payload="$(python3 -c 'import json,sys; print(json.dumps({"content": sys.argv[1]}))' "$message")" || {
log "Impossible de construire le payload JSON Discord."
return 0
}
curl -sS -X POST "$DISCORD_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "$payload" \
>/dev/null || log "Échec d'envoi de la notification Discord."
}
###############################################################################
# Vérifications de base
###############################################################################
[[ -f "$SSH_KEY" ]] || fail "clé SSH introuvable : $SSH_KEY"
[[ -r "$SSH_KEY" ]] || fail "clé SSH non lisible : $SSH_KEY"
export PGPASSWORD
SSH_OPTS=(
-i "$SSH_KEY"
-o IdentitiesOnly=yes
-o BatchMode=yes
-o ConnectTimeout="$SSH_CONNECT_TIMEOUT"
-o StrictHostKeyChecking=accept-new
)
REMOTE_SSH="${BACKUP_REMOTE_USER}@${BACKUP_REMOTE_HOST}"
###############################################################################
# Installation PostgreSQL si absent
#
# Le rôle PGUSER est créé uniquement si PostgreSQL vient d'être installé.
###############################################################################
POSTGRES_INSTALLED=false
if ! require_cmd psql || ! require_cmd pg_restore || ! require_cmd createdb || ! require_cmd dropdb; then
log "PostgreSQL absent : installation en cours..."
sudo apt update >>"$LOG_FILE" 2>&1 || fail "échec de apt update"
sudo apt install -y postgresql postgresql-client postgresql-contrib \
>>"$LOG_FILE" 2>&1 || fail "échec de l'installation de PostgreSQL"
POSTGRES_INSTALLED=true
log "Installation PostgreSQL terminée."
else
log "PostgreSQL déjà installé."
fi
###############################################################################
# Démarrage PostgreSQL
###############################################################################
if ! sudo systemctl is-active --quiet postgresql; then
log "Démarrage du service PostgreSQL..."
sudo systemctl start postgresql >>"$LOG_FILE" 2>&1 || fail "impossible de démarrer PostgreSQL"
else
log "Service PostgreSQL déjà actif."
fi
###############################################################################
# Attente disponibilité PostgreSQL
###############################################################################
log "Vérification de la disponibilité de PostgreSQL..."
for _ in {1..20}; do
if sudo -u postgres psql -d postgres -c "SELECT 1;" >/dev/null 2>&1; then
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
fail "PostgreSQL ne répond pas correctement"
fi
###############################################################################
# Création du rôle PGUSER uniquement si PostgreSQL vient d'être installé
###############################################################################
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}';" \
>>"$LOG_FILE" 2>&1 || fail "échec de création du rôle ${PGUSER}"
log "Rôle PostgreSQL ${PGUSER} créé."
fi
###############################################################################
# Affichage des bases disponibles
###############################################################################
read -r -a DBS_ARRAY <<< "$DBS"
if [[ "${#DBS_ARRAY[@]}" -eq 0 ]]; then
fail "aucune base définie dans DBS"
fi
echo "Bases disponibles dans le .env :"
for i in "${!DBS_ARRAY[@]}"; do
printf ' %d) %s\n' "$((i + 1))" "${DBS_ARRAY[$i]}"
done
echo
read -r -p "Voulez-vous utiliser une base de cette liste ? (oui/non) : " USE_LIST
DB=""
if [[ "${USE_LIST,,}" == "oui" || "${USE_LIST,,}" == "o" ]]; then
read -r -p "Sélectionnez le numéro de la base à restaurer : " DB_INDEX
[[ "$DB_INDEX" =~ ^[0-9]+$ ]] || fail "numéro invalide"
(( DB_INDEX >= 1 && DB_INDEX <= ${#DBS_ARRAY[@]} )) || fail "numéro hors plage"
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
log "Environnement : $ENV_NAME"
log "Base cible sélectionnée : $DB"
###############################################################################
# Test de connexion SSH
###############################################################################
log "Test de connexion SSH vers ${REMOTE_SSH}..."
SSH_TEST_OUTPUT=""
if ! SSH_TEST_OUTPUT="$(ssh "${SSH_OPTS[@]}" "$REMOTE_SSH" "exit 0" 2>&1)"; then
echo "$SSH_TEST_OUTPUT" | tee -a "$LOG_FILE" >&2
fail "connexion SSH impossible vers ${REMOTE_SSH}"
fi
###############################################################################
# Définition des chemins distants
###############################################################################
REMOTE_DB_DIR="${BACKUP_REMOTE_DIR}/${DB}"
REMOTE_ROLES_DIR="${BACKUP_REMOTE_DIR}/${REMOTE_ROLES_DIR_NAME}"
log "Recherche du dernier dump distant pour ${DB} dans : ${REMOTE_DB_DIR}"
log "Recherche du dernier fichier de rôles dans : ${REMOTE_ROLES_DIR}"
###############################################################################
# Recherche du dernier dump de base
###############################################################################
LAST_REMOTE_DB_DUMP="$(
ssh "${SSH_OPTS[@]}" "$REMOTE_SSH" \
"find '${REMOTE_DB_DIR}' -maxdepth 1 -type f -name '${DB}_*.dump' | LC_ALL=C sort | tail -n 1"
)"
if [[ -z "$LAST_REMOTE_DB_DUMP" ]]; then
fail "aucun dump trouvé pour la base ${DB} dans ${REMOTE_DB_DIR}"
fi
log "Dernier dump distant sélectionné : ${LAST_REMOTE_DB_DUMP}"
###############################################################################
# Recherche du dernier fichier SQL des rôles
###############################################################################
LAST_REMOTE_ROLES_FILE="$(
ssh "${SSH_OPTS[@]}" "$REMOTE_SSH" \
"find '${REMOTE_ROLES_DIR}' -maxdepth 1 -type f -name 'user_*.sql' | LC_ALL=C sort | tail -n 1"
)"
if [[ -n "$LAST_REMOTE_ROLES_FILE" ]]; then
log "Dernier fichier des rôles sélectionné : ${LAST_REMOTE_ROLES_FILE}"
else
log "Aucun fichier des rôles trouvé sur le serveur distant."
fi
###############################################################################
# Téléchargement du dump principal
###############################################################################
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" \
>>"$LOG_FILE" 2>&1 || fail "échec du téléchargement du dump principal"
###############################################################################
# Téléchargement du fichier des rôles si présent
###############################################################################
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" \
>>"$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."
fi
###############################################################################
# Test de connexion PostgreSQL locale avec PGUSER
###############################################################################
if ! psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d postgres -c "SELECT 1;" \
>>"$LOG_FILE" 2>&1; then
fail "connexion PostgreSQL locale impossible avec PGUSER=${PGUSER}"
fi
###############################################################################
# Demande d'écrasement si la base existe déjà
###############################################################################
DB_EXISTS="$(
psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d postgres -tAc \
"SELECT 1 FROM pg_database WHERE datname='${DB}'" 2>>"$LOG_FILE" || true
)"
if [[ "$DB_EXISTS" == "1" ]]; then
read -r -p "La base '${DB}' existe déjà. Voulez-vous l'écraser ? (oui/non) : " CONFIRM_OVERWRITE
if [[ "${CONFIRM_OVERWRITE,,}" != "oui" && "${CONFIRM_OVERWRITE,,}" != "o" ]]; then
fail "restauration annulée par l'utilisateur"
fi
log "Suppression de la base existante : ${DB}"
dropdb -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" --if-exists "$DB" \
>>"$LOG_FILE" 2>&1 || fail "échec de suppression de la base ${DB}"
fi
###############################################################################
# Restauration des rôles
###############################################################################
if [[ -n "$LOCAL_ROLES_FILE" ]]; then
log "Restauration des rôles depuis : ${LOCAL_ROLES_FILE}"
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")"
grep -viE '^(CREATE ROLE|ALTER ROLE) (backup_liot|postgres)\b' "$LOCAL_ROLES_FILE" \
> "$FILTERED_ROLES_FILE" || true
log "Fichier des rôles filtré généré : ${FILTERED_ROLES_FILE}"
sed -nE 's/^CREATE ROLE "?([^" ;]+)"?;$/\1/p' "$FILTERED_ROLES_FILE" \
> "$ROLES_CREATE_LIST" || true
if [[ -s "$ROLES_CREATE_LIST" ]]; then
while IFS= read -r role_name; do
[[ -z "$role_name" ]] && continue
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
)"
if [[ "$ROLE_EXISTS" != "1" ]]; then
log "Création du rôle manquant : ${role_name}"
psql -v ON_ERROR_STOP=1 \
-h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d postgres \
-c "CREATE ROLE \"${role_name}\";" \
>>"$LOG_FILE" 2>&1 || fail "échec de création du rôle ${role_name}"
else
log "Rôle déjà présent, création ignorée : ${role_name}"
fi
done < "$ROLES_CREATE_LIST"
fi
grep -viE '^CREATE ROLE ' "$FILTERED_ROLES_FILE" > "$ROLES_APPLY_FILE" || true
log "Application des ALTER ROLE / privilèges / memberships..."
psql \
-v ON_ERROR_STOP=1 \
-h "$PGHOST" \
-p "$PGPORT" \
-U "$PGUSER" \
-d postgres \
-f "$ROLES_APPLY_FILE" \
>>"$LOG_FILE" 2>&1 || fail "échec de restauration des rôles via psql"
else
log "Aucune restauration des rôles effectuée."
fi
###############################################################################
# Création de la base
###############################################################################
log "Création de la base : ${DB}"
createdb -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" "$DB" \
>>"$LOG_FILE" 2>&1 || fail "échec de création de la base ${DB}"
###############################################################################
# Restauration de la base principale
###############################################################################
log "Restauration de la base ${DB}..."
pg_restore \
-h "$PGHOST" \
-p "$PGPORT" \
-U "$PGUSER" \
-d "$DB" \
--clean \
--if-exists \
--no-owner \
--no-privileges \
"$LOCAL_DB_DUMP_FILE" \
>>"$LOG_FILE" 2>&1 || fail "échec de restauration de la base ${DB}"
###############################################################################
# Fin
###############################################################################
log "Restauration terminée avec succès pour la base : ${DB}"
log "Fichier de log : ${LOG_FILE}"
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"

View File

@@ -0,0 +1,80 @@
###############################################################################
# ENVIRONNEMENT
###############################################################################
# Nom de l'environnement
# Exemple : DEV / RECETTE / PROD
ENV_NAME=RECETTE
###############################################################################
# POSTGRESQL LOCAL
###############################################################################
# Hôte PostgreSQL local sur lequel la restauration sera effectuée
PGHOST=localhost
# Port PostgreSQL local
PGPORT=5432
# Utilisateur PostgreSQL utilisé pour créer la base et lancer la restauration
PGUSER=
# Mot de passe
PGPASSWORD=
# Liste des bases proposées à la restauration (séparées par des espaces)
# L'utilisateur pourra en choisir une dans le script
DBS="sirh inventory ferme"
###############################################################################
# SERVEUR DISTANT DE BACKUP
###############################################################################
# Utilisateur SSH du serveur distant contenant les dumps
BACKUP_REMOTE_USER=
# Hôte ou IP du serveur distant
BACKUP_REMOTE_HOST=
# Répertoire racine distant :
BACKUP_REMOTE_DIR=/home/.../backups/bdd-recette
###############################################################################
# SSH
###############################################################################
# Clé privée SSH utilisée pour se connecter au serveur distant
SSH_KEY=/home/.../.ssh/id_ed25519_backup
# Timeout de connexion SSH en secondes
# Variable optionnelle dans le script, mais utile ici comme valeur par défaut
SSH_CONNECT_TIMEOUT=8
###############################################################################
# LOGS
###############################################################################
# Dossier local dans lequel seront écrits les logs de restauration
BACKUP_LOG_DIR=/var/log/pg_backup
###############################################################################
# RESTAURATION LOCALE
###############################################################################
# Dossier local temporaire pour télécharger les fichiers avant restauration
# Optionnel : si absent, le script utilise ./restore_tmp
LOCAL_RESTORE_DIR=/tmp/rebuild-bdd-recette
###############################################################################
# RÔLES POSTGRESQL DISTANTS
###############################################################################
# Nom du dossier distant contenant les exports SQL des rôles
REMOTE_ROLES_DIR_NAME=user
###############################################################################
# DISCORD
###############################################################################
# Webhook Discord pour notifier le succès de la restauration
DISCORD_WEBHOOK_URL=

View File

@@ -1,210 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
#######################################
# Configuration
#######################################
DBS=("sirh" "inventory" "ferme")
PGHOST="localhost"
PGPORT="5432"
PGUSER="backup_liot"
PGPASSWORD="backup_liot"
IA_SSH="malio-b@192.168.0.179"
IA_BASE_DIR="/home/malio-b/backups"
SSH_KEY="/home/malio/.ssh/id_ed25519_backup"
SSH_OPTS=(-i "$SSH_KEY" -o IdentitiesOnly=yes -o BatchMode=yes -o ConnectTimeout=10)
LOG_DIR="/var/log/pg_backup"
mkdir -p "$LOG_DIR"
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"
exec > >(tee -a "$LOG_FILE") 2>&1
log() { echo "---- $(date +'%Y-%m-%d %H:%M:%S') ---- $*"; }
export PGPASSWORD
#######################################
# Discord (Webhook)
#######################################
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/1478503102888935506/YCtJM09QZiKNMiCe5u7vCQb52VcLjHAd9wwEsKNltlJVcy7sKvoMTOJkvEKOOrk-Wpkh"
discord_ping() {
local details="${1:-}"
[[ -z "${DISCORD_WEBHOOK_URL:-}" ]] && return 0
local color dumps_display users_display
if [[ -n "${DUMPS_OK:-}" && -n "${USERS_OK:-}" ]]; then
color="🟢"
else
color="🔴"
fi
dumps_display=$([[ -n "${DUMPS_OK:-}" ]] && echo "✅" || echo "❌")
users_display=$([[ -n "${USERS_OK:-}" ]] && echo "✅" || echo "❌")
local msg="**@here BACKUP BDD RECETTE ${color}**\n"
msg+="Name: ${BACKUP_DIR_NAME}\n"
msg+="Dumps transfer: ${dumps_display}\n"
msg+="Users transfer: ${users_display}\n"
[[ -n "$details" ]] && msg+="Details: $details"
curl -fsS -H "Content-Type: application/json" \
-d "{\"content\":\"$msg\"}" \
"$DISCORD_WEBHOOK_URL" >/dev/null || true
}
#######################################
# Statuts init
#######################################
DUMPS_OK=true
USERS_OK=true
DUMP_ERRORS=""
USER_ERRORS=""
#######################################
# Lock (évite 2 backups en même temps)
#######################################
LOCK_DIR="/tmp/pg_multi_dump_stream.lock.d"
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
log "ERROR: Backup déjà en cours (lock: $LOCK_DIR)"
DUMPS_OK=
USERS_OK=
discord_ping "Lock exists: $LOCK_DIR"
exit 1
fi
trap 'rm -rf "$LOCK_DIR"' EXIT
#######################################
# Remote dir
#######################################
REMOTE_DIR="${IA_BASE_DIR}/${BACKUP_DIR_NAME}"
log "Starting backup process"
log "Remote directory: ${REMOTE_DIR}"
log "Creating remote directory"
if ! ssh "${SSH_OPTS[@]}" "$IA_SSH" "mkdir -p '${REMOTE_DIR}'"; then
log "ERROR: Création dossier distant impossible: ${REMOTE_DIR}"
DUMPS_OK=
USERS_OK=
discord_ping "Remote mkdir KO: ${REMOTE_DIR}"
exit 1
fi
#######################################
# Export PostgreSQL roles (no passwords)
#######################################
ROLES_FILE="${TMP_DIR}/roles_${TS}.sql"
log "Exporting PostgreSQL roles"
set +e
psql -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d postgres -Atq <<'SQL' > "$ROLES_FILE"
SELECT
format(
'DO $$ BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = %L) THEN
CREATE ROLE %I;
END IF;
END $$;',
rolname, rolname
)
FROM pg_roles
WHERE rolname !~ '^pg_'
ORDER BY rolname;
SELECT
format(
'ALTER ROLE %I WITH %s%s%s%s%s%s%s%s;',
rolname,
CASE WHEN rolsuper THEN 'SUPERUSER ' ELSE 'NOSUPERUSER ' END,
CASE WHEN rolinherit THEN 'INHERIT ' ELSE 'NOINHERIT ' END,
CASE WHEN rolcreaterole THEN 'CREATEROLE ' ELSE 'NOCREATEROLE ' END,
CASE WHEN rolcreatedb THEN 'CREATEDB ' ELSE 'NOCREATEDB ' END,
CASE WHEN rolcanlogin THEN 'LOGIN ' ELSE 'NOLOGIN ' END,
CASE WHEN rolreplication THEN 'REPLICATION ' ELSE 'NOREPLICATION ' END,
CASE WHEN rolbypassrls THEN 'BYPASSRLS ' ELSE 'NOBYPASSRLS ' END,
'CONNECTION LIMIT ' || rolconnlimit
)
FROM pg_roles
WHERE rolname !~ '^pg_'
ORDER BY rolname;
SQL
RET=$?
if [[ $RET -ne 0 ]]; then
USERS_OK=
USER_ERRORS+="roles_export "
log "ERROR: Users export failed"
else
log "Roles export completed: $ROLES_FILE"
fi
set -e
log "Sending roles file to IA server"
set +e
scp "${SSH_OPTS[@]}" "$ROLES_FILE" "$IA_SSH:${REMOTE_DIR}/"
RET=$?
if [[ $RET -ne 0 ]]; then
USERS_OK=
USER_ERRORS+="roles_scp "
log "ERROR: Users transfer failed (roles file)"
else
log "Roles transfer completed"
fi
set -e
#######################################
# Dump des bases + transfert (continue même si KO)
#######################################
set +e
for DB in "${DBS[@]}"; do
FILE="${TMP_DIR}/${DB}_${TS}.dump"
log "Dumping database: $DB"
pg_dump -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -Fc --no-owner --no-acl -d "$DB" -f "$FILE"
RET=$?
if [[ $RET -ne 0 ]]; then
DUMPS_OK=
DUMP_ERRORS+="${DB} "
log "ERROR: Dump failed for $DB"
continue
fi
log "Dump completed: $FILE"
log "Sending dump to IA server"
scp "${SSH_OPTS[@]}" "$FILE" "$IA_SSH:${REMOTE_DIR}/"
RET=$?
if [[ $RET -ne 0 ]]; then
DUMPS_OK=
DUMP_ERRORS+="${DB}(scp) "
log "ERROR: Transfer failed for $DB"
continue
fi
log "Transfer completed for $DB"
done
set -e
#######################################
# Nettoyage
#######################################
log "Cleaning temporary files"
rm -rf "$TMP_DIR"
#######################################
# Envoi message Discord (final)
#######################################
DETAILS=""
[[ -z "${DUMPS_OK:-}" ]] && DETAILS+="Dumps KO: ${DUMP_ERRORS} "
[[ -z "${USERS_OK:-}" ]] && DETAILS+="Users KO: ${USER_ERRORS} "
discord_ping "$DETAILS"
log "Backup finished"

View File

@@ -1,6 +0,0 @@
DATA_DIR=
LOCAL_BACKUP=
REMOTE_USER=
REMOTE_HOST=
REMOTE_DIR=
SSH_KEY=

View File

@@ -1,3 +0,0 @@
.env
backup.log

View File

@@ -1,116 +0,0 @@
# FONCTIONNEMENT DU SCRIPT VAULTWARDEN
Le script de backup de vaultwarden permet une sauvegard périodique des mots de passe et utilisateurs de celui-ci.
## INITIALISATION DES VARIABLES DE MANIÈRE SÉCURISÉ
1. Les informations sensibles ne sont pas stockées directement dans le script. Elles sont placées dans un fichier .env
```bash
WEBHOOK_URL=...
REMOTE_USER=...
REMOTE_HOST=...
SSH_KEY=...
DATA_DIR=...
```
2. on recupere les varibales dans le script
```bash
REMOTE_USER=$(grep -E '^REMOTE_USER=' .env | cut -d '=' -f2-)
```
Explication:
- grep recherche la variable dans le fichier .env
- cut récupère uniquement la valeur après =
- REMOTE_USER="user" Le script récupère >> "user"
Cela permet:
- daméliorer la sécurité
- déviter de modifier le script si un paramètre change
## RÉCUPÉRATION DES DONNÉES
1. Le dossier data de Vaultwarden est dupliqué puis compressé afin de créer une archive :
```bash
tar -czf "$LOCAL_BACKUP" -C "$(dirname "$DATA_DIR")" "$(basename "$DATA_DIR")"
```
2. Transfer vers le serveur de backup
```bash
scp "${SSH_OPTS[@]}" "$LOCAL_BACKUP" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/"
```
La sauvegarde est envoyée vers une machine dédiée grâce à SCP. Pour éviter de saisir un mot de passe à chaque fois, une clé SSH est utilisée.
Cette clé SSH est générée sur la machine de backup et autorisée sur la machine Vaultwarden.
## NOTIFICATION DISCORD
Le script envoie une notification sur un salon Discord pour informer de létat de la sauvegarde. Cela se fait grâce à un webhook Discord.
1. on défini le message
```bash
local msg="**@here Backup Vaultwarden $color**\n"
msg+="Backup: ${BACKUP_NAME}\n"
msg+="Data transfer: $dumps_display\n"
[[ -n "$details" ]] && msg+="Details: $details"
```
2. on envoie le message sur discord avec le message et le webhook
```bash
curl -fsS -H "Content-Type: application/json" \
-d "{\"content\":\"$msg\"}" \
"$DISCORD_WEBHOOK_URL"
```
Le message indique:
- si la sauvegarde a réussi 🟢
- si elle a échoué 🔴
- le nom du backup
- les détails de lerreur si nécessaire
## PLANIFICATION AVEC CRON
Le script est exécuté automatiquement chaque jour grâce à cron.
1. Ouvrez le crontab pour l'édition :
```bash
crontab -e
```
2. Ajoutez la ligne suivante pour exécuter le script tous les jours à 19h :
```bash
0 19 * * * /chemin/vers/le/script/check_storage.sh
```
Signification:
- 0 minute 0
- 19 19h
- * tous les jours du mois
- * tous les mois
- * tous les jours de la semaine
Tous les jours à 19h, le script est exécuté et les logs sont enregistrés dans backup.log ce qui permet danalyser les erreurs si un problème survient.
## NETTOYAGE
Une fois la sauvegarde envoyée sur la machine distante, le fichier temporaire est supprimé :
```bash
rm -f "$LOCAL_BACKUP"
```
Cela permet de garder le serveur propre et éviter de remplir le disque.
## RÉSUMÉ
Le script automatise complètement les sauvegardes Vaultwarden :
- sauvegarde du dossier data
- compression et datation
- transfert sécurisé via SSH
- notification Discord
- exécution automatique avec cron
- sécurisation des paramètres via .env
Cela permet davoir une sauvegarde quotidienne fiable et surveillée.

View File

@@ -1,147 +0,0 @@
#!/usr/bin/env bash
set -u
#######################################
# Sites à vérifier
#######################################
SITES=(
"ferme.malio-dev.fr"
"sirh.malio-dev.fr"
"inventory.malio-dev.fr"
)
SCHEME="http"
CONNECT_TIMEOUT=3
MAX_TIME=8
#######################################
# Logs
#######################################
LOG_DIR="/var/log/app_health"
mkdir -p "$LOG_DIR"
LOG_FILE="${LOG_DIR}/app_health_$(date +'%Y-%m-%d').log"
#######################################
# Discord
#######################################
DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/1478379245842600007/tSxi3G6PbCn89pOdeqK34LR7c-GhXfT-lSCPolwBywJXcpa3ihL8rN4QRwsTjF6SS3w0"
discord_ping() {
local site="$1"
local status="$2"
local detail="$3"
[[ -z "${DISCORD_WEBHOOK_URL:-}" ]] && return 0
local color icon
if [[ "$status" == "OK" ]]; then
color="🟢"
icon="✅"
else
color="🔴"
icon="❌"
fi
local msg="**CHECK APP RECETTE $color**\n"
msg+="Application: ${site}\n"
msg+="Status: ${icon}\n"
msg+="Details: ${detail}"
curl -fsS -H "Content-Type: application/json" \
-d "{\"content\":\"$msg\"}" \
"$DISCORD_WEBHOOK_URL" >/dev/null || true
}
#######################################
# Logging
#######################################
log_line() {
# 2026-03-04 14:12:33 | LEVEL | site | message
printf "%s | %s | %s | %s\n" \
"$(date +'%Y-%m-%d %H:%M:%S')" "$1" "$2" "$3" | tee -a "$LOG_FILE"
# Envoi Discord par application
discord_ping "$2" "$1" "$3"
}
#######################################
# DNS
#######################################
dns_ok() {
getent hosts "$1" >/dev/null 2>&1
}
#######################################
# Check application
#######################################
check_site() {
local host="$1"
local url="${SCHEME}://${host}/"
if ! dns_ok "$host"; then
log_line "DOWN" "$host" "Résolution impossible (getent hosts)"
return 1
fi
local http_code curl_exit stderr
stderr="$(mktemp)"
http_code="$(
curl -sS -o /dev/null \
-w '%{http_code}' \
--connect-timeout "$CONNECT_TIMEOUT" \
--max-time "$MAX_TIME" \
"$url" 2>"$stderr"
)"
curl_exit=$?
if [ $curl_exit -ne 0 ]; then
local err
err="$(head -n 1 "$stderr" | tr -d '\r')"
rm -f "$stderr"
log_line "DOWN" "$host" "curl exit=$curl_exit : ${err:-"(aucun)"}"
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"
return 0
fi
log_line "DOWN" "$host" "HTTP $http_code (erreur appli)"
return 1
fi
log_line "DOWN" "$host" "Code HTTP inattendu: $http_code"
return 1
}
#######################################
# Main
#######################################
main() {
local failures=0
for site in "${SITES[@]}"; do
if ! check_site "$site"; then
failures=$((failures + 1))
fi
done
if [ "$failures" -gt 0 ]; then
exit 2
fi
exit 0
}
main "$@"

132
global.env.exemple Normal file
View File

@@ -0,0 +1,132 @@
###############################################################################
# FICHIER .env.example
#
# Ce fichier sert de modèle de configuration pour les scripts d'automatisation :
# - backup-bdd-recette.sh → sauvegarde PostgreSQL
# - rebuild-bdd-recette.sh → reconstruction d'une base PostgreSQL
# - check-statut-recette.sh → vérification disponibilité des applications
# - check-storage.sh → surveillance de l'espace disque
# - backup-vaultwarden.sh → sauvegarde du service Vaultwarden
#
# Copier ce fichier en .env puis remplir les valeurs.
###############################################################################
#############################################
# ENVIRONNEMENT
#############################################
# Nom de l'environnement (ex : DEV / RECETTE / PROD)
ENV_NAME=RECETTE
#############################################
# DISCORD
#############################################
# Webhook Discord utilisé pour envoyer les notifications
WEBHOOK_URL=
#############################################
# POSTGRESQL
#############################################
# Adresse du serveur PostgreSQL
PGHOST=localhost
# Port PostgreSQL
PGPORT=5432
# Utilisateur utilisé pour les dumps
PGUSER=
# Mot de passe
PGPASSWORD=
# Bases de données à sauvegarder (séparées par espace)
# Utilisé par backup-bdd-recette.sh
DBS="sirh inventory ferme"
#############################################
# BACKUPS LOCAUX
#############################################
# Dossier local où les dumps seront générés temporairement
BACKUP_LOCAL_DIR=/var/backups/postgresql
# Dossier des logs de sauvegarde
BACKUP_LOG_DIR=/var/log/script/...
#############################################
# SERVEUR DISTANT DE STOCKAGE
#############################################
# Utilisateur du serveur de backup distant
BACKUP_REMOTE_USER=
# Adresse IP ou hostname du serveur de stockage
BACKUP_REMOTE_HOST=
# Dossier distant où stocker les backups
BACKUP_REMOTE_DIR=/home/.../backups/bdd-recette
#############################################
# SSH
#############################################
# Clé SSH utilisée pour se connecter au serveur distant
SSH_KEY=/home/.../.ssh/id_ed25519_backup
# Timeout SSH (secondes)
SSH_TIMEOUT=10
#############################################
# ROTATION DES BACKUPS
#############################################
# Nombre de jours de conservation des sauvegardes
BACKUP_RETENTION_DAYS=10
#############################################
# APPLICATIONS À SURVEILLER
#############################################
# Liste des applications à vérifier
APPS="
ferme.malio-dev.fr
inventory.malio-dev.fr
sirh.malio-dev.fr
"
#############################################
# VAULTWARDEN
#############################################
# Dossier contenant les données Vaultwarden
VAULTWARDEN_DATA_DIR=/opt/vaultwarden/data
# Dossier local où stocker le backup
VAULTWARDEN_BACKUP_DIR=/var/backups/vaultwarden
#############################################
# SERVEUR IA / STOCKAGE CENTRAL
#############################################
# Utilisateur SSH du serveur distant
IA_SSH_USER=
# Host du serveur distant
IA_SSH_HOST=
# Dossier racine contenant les dumps PostgreSQL
IA_BASE_DIR=/home/.../backups/bdd-recette
# Dossier contenant les rôles PostgreSQL exportés
REMOTE_ROLES_NAME=user