#!/bin/bash # Copyright (C) 2026 Cédric Abonnel # License: GNU Affero General Public License v3 set -euo pipefail # --- Configuration --- BASE_DIR="/opt/monitoring" CONF_DIR="${BASE_DIR}/conf" LOG_DIR="/var/log/monitoring" STATE_DIR="/var/lib/monitoring" LOCK_DIR="/var/lock/monitoring" TMP_DIR="/tmp/monitoring-install" UPDATE_BASE_URL="https://git.abonnel.fr/cedricAbonnel/scripts-bash/raw/branch/main/servers/linux/monitoring" MANIFEST_URL="${UPDATE_BASE_URL}/manifest.txt" INSTALL_DEPS="${INSTALL_DEPS:-true}" # --- Fonctions d'affichage --- info() { echo -e "\e[34m[INFO]\e[0m $1"; } ok() { echo -e "\e[32m[OK]\e[0m $1"; } warn() { echo -e "\e[33m[WARN]\e[0m $1"; } err() { echo -e "\e[31m[ERR]\e[0m $1"; } # --- Fonctions Techniques --- require_root() { if [ "${EUID}" -ne 0 ]; then err "Ce script doit être exécuté en root." exit 1 fi } install_deps() { if [ "${INSTALL_DEPS}" != "true" ]; then return 0; fi info "Vérification des dépendances système..." if command -v apt-get >/dev/null 2>&1; then apt-get update -qq apt-get install -y -qq curl coreutils findutils grep sed gawk util-linux ca-certificates php-cli php-curl php-common > /dev/null ok "Dépendances installées." fi } prepare_dirs() { info "Préparation de l'arborescence dans ${BASE_DIR}..." mkdir -p "${BASE_DIR}/bin" "${BASE_DIR}/lib" "${CONF_DIR}" "${LOG_DIR}" "${STATE_DIR}" "${LOCK_DIR}" "${TMP_DIR}" } fetch_manifest() { info "Téléchargement du manifeste distant..." curl -fsS "${MANIFEST_URL}" -o "${TMP_DIR}/manifest.txt" } validate_manifest() { awk ' NF == 3 && $1 ~ /^[0-9a-fA-F]{64}$/ && $2 ~ /^(644|755|600)$/ && $3 ~ /^(bin|lib|conf)\/[A-Za-z0-9._\/-]+$/ && $3 !~ /\.\./ ' "${TMP_DIR}/manifest.txt" } download_and_install() { local expected_hash="$1" local mode="$2" local rel_path="$3" local dst="${BASE_DIR}/${rel_path}" if [ -f "$dst" ]; then local current_hash current_hash=$(sha256sum "$dst" | awk '{print $1}') if [ "$current_hash" == "$expected_hash" ]; then return 0 # Déjà à jour fi info "Mise à jour : $rel_path" else info "Installation : $rel_path" fi local tmp_file tmp_file="$(mktemp "${TMP_DIR}/file.XXXXXX")" if ! curl -fsS "${UPDATE_BASE_URL}/${rel_path}" -o "$tmp_file"; then err "Échec du téléchargement pour $rel_path" rm -f "$tmp_file" return 1 fi local got_hash got_hash=$(sha256sum "$tmp_file" | awk '{print $1}') if [ "$got_hash" != "$expected_hash" ]; then err "Hash invalide pour $rel_path" rm -f "$tmp_file" return 1 fi mkdir -p "$(dirname "$dst")" mv -f "$tmp_file" "$dst" chmod "$mode" "$dst" } purge_obsolete_files() { info "Analyse des fichiers obsolètes (Synchronisation avec Git)..." # On scanne bin, lib et conf find "${BASE_DIR}/bin" "${BASE_DIR}/lib" "${BASE_DIR}/conf" -type f | while read -r local_file; do local rel_path="${local_file#$BASE_DIR/}" # PROTECTION : On ne touche jamais aux fichiers locaux personnels if [[ "$rel_path" == *".local."* ]]; then continue fi # Si le fichier n'est pas dans le manifeste validé, on le supprime if ! grep -qW "$rel_path" "${TMP_DIR}/manifest-valid.txt"; then warn "Suppression : $rel_path (absent du dépôt)" rm -f "$local_file" fi done } # --- Main --- main() { require_root install_deps prepare_dirs fetch_manifest if ! validate_manifest > "${TMP_DIR}/manifest-valid.txt"; then err "Le manifeste est invalide ou corrompu." exit 1 fi echo "--------------------------------------------------" info "Phase 1 : Installation et mises à jour" while read -r hash mode rel_path; do [ -n "${hash:-}" ] || continue download_and_install "$hash" "$mode" "$rel_path" done < "${TMP_DIR}/manifest-valid.txt" echo "--------------------------------------------------" info "Phase 2 : Nettoyage" purge_obsolete_files rm -rf "${TMP_DIR}" echo "--------------------------------------------------" ok "Opération terminée avec succès." # Rappel final if [ ! -f "${CONF_DIR}/monitoring.local.conf.php" ]; then warn "Pensez à créer votre fichier ${CONF_DIR}/monitoring.local.conf.php" fi } main "$@"