Compare commits
2 Commits
7261823806
...
e68c99a8b3
| Author | SHA1 | Date | |
|---|---|---|---|
| e68c99a8b3 | |||
| 7b91691ef8 |
@@ -33,4 +33,23 @@ REMOTE_DIR=
|
||||
#############################################
|
||||
|
||||
# Chemin vers la clé privée SSH utilisée pour la connexion
|
||||
SSH_KEY=
|
||||
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
|
||||
|
||||
@@ -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/<USER>/Malio-ops/BackupVaultWarden/
|
||||
```
|
||||
|
||||
Structure recommandée :
|
||||
|
||||
```bash
|
||||
/home/matt/vaultwarden/Malio-ops/BackupVaultWarden/
|
||||
/home/<USER>/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=<USER>
|
||||
REMOTE_HOST=<IP_SERVEUR>
|
||||
SSH_KEY=/home/matt/.ssh/id_ed25519_vaultwarden_backup
|
||||
SSH_KEY=/home/<USER>/.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 <USER>@<IP_SERVEUR>
|
||||
ssh-copy-id -i ~/.ssh/id_ed25519_backup.pub <USER>@<IP_SERVEUR>
|
||||
```
|
||||
|
||||
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 <USER>@<IP_SERVEUR>
|
||||
```
|
||||
|
||||
#### 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 <IP_SERVEUR> >> ~/.ssh/known_hosts
|
||||
chmod 600 ~/.ssh/known_hosts
|
||||
```
|
||||
|
||||
#### 7. Déclaration dans `.env`
|
||||
|
||||
```bash
|
||||
SSH_KEY=/home/<USER>/.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/<USER>/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/<USER>/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 :
|
||||
|
||||
|
||||
@@ -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"
|
||||
echo "Backup $BACKUP_NAME terminé et envoyé sur $REMOTE_HOST:$REMOTE_DIR"
|
||||
|
||||
56
CHANGELOG.md
56
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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <USER>@<HOST>
|
||||
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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
22
README.md
22
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
42
RebuildBdd/Config/Targets/prod.env.exemple
Normal file
42
RebuildBdd/Config/Targets/prod.env.exemple
Normal file
@@ -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
|
||||
@@ -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
|
||||
###############################################################################
|
||||
# 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
|
||||
@@ -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/<nom_cible>.env
|
||||
Config/Targets/<nom_cible>.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/<cible>.env
|
||||
Config/Targets/<cible>.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/<user>` 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/<cible>.env`
|
||||
1. créer ou mettre à jour `Config/Targets/<cible>.env`
|
||||
2. lancer `bootstrap-target-host.sh --target <cible>`
|
||||
3. lancer ensuite `run-rebuild-bdd.sh --target <cible> ...`
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" || {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
exit 2
|
||||
|
||||
@@ -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
|
||||
DISCORD_PING=@here
|
||||
|
||||
@@ -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 "$@"
|
||||
main "$@"
|
||||
|
||||
@@ -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"
|
||||
send_discord_message "$SUCCESS_MESSAGE"
|
||||
|
||||
@@ -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=
|
||||
DISCORD_WEBHOOK_URL=
|
||||
|
||||
@@ -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/<USER>/.ssh/id_ed25519_backup
|
||||
|
||||
# Timeout SSH (secondes)
|
||||
SSH_TIMEOUT=10
|
||||
|
||||
Reference in New Issue
Block a user