#!/bin/bash # ============================================================ # Scan ClamAV incrémental avec priorité CPU/I/O faible et lock # Compatible espaces/accents et dashboard (racine /) # Exclusions dynamiques des sous-montages # Usage : cron toutes les heures # Auteur : CB # ============================================================ LOGFILE="/var/log/clamscan-auto.log" MAILTO="admin@example.com" # <-- Y mettre votre email CURRENT_HOUR=$(date '+%H') # Disques et heures de passage declare -A MOUNTS_SCHEDULE=( ["/diskTOTO"]="02" ["/diskTITI"]="03" ["/diskTATA"]="04" ["/"]="05" ) # Rotation simple du log (5 Mo max) [ -f "$LOGFILE" ] && [ "$(stat -c%s "$LOGFILE")" -gt 5000000 ] && mv "$LOGFILE" "${LOGFILE}.1" for MOUNT in "${!MOUNTS_SCHEDULE[@]}"; do if [ "${MOUNTS_SCHEDULE[$MOUNT]}" == "$CURRENT_HOUR" ]; then DATE=$(date '+%F %T') echo "===== Scan ClamAV incrémental sur $MOUNT démarré à $DATE =====" >> "$LOGFILE" if mountpoint -q "$MOUNT"; then # Normalise le nom du lock et timestamp (pour dashboard) if [ "$MOUNT" == "/" ]; then NAME="_" else NAME=$(basename "$MOUNT") fi mkdir -p /var/run/clamscan-locks LOCKFILE="/var/run/clamscan-locks/${NAME}.lock" # Lock par disque pour éviter scans simultanés exec 200>"$LOCKFILE" if ! flock -n 200; then echo "Scan sur $MOUNT déjà en cours. Ignoré." >> "$LOGFILE" continue fi # Fichier de suivi de la dernière exécution TIMESTAMP_FILE="/var/log/clamscan-last-${NAME}.timestamp" LAST_RUN="1970-01-01 00:00:00" [ -f "$TIMESTAMP_FILE" ] && LAST_RUN=$(cat "$TIMESTAMP_FILE") # Liste temporaire des fichiers modifiés FILE_LIST=$(mktemp) trap 'rm -f "$FILE_LIST"' EXIT # Construction dynamique des exclusions si c'est la racine if [ "$MOUNT" == "/" ]; then # Liste des chemins à exclure pour le scan du système EXCLUDES=( "/proc" "/sys" "/dev" "/run" "/var/run" "/tmp" "/var/tmp" "/var/cache" "/var/lib/apt/lists" "/boot" "/usr/share/doc" "/usr/share/man" ) # Construction dynamique du filtre d'exclusion PRUNE_EXPR="" # On exclut d'abord les autres points de montage définis dans MOUNTS_SCHEDULE for DISK in "${!MOUNTS_SCHEDULE[@]}"; do [ "$DISK" != "/" ] && PRUNE_EXPR="$PRUNE_EXPR -path $DISK -o" done # Puis on ajoute les exclusions système for EXCL in "${EXCLUDES[@]}"; do PRUNE_EXPR="$PRUNE_EXPR -o -path $EXCL" done # Supprime le dernier -o éventuel (sinon find râle) PRUNE_EXPR=${PRUNE_EXPR% -o} echo "Exclusions actives : $PRUNE_EXPR" >> "$LOGFILE" # Recherche des fichiers modifiés en ignorant les exclusions find / \( $PRUNE_EXPR \) -prune -o -type f -newermt "$LAST_RUN" -print0 > "$FILE_LIST" else # Pour les autres montages, on scanne tout find "$MOUNT" -type f -newermt "$LAST_RUN" -print0 > "$FILE_LIST" fi if [ -s "$FILE_LIST" ]; then echo "$(date '+%F %T') - Lancement du scan sur fichiers modifiés" >> "$LOGFILE" # Limiter charge CPU/I/O et mémoire (traitement par lots de 100) SCAN_OUTPUT=$(ionice -c3 nice -n19 \ xargs -0 -n100 --no-run-if-empty /usr/bin/clamscan -i < "$FILE_LIST" 2>&1) echo "$SCAN_OUTPUT" >> "$LOGFILE" # Envoi d'alerte si infection détectée if echo "$SCAN_OUTPUT" | grep -q "FOUND"; then { echo "Sujet: [ALERTE] Virus détecté sur $MOUNT" echo echo "$SCAN_OUTPUT" } | /usr/sbin/sendmail "$MAILTO" fi else echo "Aucun fichier modifié depuis le dernier scan." >> "$LOGFILE" fi # Sauvegarde du timestamp et nettoyage date '+%F %T' > "$TIMESTAMP_FILE" rm -f "$FILE_LIST" trap - EXIT else echo "Ignoré : $MOUNT n'est pas monté." >> "$LOGFILE" fi echo "===== Scan terminé à $(date '+%F %T') =====" >> "$LOGFILE" echo "" >> "$LOGFILE" fi done