Комплексный аудит сервера Linux: Bash-скрипт для сисадмина
🛡️ Комплексный аудит сервера за 5 секунд: bash-скрипт для администраторов
Представьте ситуацию: вам поручили провести аудит десятка серверов. Сидеть, подключаться к каждому, ковырять /var/log/syslog, проверять dmesg в поисках OOM Killer'а, анализировать занятость дисков и вычислять, кто и когда заходил по SSH — это долгие часы монотонной работы.
А если нужно еще и составить отчет о проверке, чтобы предъявить его руководству?
Именно для таких задач я написал скрипт комплексного аудита серверов. Он за доли секунды собирает всю ключевую информацию, подсвечивает проблемы и заботливо складывает всё в готовый лог-файл.
Скрипт отлично работает на базе Astra Linux и РЕД ОС (да и на любой Debian/RHEL-подобной системе покажет себя отлично).
🚀 Что умеет скрипт? (Версия 4.0)
Это не просто набор команд, а умный анализатор. При запуске он спрашивает, за сколько месяцев вам нужна статистика, и выдает структурированный отчет:
- Система и Процессор: Имя хоста, аптайм, версия ядра. Скрипт проверяет, работает ли синхронизация времени (NTP) и не было ли пиков загрузки CPU выше 50% (использует утилиту
sar). - Диски и ОЗУ: Показывает свободное место и использование файла подкачки. Если на каком-то разделе занято больше 85% — выведет КРИТИЧЕСКОЕ предупреждение.
- ТОП Потребителей: Моментальный срез: Топ-10 процессов, жрущих процессор, и Топ-10 процессов, съедающих оперативную память.
- Сеть и Firewall: Подсчитывает трафик в гигабайтах с момента последней перезагрузки. Проверяет правила Firewall (поддерживает UFW, Firewalld и голый iptables) и — внимание, крутая фишка! — вытаскивает из ядра ТОП-15 последних заблокированных подключений (DROP/REJECT).
- История авторизаций: Показывает, кто логинился на сервер за выбранный период. Причем скрипт использует умный фильтр
awk: если админ перезаходил 5 раз подряд, в лог попадет только первая запись, чтобы не засорять отчет. - Аудит Безопасности (auditd): Проверяет службу
auditd. Вытаскивает блокировки прав доступа (AVC denied) и неудачные системные вызовы. - Анализ логов: Скрипт сканирует системный журнал на наличие критических ошибок, считает неудачные попытки входа по SSH (защита от брутфорса) и ищет следы OOM Killer (когда ядру не хватает памяти и оно убивает процессы).
- Проверка СУБД Jatoba: Специфичная, но очень полезная функция для серверов с БД. Скрипт ищет логи Jatoba 4 или 5 версии и выводит сегодняшние ошибки (Fatal, Error).
- Зомби и Службы: Считает процессы-зомби и ищет аварийно упавшие systemd-службы.
- Сводное заключение: В самом конце скрипт выводит краткое резюме. Если всё ок — похвалит зеленым цветом. Если есть косяки — выведет нумерованный список проблем, на которые нужно обратить внимание.
💾 Логирование и цвета
Скрипт выводит информацию в консоль с красивой цветовой индикацией (зеленый — ок, желтый — warning, красный — critical). Но самое главное: он параллельно пишет чистый лог-файл без цветовых ANSI-кодов в папку /var/log/server_audit. Вы просто берете этот готовый текстовый файл и прикрепляете к отчету о проделанной работе!
#!/bin/bash
# ==============================================================================
# Скрипт комплексного аудита и проверки состояния сервера (Astra Linux / РЕД ОС)
# Версия 4.0 (Топ-10, Блокировки Firewall, Умный фильтр пользователей)
# ==============================================================================
# --- Цветовые переменные для консоли ---
C_RED='\033[1;31m'
C_GREEN='\033[1;32m'
C_YELLOW='\033[1;33m'
C_CYAN='\033[1;36m'
C_RESET='\033[0m'
# Проверка на права root
if [ "$EUID" -ne 0 ]; then
echo -e "${C_RED}[ ОШИБКА ] Для сбора полной статистики запустите скрипт через sudo.${C_RESET}"
exit 1
fi
# Запрос периода для статистики
read -p "За какое время нужна статистика (в месяцах, например: 1, 3, 6)? " MONTHS
if ! [[ "$MONTHS" =~ ^[0-9]+$ ]]; then
echo -e "${C_RED}[ ОШИБКА ] Введите корректное число месяцев.${C_RESET}"
exit 1
fi
LOG_DIR="/var/log/server_audit"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/audit_report_$(date +%F_%H-%M-%S).log"
TIME_FILTER="-$MONTHS month"
PROBLEMS=()
# Функция логирования (экран с цветом, файл без цвета)
log_print() {
local msg="$1"
local color="$2"
if [ -n "$color" ]; then echo -e "${color}${msg}${C_RESET}"; else echo -e "$msg"; fi
echo -e "$msg" | sed -r 's/\x1B\[[0-9;]*[mK]//g' >> "$LOG_FILE"
}
log_print "============================================================" "$C_CYAN"
log_print " ТЕХНИЧЕСКОЕ ОБСЛУЖИВАНИЕ СЕРВЕРА (ОТЧЕТ)" "$C_CYAN"
log_print " Глубина анализа: $MONTHS мес. | Дата отчета: $(date +'%Y-%m-%d %H:%M:%S')" "$C_CYAN"
log_print "============================================================\n" "$C_CYAN"
# ==============================================================================
# БЛОК 1: СИСТЕМНАЯ ИНФОРМАЦИЯ И ВРЕМЯ
# ==============================================================================
log_print "[1] ОСНОВНАЯ ИНФОРМАЦИЯ, ВРЕМЯ И ПРОЦЕССОР"
HOSTNAME=$(hostname)
OS_VERSION=$(grep PRETTY_NAME /etc/os-release | cut -d '"' -f 2)
KERNEL_VER=$(uname -r)
UPTIME=$(uptime -p | sed 's/up //')
CUR_TIME=$(date '+%Y-%m-%d %H:%M:%S')
# Проверка синхронизации времени
NTP_SYNC=$(timedatectl | grep -iE 'synchronized|NTP service' | xargs | tr '\n' ' | ')
log_print " * Имя сервера: $HOSTNAME"
log_print " * ОС: $OS_VERSION"
log_print " * Ядро: $KERNEL_VER"
log_print " * Аптайм: $UPTIME"
log_print " * Текущее время сервера: $CUR_TIME"
log_print " * Синхронизация времени: $NTP_SYNC"
if echo "$NTP_SYNC" | grep -iq "no"; then
PROBLEMS+=("Не работает синхронизация времени (NTP).")
log_print " [ ПРЕДУПРЕЖДЕНИЕ ] Время не синхронизируется!" "$C_YELLOW"
fi
log_print "\n--- ИСТОРИЯ ЗАГРУЗКИ ПРОЦЕССОРА ---"
if command -v sar &> /dev/null; then
HIGH_LOAD=$(sar -u | awk 'NR>3 && $NF ~ /^[0-9]/ && $NF < 50.00 {print $0}')
if [ -n "$HIGH_LOAD" ]; then
log_print " [ ПРЕДУПРЕЖДЕНИЕ ] Зафиксированы пики загрузки процессора более 50%:" "$C_YELLOW"
echo "$HIGH_LOAD" | while read line; do log_print " $line" "$C_YELLOW"; done
PROBLEMS+=("Зафиксированы пики высокой загрузки процессора (более 50%).")
else
log_print " [ ОК ] Сегодня пиков загрузки процессора выше 50% не зафиксировано." "$C_GREEN"
fi
else
log_print " [ ИНФО ] Утилита 'sar' не установлена. Для истории нагрузки нужен sysstat." "$C_YELLOW"
LOAD_AVG=$(uptime | awk -F'load average:' '{ print $2 }')
log_print " Текущая средняя загрузка: $LOAD_AVG"
fi
log_print ""
# ==============================================================================
# БЛОК 2: РЕСУРСЫ И ДИСКИ
# ==============================================================================
log_print "[2] ОЗУ И ДИСКОВАЯ ПОДСИСТЕМА"
RAM_TOTAL=$(free -h | awk '/^Mem:/{print $2}')
RAM_FREE=$(free -h | awk '/^Mem:/{print $7}')
SWAP_USE=$(free -m | awk '/^Swap:/{print $3}')
log_print " * ОЗУ (RAM): Всего $RAM_TOTAL | Доступно $RAM_FREE"
if [ "$SWAP_USE" -gt 100 ]; then
log_print " * Файл подкачки (Swap): Используется ${SWAP_USE} MB" "$C_YELLOW"
else
log_print " * Файл подкачки (Swap): В норме (Используется ${SWAP_USE} MB)"
fi
log_print "\n--- СОСТОЯНИЕ ВСЕХ ДИСКОВ ---"
df -hT | grep -E '^/dev/|Filesystem' | while read line; do log_print " $line"; done
df -h | grep '^/dev/' | while read line; do
USE_PCT=$(echo "$line" | awk '{print $5}' | sed 's/%//')
MNT_PT=$(echo "$line" | awk '{print $6}')
if [ "$USE_PCT" -gt 85 ]; then
log_print " [ КРИТИЧНО ] Заканчивается место на разделе $MNT_PT ($USE_PCT% занято)!" "$C_RED"
PROBLEMS+=("КРИТИЧНО: Заканчивается место на разделе $MNT_PT ($USE_PCT% занято).")
fi
done
log_print ""
# ==============================================================================
# БЛОК 3: ТОП ПРОЦЕССОВ (10 САМЫХ АКТИВНЫХ)
# ==============================================================================
log_print "[3] ТОП ПОТРЕБИТЕЛЕЙ РЕСУРСОВ (Текущее состояние)"
log_print "--- ТОП-10 процессов по загрузке CPU ---"
ps -eo pid,user,%cpu,%mem,cmd --sort=-%cpu | head -n 11 | while read line; do log_print " $line"; done
log_print "\n--- ТОП-10 процессов по использованию ОЗУ ---"
ps -eo pid,user,%mem,%cpu,cmd --sort=-%mem | head -n 11 | while read line; do log_print " $line"; done
log_print ""
# ==============================================================================
# БЛОК 4: СЕТЬ И ФАЙРВОЛ
# ==============================================================================
log_print "[4] СЕТЕВЫЕ ИНТЕРФЕЙСЫ, ТРАФИК И ФАЙРВОЛ"
IP_INFO=$(ip -br a)
log_print "Адреса интерфейсов:\n$IP_INFO\n"
log_print "Трафик (с момента последней перезагрузки, в Гигабайтах):"
for iface in $(ls /sys/class/net/ | grep -v lo); do
RX_BYTES=$(cat /sys/class/net/"$iface"/statistics/rx_bytes 2>/dev/null || echo 0)
TX_BYTES=$(cat /sys/class/net/"$iface"/statistics/tx_bytes 2>/dev/null || echo 0)
RX_GB=$(awk "BEGIN {printf \"%.2f\", $RX_BYTES/1073741824}")
TX_GB=$(awk "BEGIN {printf \"%.2f\", $TX_BYTES/1073741824}")
log_print " * $iface -> Получено: ${RX_GB} GB | Передано: ${TX_GB} GB"
done
log_print "\n--- ПРАВИЛА ФАЙРВОЛА ---"
FW_ACTIVE=1
if command -v ufw >/dev/null && ufw status | grep -q "active"; then
log_print "Используется UFW. Текущие правила:"
ufw status numbered | head -n 20 | while read line; do log_print " $line"; done
elif command -v firewall-cmd >/dev/null && firewall-cmd --state &>/dev/null; then
log_print "Используется Firewalld. Текущие правила:"
firewall-cmd --list-all | while read line; do log_print " $line"; done
else
log_print "Используется iptables (показаны активные цепочки):"
iptables -L -n -v | head -n 20 | while read line; do log_print " $line"; done
fi
log_print "\n--- ПОСЛЕДНИЕ ЗАБЛОКИРОВАННЫЕ ПОДКЛЮЧЕНИЯ (ТОП-15) ---"
# Выборка из лога ядра: ищем сброшенные пакеты (DROP/REJECT/DENY/BLOCK) с указанием интерфейса или источника
FW_BLOCKED=$(journalctl -k --no-pager | grep -iE "BLOCK|DROP|REJECT|DENY" | grep -iE "IN=|SRC=" | tail -n 15)
if [ -n "$FW_BLOCKED" ]; then
echo "$FW_BLOCKED" | while read line; do log_print " $line" "$C_YELLOW"; done
else
log_print " [ ИНФО ] Заблокированных подключений в журнале ядра не найдено."
fi
log_print ""
# ==============================================================================
# БЛОК 5: ПОЛЬЗОВАТЕЛИ
# ==============================================================================
log_print "[5] ИСТОРИЯ АВТОРИЗАЦИИ"
log_print "Пользователи, заходившие за $MONTHS мес. (без подряд идущих повторов):"
# Фильтр awk проверяет первое поле (имя пользователя). Если оно совпадает с предыдущим, строка пропускается.
last --since "-$MONTHS months" 2>/dev/null | grep -vE "wtmp|reboot|begins|^$" | awk '$1 != prev {print " " $0; prev=$1}' | head -n 15 | while read line; do log_print "$line"; done
log_print ""
# ==============================================================================
# БЛОК 6: АУДИТ БЕЗОПАСНОСТИ (AUDITD)
# ==============================================================================
log_print "[6] ПРОВЕРКА ПОДСИСТЕМЫ АУДИТА"
if systemctl is-active --quiet auditd; then
log_print " [ ОК ] Служба auditd работает." "$C_GREEN"
AUDIT_LOG="/var/log/audit/audit.log"
if [ -f "$AUDIT_LOG" ]; then
AVC_DENIALS=$(grep -i "avc: denied" "$AUDIT_LOG" | tail -n 5)
if [ -n "$AVC_DENIALS" ]; then
log_print " [ ПРЕДУПРЕЖДЕНИЕ ] Последние блокировки прав доступа (AVC Denied):" "$C_YELLOW"
echo "$AVC_DENIALS" | while read line; do log_print " $line" "$C_YELLOW"; done
PROBLEMS+=("В auditd зафиксированы блокировки прав доступа (AVC denied).")
fi
FAIL_EVENTS=$(grep -i "res=failed" "$AUDIT_LOG" | grep -v "cron" | tail -n 5)
if [ -n "$FAIL_EVENTS" ]; then
log_print " [ ПРЕДУПРЕЖДЕНИЕ ] Последние неудачные системные вызовы (res=failed):" "$C_YELLOW"
echo "$FAIL_EVENTS" | while read line; do log_print " $line" "$C_YELLOW"; done
PROBLEMS+=("В auditd зафиксированы неудачные системные события (res=failed).")
fi
if [ -z "$AVC_DENIALS" ] && [ -z "$FAIL_EVENTS" ]; then
log_print " [ ОК ] Критических событий и блокировок в аудите за последнее время не найдено." "$C_GREEN"
fi
fi
else
log_print " [ ПРЕДУПРЕЖДЕНИЕ ] Служба auditd НЕ работает или не установлена!" "$C_YELLOW"
PROBLEMS+=("Служба auditd не активна.")
fi
log_print ""
# ==============================================================================
# БЛОК 7: АНАЛИЗ СИСТЕМНЫХ ЛОГОВ (ОШИБКИ, OOM, SSH)
# ==============================================================================
log_print "[7] АНАЛИЗ СИСТЕМНЫХ ЖУРНАЛОВ (за $MONTHS мес.)"
CRIT_ERRORS=$(journalctl --since "$TIME_FILTER" -p 0..3 --no-pager | grep -v "journalctl")
CRIT_ERR_COUNT=$(echo "$CRIT_ERRORS" | sed '/^\s*$/d' | wc -l)
if [ "$CRIT_ERR_COUNT" -gt 0 ]; then
log_print " [ КРИТИЧНО ] Найдено системных ошибок: $CRIT_ERR_COUNT" "$C_RED"
log_print " --- Последние 10 системных ошибок ---" "$C_RED"
echo "$CRIT_ERRORS" | tail -n 10 | while read line; do log_print " $line" "$C_RED"; done
PROBLEMS+=("В системном журнале зафиксировано $CRIT_ERR_COUNT ошибок. Проверьте раздел 7 отчета.")
else
log_print " [ ОК ] Критических системных ошибок не найдено." "$C_GREEN"
fi
OOM_COUNT=$(journalctl --since "$TIME_FILTER" -k | grep -ic -E "killed process|oom-killer" || true)
if [ "$OOM_COUNT" -gt 0 ]; then
log_print " [ КРИТИЧНО ] Срабатываний OOM Killer (нехватка ОЗУ): $OOM_COUNT" "$C_RED"
PROBLEMS+=("Нехватка ОЗУ: ядро принудительно завершало процессы ($OOM_COUNT раз).")
fi
AUTH_FAILS=$(journalctl --since "$TIME_FILTER" -u ssh --no-pager | grep -ic "Failed password" || true)
if [ "$AUTH_FAILS" -gt 30 ]; then
log_print " [ ПРЕДУПРЕЖДЕНИЕ ] Неудачных попыток входа по SSH: $AUTH_FAILS" "$C_YELLOW"
PROBLEMS+=("Подозрение на брутфорс SSH: $AUTH_FAILS неудачных попыток входа.")
fi
log_print ""
# ==============================================================================
# БЛОК 8: ПРОВЕРКА СУБД JATOBA
# ==============================================================================
log_print "[8] ПРОВЕРКА СУБД JATOBA"
JATOBA_FOUND=0
DAY_EN=$(LC_ALL=C date +%a)
for J_VER in 4 5; do
J_DIR="/var/lib/jatoba/$J_VER/data/log"
if [ -d "$J_DIR" ]; then
JATOBA_FOUND=1
log_print " [ ИНФО ] Найдена директория логов Jatoba версии $J_VER."
J_LOG="$J_DIR/jatoba-$DAY_EN.log"
if [ -f "$J_LOG" ]; then
ERR_COUNT=$(grep -iE 'error|warning|fatal' "$J_LOG" | wc -l)
if [ "$ERR_COUNT" -gt 0 ]; then
log_print " [ КРИТИЧНО ] В логе Jatoba за сегодня найдено $ERR_COUNT ошибок/предупреждений:" "$C_RED"
grep -iE 'error|warning|fatal' "$J_LOG" | tail -n 5 | while read line; do log_print " -> $line" "$C_RED"; done
PROBLEMS+=("В логах Jatoba ($J_LOG) зафиксированы ошибки/предупреждения.")
else
log_print " [ ОК ] Ошибок и предупреждений в Jatoba за сегодня не обнаружено." "$C_GREEN"
fi
else
log_print " [ ПРЕДУПРЕЖДЕНИЕ ] Лог-файл за сегодня ($J_LOG) еще не создан." "$C_YELLOW"
fi
fi
done
if [ "$JATOBA_FOUND" -eq 0 ]; then log_print " [ ИНФО ] Служба Jatoba не найдена."; fi
log_print ""
# ==============================================================================
# БЛОК 9: СОСТОЯНИЕ СЛУЖБ И ЗОМБИ-ПРОЦЕССОВ
# ==============================================================================
log_print "[9] ЗОМБИ И УПАВШИЕ СЛУЖБЫ"
ZOMBIES=$(ps aux | awk '{if ($8=="Z") print $0}' | wc -l)
ZOMBIES=$((ZOMBIES - 1))
if [ "$ZOMBIES" -gt 0 ]; then
log_print " [ ПРЕДУПРЕЖДЕНИЕ ] Найдено процессов-зомби: $ZOMBIES" "$C_YELLOW"
PROBLEMS+=("В системе висят процессы-зомби ($ZOMBIES шт.).")
else
log_print " [ ОК ] Процессов-зомби нет." "$C_GREEN"
fi
FAILED_SVCS=$(systemctl list-units --state=failed --no-legend)
if [ -n "$FAILED_SVCS" ]; then
log_print " [ КРИТИЧНО ] Найдены упавшие системные службы:" "$C_RED"
echo "$FAILED_SVCS" | awk '{print " "$1" "$2}' | while read line; do log_print "$line" "$C_RED"; done
PROBLEMS+=("Обнаружены аварийно завершенные службы (systemctl --failed).")
else
log_print " [ ОК ] Упавших служб systemd не обнаружено." "$C_GREEN"
fi
log_print ""
# ==============================================================================
# БЛОК 10: ЗАКЛЮЧЕНИЕ
# ==============================================================================
log_print "============================================================" "$C_CYAN"
log_print " ЗАКЛЮЧЕНИЕ" "$C_CYAN"
log_print "============================================================" "$C_CYAN"
if [ ${#PROBLEMS[@]} -eq 0 ]; then
log_print " [ ОТЛИЧНО ] С сервером всё хорошо! Критических проблем не обнаружено." "$C_GREEN"
else
log_print " [ ВНИМАНИЕ ] Обнаружены следующие проблемы:" "$C_RED"
for idx in "${!PROBLEMS[@]}"; do
log_print " $((idx+1)). ${PROBLEMS[$idx]}" "$C_RED"
done
fi
log_print "\n============================================================" "$C_CYAN"
log_print " Полный отчет сохранен: $LOG_FILE"
log_print "============================================================" "$C_CYAN"
Как запустить:
- Сохраните код в файл, например:
audit.sh - Дайте права на выполнение:
chmod +x audit.sh - Запустите от имени суперпользователя:
sudo ./audit.sh
Удачного администрирования и пусть ваши сервера работают без сбоев! 😎
- misterleks от